Explorar o código

type.js -> Types.ob

Vladislav Folts %!s(int64=11) %!d(string=hai) anos
pai
achega
d1e15ab197
Modificáronse 23 ficheiros con 1248 adicións e 562 borrados
  1. 18 14
      src/cast.js
  2. 2 2
      src/code.js
  3. 75 70
      src/context.js
  4. 16 14
      src/eberon/eberon_context.js
  5. 19 0
      src/js/Context.js
  6. 66 0
      src/js/JsArray.js
  7. 40 0
      src/js/JsMap.js
  8. 14 0
      src/js/JsString.js
  9. 6 0
      src/js/Object.js
  10. 594 0
      src/js/Types.js
  11. 10 7
      src/module.js
  12. 8 4
      src/nodejs.js
  13. 1 1
      src/ob/Context.ob
  14. 44 13
      src/ob/JsArray.ob
  15. 215 99
      src/ob/Types.ob
  16. 2 2
      src/oberon/oberon_context.js
  17. 30 5
      src/oc_nodejs.js
  18. 16 14
      src/operator.js
  19. 51 47
      src/procedure.js
  20. 10 7
      src/scope.js
  21. 1 1
      src/symbol.js
  22. 0 255
      src/type.js
  23. 10 7
      test/test_unit.js

+ 18 - 14
src/cast.js

@@ -1,20 +1,22 @@
 "use strict";
 var Code = require("code.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 var ArrayType = Type.Array;
 var PointerType = Type.Pointer;
 var ProcedureType = Type.Procedure;
 
+var basicTypes = Type.basic();
+
 function doNoting(context, e){return e;}
 
 function findBaseType(base, type){
     while (type && type != base)
-        type = type.baseType();
+        type = Type.recordBase(type);
     return type;
 }
 
 function findPointerBaseType(pBase, pType){
-    if (!findBaseType(pBase.baseType(), pType.baseType()))
+    if (!findBaseType(Type.pointerBase(pBase), Type.pointerBase(pType)))
         return undefined;
     return pBase;
 }
@@ -27,9 +29,10 @@ function areTypesExactlyMatch(t1, t2){
     if (t1 == t2)
         return true;
     if (t1 instanceof ArrayType && t2 instanceof ArrayType)
-        return t1.length() === t2.length() && areTypesMatch(t1.elementsType(), t2.elementsType());
+        return Type.arrayLength(t1) == Type.arrayLength(t2) 
+            && areTypesMatch(Type.arrayElementsType(t1), Type.arrayElementsType(t2));
     if (t1 instanceof PointerType && t2 instanceof PointerType)
-        return areTypesMatch(t1.baseType(), t2.baseType());
+        return areTypesMatch(Type.pointerBase(t1), Type.pointerBase(t2));
     if (t1 instanceof ProcedureType && t2 instanceof ProcedureType)
         return areProceduresMatch(t1, t2);
     return false;
@@ -70,26 +73,27 @@ function implicitCast(from, to, ops){
     if (from == to)
         return doNoting;
 
-    if (from == Type.basic.uint8 && to == Type.basic.integer)
+    if (from == basicTypes.uint8 && to == basicTypes.integer)
         return doNoting;
 
-    if (from == Type.basic.integer && to == Type.basic.uint8)
+    if (from == basicTypes.integer && to == basicTypes.uint8)
         return function(context, e){
-            return ops.setIntersection(e, new Code.Expression("0xFF", Type.basic.integer, undefined, 0xFF));
+            return ops.setIntersection(e, new Code.Expression("0xFF", basicTypes.integer, undefined, 0xFF));
         };
 
     if (from instanceof Type.String){
-        if (to === Type.basic.ch){
-            var v = from.asChar();
-            if (v !== undefined)
+        if (to === basicTypes.ch){
+            var v;
+            if (Type.stringAsChar(from, {set: function(value){v = value;}}))
                 return function(){return new Code.Expression(v, to);};
         }
-        else if (to instanceof Type.Array && to.elementsType() == Type.basic.ch)
+        else if (to instanceof Type.Array && Type.arrayElementsType(to) == basicTypes.ch)
             return doNoting;
     }
     else if (from instanceof ArrayType && to instanceof ArrayType)
-        return (to.length() === undefined || to.length() === from.length())
-            && implicitCast(from.elementsType(), to.elementsType());
+        return (Type.arrayLength(to) == Type.openArrayLength || Type.arrayLength(to) == Type.arrayLength(from))
+            ? implicitCast(Type.arrayElementsType(from), Type.arrayElementsType(to))
+            : undefined;
     else if (from instanceof PointerType && to instanceof PointerType){
         if (findPointerBaseType(to, from))
             return doNoting;

+ 2 - 2
src/code.js

@@ -1,7 +1,7 @@
 "use strict";
 
 var Class = require("rtl.js").Class;
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
 var NullCodeGenerator = Class.extend({
 	init: function NullCodeGenerator(){},
@@ -90,7 +90,7 @@ function genExport(symbol){
     if (symbol.isType()){
         var type = symbol.info().type();
         if (!(type instanceof Type.Record 
-              || (type instanceof Type.Pointer && !type.baseType().name())))
+              || (type instanceof Type.Pointer && !Type.typeName(Type.pointerBase(type)))))
             return undefined;
     }
     return symbol.id();

+ 75 - 70
src/context.js

@@ -10,16 +10,16 @@ var Procedure = require("procedure.js");
 var Class = require("rtl.js").Class;
 var Scope = require("scope.js");
 var Symbol = require("symbol.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
-var basicTypes = Type.basic;
+var basicTypes = Type.basic();
 
 function getSymbolAndScope(context, id){
     var s = context.findSymbol(id);
     if (!s)
         throw new Errors.Error(
             context instanceof Type.Module
-                ? "identifier '" + id + "' is not exported by module '" + context.name() + "'"
+                ? "identifier '" + id + "' is not exported by module '" + Type.moduleName(context) + "'"
                 : "undeclared identifier: '" + id + "'");
     return s;
 }
@@ -61,9 +61,9 @@ function checkImplicitCast(from, to){
 
 function promoteTypeInExpression(e, type){
     var fromType = e.type();
-    if (type == Type.basic.ch && fromType instanceof Type.String){
-        var v = fromType.asChar();
-        if (v !== undefined)
+    if (type == basicTypes.ch && fromType instanceof Type.String){
+        var v;
+        if (Type.stringAsChar(fromType, {set: function(value){v = value;}}))
             return new Code.Expression(v, type);
     }
     return e;
@@ -83,13 +83,13 @@ function promoteExpressionType(context, left, right){
 
 function checkTypeCast(from, to, msg){
     if (from instanceof Type.Pointer)
-        from = from.baseType();
+        from = Type.pointerBase(from);
     if (to instanceof Type.Pointer)
-        to = to.baseType();
+        to = Type.pointerBase(to);
 
-    var t = to.baseType();
+    var t = Type.recordBase(to);
     while (t && t != from)
-        t = t.baseType();
+        t = Type.recordBase(t);
     if (!t)
         throw new Errors.Error(msg + ": '" + to.description()
                              + "' is not an extension of '" + from.description() + "'");
@@ -183,7 +183,7 @@ exports.String = ChainedContext.extend({
     toStr: function(s){return s;},
     endParse: function(){
         var s = this.toStr(this.__result);
-        this.parent().handleConst(new Type.String(s), s, escapeString(s));
+        this.parent().handleConst(Type.makeString(s), s, escapeString(s));
         }
 });
 
@@ -289,8 +289,10 @@ exports.Designator = ChainedContext.extend({
         var info = s.info();
         if (info instanceof Type.Type || s.isType() || s.isProcedure())
             this.__currentType = info;
-        else if (s.isVariable() || s.isConst())
-            this.__currentType = info.type();
+        else if (s.isConst())
+            this.__currentType = Type.constType(info);
+        else if (s.isVariable())
+            this.__currentType = Type.variableType(info);
         this.__info = info;
         this.__code += code;
     },
@@ -298,7 +300,7 @@ exports.Designator = ChainedContext.extend({
         var t = this.__currentType;
         var pointerType;
         var isReadOnly = this.__info instanceof Type.Variable 
-                      && this.__info.isReadOnly();
+                      && Type.isVariableReadOnly(this.__info);
         if (t instanceof Type.Pointer){
             pointerType = t;
             this.__handleDeref();
@@ -310,7 +312,7 @@ exports.Designator = ChainedContext.extend({
             throw new Errors.Error("cannot designate '" + t.description() + "'");
 
         this.__denote(id, pointerType);
-        this.__info = new Type.Variable(this.__currentType, isReadOnly);
+        this.__info = Type.makeVariable(this.__currentType, isReadOnly);
         this.__scope = undefined;
     },
     handleExpression: function(e){this.__indexExpression = e;},
@@ -325,13 +327,14 @@ exports.Designator = ChainedContext.extend({
         if (!(type instanceof Type.Array))
             throw new Errors.Error("ARRAY expected, got '" + type.description() + "'");
         var value = e.constValue();
-        if (value !== undefined && value >= type.length())
+        var arrayLen = Type.arrayLength(type);
+        if (value !== undefined && arrayLen != Type.openArrayLength && value >= arrayLen)
             throw new Errors.Error("index out of bounds: maximum possible index is "
-                                 + (type.length() - 1)
+                                 + (Type.arrayLength(type) - 1)
                                  + ", got " + value );
 
-        this.__currentType = type.elementsType();
-        this.__info = new Type.Variable(this.__currentType, this.__info.isReadOnly());
+        this.__currentType = Type.arrayElementsType(type);
+        this.__info = Type.makeVariable(this.__currentType, Type.isVariableReadOnly(this.__info));
     },
     handleLiteral: function(s){
         if (s == "]" || s == ","){
@@ -351,14 +354,14 @@ exports.Designator = ChainedContext.extend({
         }
         else if (s == "^"){
             this.__handleDeref();
-            this.__info = new Type.VariableRef(this.__currentType);
+            this.__info = Type.makeVariableRef(this.__currentType, false);
         }
     },
     __handleDeref: function(){
         if (!(this.__currentType instanceof Type.Pointer))
             throw new Errors.Error("POINTER TO type expected, got '"
                                  + this.__currentType.description() + "'");
-        this.__currentType = this.__currentType.baseType();
+        this.__currentType = Type.pointerBase(this.__currentType);
         if (this.__currentType instanceof Type.NonExportedRecord)
             throw new Errors.Error("POINTER TO non-exported RECORD type cannot be dereferenced");
     },
@@ -366,7 +369,7 @@ exports.Designator = ChainedContext.extend({
         if (this.__currentType instanceof Type.Record){
             if (!(this.__info instanceof Type.VariableRef))
                 throw new Errors.Error(
-                    "invalid type cast: a value variable and cannot be used in typeguard");
+                    "invalid type cast: a value variable cannot be used in typeguard");
             if (!(type instanceof Type.Record))
                 throw new Errors.Error(
                     "invalid type cast: RECORD type expected as an argument of RECORD type guard, got '"
@@ -380,8 +383,8 @@ exports.Designator = ChainedContext.extend({
 
         checkTypeCast(this.__currentType, type, "invalid type cast");
 
-        var baseType = type instanceof Type.Pointer ? type.baseType() : type;
-        var castName = this.qualifyScope(baseType.scope()) + baseType.cons();
+        var baseType = type instanceof Type.Pointer ? Type.pointerBase(type) : type;
+        var castName = this.qualifyScope(Type.recordScope(baseType)) + Type.recordConstructor(baseType);
         var code = this.rtl().typeGuard(this.__code, castName);
         this.__code = code;
 
@@ -391,8 +394,8 @@ exports.Designator = ChainedContext.extend({
         var t = this.__currentType;
         var fieldType = t.findSymbol(id);
         if (!fieldType){
-            var typeDesc = !t.name() && pointerType && pointerType.name()
-                ? pointerType.name()
+            var typeDesc = !Type.typeName(t) && pointerType && Type.typeName(pointerType)
+                ? Type.typeName(pointerType)
                 : t.description();
             throw new Errors.Error("type '" + typeDesc + "' has no '" + id + "' field");
         }
@@ -444,9 +447,10 @@ exports.FormalType = HandleSymbolAsType.extend({
     },
     setType: function(type){           
         for(var i = 0; i < this.__arrayDimension; ++i)
-            type = new Type.Array("ARRAY OF " + type.name()
-                               , undefined
-                               , type);
+            type = Type.makeArray("ARRAY OF " + Type.typeName(type),
+                                  undefined,
+                                  type,
+                                  0);
         this.parent().setType(type);
 
     },
@@ -551,8 +555,8 @@ exports.ProcDecl = ChainedContext.extend({
         if (name == this.__id.id())
             throw new Errors.Error("argument '" + name + "' has the same name as procedure");
         var readOnly = !arg.isVar && (arg.type instanceof Type.Array);
-        var v = arg.isVar ? new Type.VariableRef(arg.type)
-                          : new Type.Variable(arg.type, readOnly);
+        var v = arg.isVar ? Type.makeVariableRef(arg.type)
+                          : Type.makeVariable(arg.type, readOnly);
         var s = new Symbol.Symbol(name, v);
         this.currentScope().addSymbol(s);
 
@@ -598,7 +602,7 @@ exports.ProcDecl = ChainedContext.extend({
         var result = this.__type.result();
         if (result && !this.__returnParsed)
             throw new Errors.Error("RETURN expected at the end of PROCEDURE declared with '"
-                                 + result.name() + "' result type");
+                                 + Type.typeName(result) + "' result type");
     }
 });
 
@@ -659,11 +663,11 @@ exports.PointerDecl = ChainedContext.extend({
         var name = parent.isAnonymousDeclaration() 
             ? undefined
             : parent.genTypeName();
-        var pointerType = new Type.Pointer(name, typeId);
+        var pointerType = Type.makePointer(name, typeId);
         parent.setType(pointerType);
     },
     setType: function(type){
-        var typeId = new Type.TypeId(type);
+        var typeId = Type.makeTypeId(type);
         this.currentScope().addFinalizer(function(){typeId.strip();});
         this.__setTypeId(typeId);
     },
@@ -678,7 +682,7 @@ exports.PointerDecl = ChainedContext.extend({
         var resolve = function(){return getSymbol(parent, id).info().type();};
 
         return new Symbol.Found(
-            new Symbol.Symbol(id, new Type.ForwardTypeId(resolve)),
+            new Symbol.Symbol(id, Type.makeForwardTypeId(resolve)),
             scope
             );
     },
@@ -707,10 +711,10 @@ exports.ArrayDecl = HandleSymbolAsType.extend({
                 ? isCharArray ? this.rtl().makeCharArray(dimensions)
                               : this.rtl().makeArray(dimensions + ", " + initializer)
                 : undefined;
-            type = new Type.Array("ARRAY OF " + type.name()
-                               , arrayInit
-                               , type
-                               , length);
+            type = Type.makeArray("ARRAY OF " + Type.typeName(type),
+                                  arrayInit,
+                                  type,
+                                  length);
         }
 
         this.__type = type;
@@ -743,7 +747,7 @@ exports.ArrayDimensions = ChainedContext.extend({
 
 var numericOpTypeCheck = {
     expect: "numeric type",
-    check: function(t){return Type.numeric.indexOf(t) != -1;}
+    check: function(t){return Type.numeric().indexOf(t) != -1;}
 };
 
 var intOpTypeCheck = {
@@ -931,7 +935,7 @@ exports.Term = ChainedContext.extend({
         var value;
         var info = d.info();
         if (info instanceof Type.Const)
-            value = info.value();
+            value = Type.constValue(info);
         this.handleExpression(
             new Code.Expression(d.code(), d.type(), d, value));
     },
@@ -1121,7 +1125,7 @@ exports.Expression = ChainedContext.extend({
         if (this.__relation == "IN"){
             if (!Type.isInt(leftType))
                 throw new Errors.Error(
-                    Type.intsDescription() + " expected as an element of SET, got '" + leftType.name() + "'");
+                    Type.intsDescription() + " expected as an element of SET, got '" + Type.typeName(leftType) + "'");
             checkImplicitCast(rightType, basicTypes.set);
 
             code = "1 << " + leftCode + " & " + rightCode;
@@ -1234,8 +1238,8 @@ exports.Case = ChainedContext.extend({
         var type = e.type();
         var gen = this.codeGenerator();
         if (type instanceof Type.String){
-            var v = type.asChar();
-            if (v !== undefined){
+            var v;
+            if (Type.stringAsChar(type, {set: function(value){v = value;}})){
                 gen.write(v);
                 type = basicTypes.ch;
             }
@@ -1255,8 +1259,8 @@ exports.Case = ChainedContext.extend({
     handleLabelType: function(type){
         if (!Cast.areTypesMatch(type, this.__type))
             throw new Errors.Error(
-                "label must be '" + this.__type.name() + "' (the same as case expression), got '"
-                + type.name() + "'");
+                "label must be '" + Type.typeName(this.__type) + "' (the same as case expression), got '"
+                + Type.typeName(type) + "'");
     }
 });
 
@@ -1313,8 +1317,7 @@ exports.CaseRange = ChainedContext.extend({
     },
     handleConst: function(type, value){
         if (type instanceof Type.String){
-            value = type.asChar();
-            if (value === undefined)
+            if (!Type.stringAsChar(type, {set: function(v){value = v;}}))
                 throw new Errors.Error("single-character string expected");
             type = basicTypes.ch;
         }
@@ -1325,11 +1328,11 @@ exports.CaseRange = ChainedContext.extend({
         if (!s.isConst())
             throw new Errors.Error("'" + id + "' is not a constant");
         
-        var type = s.info().type();
+        var type = Type.constType(s.info());
         if (type instanceof Type.String)
             this.handleConst(type, undefined);
         else
-            this.handleLabel(type, s.info().value());
+            this.handleLabel(type, Type.constValue(s.info()));
     },
     endParse: function(){this.parent().handleRange(this.__from, this.__to);}
 });
@@ -1389,7 +1392,7 @@ exports.For = ChainedContext.extend({
         var s = getSymbol(this.parent(), id);
         if (!s.isVariable())
             throw new Errors.Error("'" + s.id() + "' is not a variable");
-        var type = s.info().type();
+        var type = Type.variableType(s.info());
         if (type !== basicTypes.integer)
             throw new Errors.Error(
                   "'" + s.id() + "' is a '" 
@@ -1488,7 +1491,7 @@ exports.ConstDecl = ChainedContext.extend({
         this.__value = value;
     },
     endParse: function(){
-        var c = new Type.Const(this.__type, this.__value);
+        var c = Type.makeConst(this.__type, this.__value);
         this.currentScope().addSymbol(new Symbol.Symbol(this.__id.id(), c), this.__id.exported());
         this.codeGenerator().write(";\n");
     }
@@ -1518,7 +1521,7 @@ exports.VariableDeclaration = HandleSymbolAsType.extend({
     typeName: function(){return undefined;},
     isAnonymousDeclaration: function(){return true;},
     endParse: function(){
-        var v = new Type.Variable(this.__type);
+        var v = Type.makeVariable(this.__type, false);
         var idents = this.__idents;
         var gen = this.codeGenerator();
         for(var i = 0; i < idents.length; ++i){
@@ -1529,7 +1532,7 @@ exports.VariableDeclaration = HandleSymbolAsType.extend({
                 throw new Errors.Error("only scalar type variables can be exported");
             var varName = id.id();
             this.currentScope().addSymbol(new Symbol.Symbol(varName, v), id.exported());
-            var t = v.type();
+            var t = Type.variableType(v);
             gen.write("var " + varName + " = " + t.initializer(this) + ";");
         }
 
@@ -1644,26 +1647,26 @@ function isTypeRecursive(type, base){
     if (type == base)
         return true;
     if (type instanceof Type.Record){
-        if (isTypeRecursive(type.baseType(), base))
+        if (isTypeRecursive(Type.recordBase(type), base))
             return true;
-        var fields = type.ownFields();
+        var fields = Type.recordOwnFields(type);
         for(var fieldName in fields){
             if (isTypeRecursive(fields[fieldName], base))
                 return true;
         }
     }
     else if (type instanceof Type.Array)
-        return isTypeRecursive(type.elementsType(), base);
+        return isTypeRecursive(Type.arrayElementsType(type), base);
     return false;
 }
 
 exports.RecordDecl = ChainedContext.extend({
-    init: function RecordDeclContext(context, RecordType){
+    init: function RecordDeclContext(context, makeRecord){
         ChainedContext.prototype.init.call(this, context);
         var parent = this.parent();
         var cons = parent.genTypeName();
         var name = parent.isAnonymousDeclaration() ? undefined : cons;
-        this.__type = new RecordType(name, cons, context.currentScope());
+        this.__type = makeRecord(name, cons, context.currentScope());
         parent.setType(this.__type);
         parent.codeGenerator().write("var " + cons + " = ");
     },
@@ -1684,23 +1687,23 @@ exports.RecordDecl = ChainedContext.extend({
                 + "'");
         if (isTypeRecursive(type, this.__type))
             throw new Errors.Error("recursive inheritance: '"
-                + this.__type.name() + "'");
-        this.__type.setBaseType(type);
+                + Type.typeName(this.__type) + "'");
+        Type.setRecordBase(this.__type, type);
     },
     endParse: function(){
         var type = this.__type;
-        var baseType = type.baseType();
+        var baseType = Type.recordBase(type);
         var gen = this.codeGenerator();
-        var qualifiedBase = baseType ? this.qualifyScope(baseType.scope()) + baseType.name() : undefined; 
+        var qualifiedBase = baseType ? this.qualifyScope(Type.recordScope(baseType)) + Type.typeName(baseType) : undefined; 
         gen.write((baseType ? qualifiedBase + ".extend" 
                             : this.rtl().extendId())
                 + "(");
         gen.openScope();
-        gen.write("init: function " + this.__type.cons() + "()");
+        gen.write("init: function " + Type.recordConstructor(this.__type) + "()");
         gen.openScope();
         if (baseType)
             gen.write(qualifiedBase + ".prototype.init.call(this);\n");
-        var ownFields = type.ownFields();
+        var ownFields = Type.recordOwnFields(type);
         for(var f in ownFields)
             gen.write("this." + f + " = " + ownFields[f].initializer(this) + ";\n");
 
@@ -1716,7 +1719,7 @@ exports.TypeDeclaration = ChainedContext.extend({
         this.__symbol = undefined;
     },
     handleIdentdef: function(id){
-        var typeId = new Type.LazyTypeId();
+        var typeId = Type.makeLazyTypeId();
         var symbol = new Symbol.Symbol(id.id(), typeId);
         this.currentScope().addSymbol(symbol, id.exported());
         if (!id.exported())
@@ -1725,7 +1728,7 @@ exports.TypeDeclaration = ChainedContext.extend({
         this.__symbol = symbol;
     },
     setType: function(type){
-        this.__symbol.info().define(type);
+        Type.defineTypeId(this.__symbol.info(), type);
         this.currentScope().resolve(this.__symbol);
     },
     typeName: function(){return this.__id.id();},
@@ -1802,7 +1805,7 @@ exports.ModuleDeclaration = ChainedContext.extend({
         var moduleAliases = {};
         for(var i = 0; i < modules.length; ++i){
             var s = modules[i];
-            var name = s.info().name();
+            var name = Type.moduleName(s.info());
             this.__imports[name] = s;
             scope.addSymbol(s);
             moduleAliases[name] = s.id();
@@ -1813,8 +1816,10 @@ exports.ModuleDeclaration = ChainedContext.extend({
         this.codeGenerator().write(this.__moduleGen.prolog());
     },
     qualifyScope: function(scope){
-        if (scope != this.__moduleScope && scope instanceof Scope.Module)
-            return this.__imports[scope.module().id()].id() + ".";
+        if (scope != this.__moduleScope && scope instanceof Scope.Module){
+            var id = scope.module().id();
+            return this.__imports[id].id() + ".";
+        }
         return "";
     }
 });

+ 16 - 14
src/eberon/eberon_context.js

@@ -5,7 +5,7 @@ var Context = require("context.js");
 var Errors = require("js/Errors.js");
 var Symbol = require("symbol.js");
 var Procedure = require("procedure.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
 function methodCallGenerator(context, id, type){
     return new Procedure.CallGenerator(context, id, type);
@@ -108,7 +108,8 @@ var Designator = Context.Designator.extend({
 
 var RecordType = Type.Record.extend({
     init: function EberonContext$RecordType(name, cons, scope){
-        Type.Record.prototype.init.call(this, name, cons, scope);
+        Type.Record.prototype.init.call(this);
+        Type.initRecord(this, name, cons, scope)
         this.__finalized = false;
         this.__declaredMethods = {};
         this.__definedMethods = [];
@@ -153,12 +154,12 @@ var RecordType = Type.Record.extend({
             this.__nonExportedMethods.push(id);
     },
     defineMethod: function(methodId, type){
-        var base = this.baseType();
+        var base = Type.recordBase(this);
         var id = methodId.id();
         var existing = this.findSymbol(id);
         if (!(existing instanceof MethodType)){
             throw new Errors.Error(
-                  "'" + this.name() + "' has no declaration for method '" + id 
+                  "'" + Type.typeName(this) + "' has no declaration for method '" + id 
                 + "'");
         }
         //if (this.__definedMethods.indexOf(id) != -1)
@@ -183,7 +184,7 @@ var RecordType = Type.Record.extend({
     abstractMethods: function(){return this.__abstractMethods;},
     __collectAbstractMethods: function(){
         var selfMethods = Object.keys(this.__declaredMethods);
-        var baseType = this.baseType();
+        var baseType = Type.recordBase(this);
         var methods = baseType ? baseType.abstractMethods().concat(selfMethods)
                                : selfMethods;
         for(var i = 0; i < methods.length; ++i){
@@ -221,7 +222,7 @@ var RecordType = Type.Record.extend({
         var am = this.abstractMethods();
         if (am.length)
             throw new Errors.Error(
-                  "cannot instantiate '" + this.name() 
+                  "cannot instantiate '" + Type.typeName(this) 
                 + "' because it has abstract method(s): "
                 + am.join(", ")
                 );
@@ -230,20 +231,21 @@ var RecordType = Type.Record.extend({
         var type = this;
         var result;
         while (type && !(result = type.__declaredMethods[id]))
-            type = type.baseType();
+            type = Type.recordBase(type);
         return result;
     },
     __hasMethodDefinition: function(id){
         var type = this;
         while (type && type.__definedMethods.indexOf(id) == -1)
-            type = type.baseType();
+            type = Type.recordBase(type);
         return type;
     }
 });
 
 var RecordDecl = Context.RecordDecl.extend({
     init: function EberonContext$RecordDecl(context){
-        Context.RecordDecl.prototype.init.call(this, context, RecordType);
+        var makeRecord = function(name, cons, scope){return new RecordType(name, cons, scope);};
+        Context.RecordDecl.prototype.init.call(this, context, makeRecord);
     },
     handleMessage: function(msg){
         if (msg instanceof MethodOrProcMsg)
@@ -287,7 +289,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
 
             Context.ProcDecl.prototype.handleIdentdef.call(
                 this,
-                type ? new Context.IdentdefInfo(type.name() + "." + id.id(),
+                type ? new Context.IdentdefInfo(Type.typeName(type) + "." + id.id(),
                                                 id.exported()) 
                      : id
                 );
@@ -297,7 +299,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     },
     _prolog: function(){
         return this.__boundType
-            ? this.__boundType.name() + ".prototype." + this.__methodId.id() + " = function("
+            ? Type.typeName(this.__boundType) + ".prototype." + this.__methodId.id() + " = function("
             : Context.ProcDecl.prototype._prolog.call(this);
     },
     setType: function(type){
@@ -340,17 +342,17 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         if (!this.__methodId)
             throw new Errors.Error("SUPER can be used only in methods");
 
-        var baseType = this.__boundType.baseType();
+        var baseType = Type.recordBase(this.__boundType);
         if (!baseType)
             throw new Errors.Error(
-                  "'" + this.__boundType.name()
+                  "'" + Type.typeName(this.__boundType)
                 + "' has no base type - SUPER cannot be used");
 
         var id = this.__methodId.id();
         baseType.requireMethodDefinition(id);
         return {
             symbol: new Symbol.Symbol("method", new MethodType(this.__methodType.procType(), superMethodCallGenerator)),
-            code: this.__boundType.baseType().name() + ".prototype." + id + ".call"
+            code: Type.typeName(baseType) + ".prototype." + id + ".call"
         };
     }
 });

+ 19 - 0
src/js/Context.js

@@ -0,0 +1,19 @@
+var RTL$ = require("rtl.js");
+var JsString = require("js/JsString.js");
+var Object = require("js/Object.js");
+var Scope = RTL$.extend({
+	init: function Scope(){
+	}
+});
+var Type = RTL$.extend({
+	init: function Type(){
+		this.handleChar = null;
+		this.handleLiteral = null;
+		this.handleString = null;
+		this.handleIdent = null;
+		this.isLexem = null;
+		this.qualifyScope = null;
+	}
+});
+exports.Scope = Scope;
+exports.Type = Type;

+ 66 - 0
src/js/JsArray.js

@@ -0,0 +1,66 @@
+var RTL$ = require("rtl.js");
+var JS = GLOBAL;
+var Object = require("js/Object.js");
+var JsString = require("js/JsString.js");
+var Type = RTL$.extend({
+	init: function Type(){
+	}
+});
+var Strings = RTL$.extend({
+	init: function Strings(){
+	}
+});
+
+function len(a/*Type*/){
+	var result = 0;
+	result = a.length;
+	return result;
+}
+
+function stringsLen(a/*Strings*/){
+	var result = 0;
+	result = a.length;
+	return result;
+}
+
+function add(a/*Type*/, o/*PType*/){
+	a.push(o);
+}
+
+function stringsAdd(a/*Strings*/, o/*Type*/){
+	a.push(o);
+}
+
+function at(a/*Strings*/, i/*INTEGER*/){
+	var result = null;
+	result = a[i];
+	return result;
+}
+
+function stringsAt(a/*Strings*/, i/*INTEGER*/){
+	var result = null;
+	result = a[i];
+	return result;
+}
+
+function make(){
+	var result = null;
+	result = [];
+	return result;
+}
+
+function makeStrings(){
+	var result = null;
+	result = [];
+	return result;
+}
+exports.Type = Type;
+exports.Strings = Strings;
+exports.len = len;
+exports.stringsLen = stringsLen;
+exports.add = add;
+exports.stringsAdd = stringsAdd;
+exports.at = at;
+exports.stringsAt = stringsAt;
+exports.make = make;
+exports.makeStrings = makeStrings;

+ 40 - 0
src/js/JsMap.js

@@ -0,0 +1,40 @@
+var RTL$ = require("rtl.js");
+var JS = GLOBAL;
+var JsString = require("js/JsString.js");
+var Object = require("js/Object.js");
+var Type = RTL$.extend({
+	init: function Type(){
+	}
+});
+
+function make(){
+	var result = null;
+	result = {};
+	return result;
+}
+
+function has(m/*Type*/, s/*Type*/){
+	var result = false;
+	result = m.hasOwnProperty(s);
+	return result;
+}
+
+function find(m/*Type*/, s/*Type*/, r/*VAR PType*/){
+	var result = false;
+	var value = m[s]; if (value !== undefined){result = true; r.set(value);};
+	return result;
+}
+
+function put(m/*Type*/, s/*Type*/, o/*PType*/){
+	m[s] = o;
+}
+
+function erase(m/*Type*/, s/*Type*/){
+	delete m[s];
+}
+exports.Type = Type;
+exports.make = make;
+exports.has = has;
+exports.find = find;
+exports.put = put;
+exports.erase = erase;

+ 14 - 0
src/js/JsString.js

@@ -15,6 +15,12 @@ function make(s/*ARRAY OF CHAR*/){
 	return result;
 }
 
+function fromInt(i/*INTEGER*/){
+	var result = null;
+	result = '' + i;
+	return result;
+}
+
 function len(self/*Type*/){
 	var result = 0;
 	result = self.length;
@@ -51,11 +57,19 @@ function appendChar(self/*Type*/, c/*CHAR*/){
 	result += JS.String.fromCharCode(c);
 	return result;
 }
+
+function concat(self/*Type*/, add/*Type*/){
+	var result = null;
+	result = self + add;
+	return result;
+}
 exports.Type = Type;
 exports.make = make;
+exports.fromInt = fromInt;
 exports.len = len;
 exports.at = at;
 exports.indexOf = indexOf;
 exports.indexOfFrom = indexOfFrom;
 exports.substr = substr;
 exports.appendChar = appendChar;
+exports.concat = concat;

+ 6 - 0
src/js/Object.js

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

+ 594 - 0
src/js/Types.js

@@ -0,0 +1,594 @@
+var RTL$ = require("rtl.js");
+var Context = require("js/Context.js");
+var Errors = require("js/Errors.js");
+var JS = GLOBAL;
+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 openArrayLength = 0;
+var Id = Object.Type.extend({
+	init: function Id(){
+		Object.Type.prototype.init.call(this);
+	}
+});
+var Type = Id.extend({
+	init: function Type(){
+		Id.prototype.init.call(this);
+	}
+});
+var TypeId = RTL$.extend({
+	init: function TypeId(){
+		this.mType = null;
+	}
+});
+var ForwardTypeId = TypeId.extend({
+	init: function ForwardTypeId(){
+		TypeId.prototype.init.call(this);
+		this.resolve = null;
+	}
+});
+var LazyTypeId = TypeId.extend({
+	init: function LazyTypeId(){
+		TypeId.prototype.init.call(this);
+	}
+});
+var Const = Id.extend({
+	init: function Const(){
+		Id.prototype.init.call(this);
+		this.type = null;
+		this.value = undefined;
+	}
+});
+var Variable = Id.extend({
+	init: function Variable(){
+		Id.prototype.init.call(this);
+		this.type = null;
+		this.readOnly = false;
+	}
+});
+var VariableRef = Variable.extend({
+	init: function VariableRef(){
+		Variable.prototype.init.call(this);
+	}
+});
+var ExportedVariable = Variable.extend({
+	init: function ExportedVariable(){
+		Variable.prototype.init.call(this);
+	}
+});
+var String = Type.extend({
+	init: function String(){
+		Type.prototype.init.call(this);
+		this.s = null;
+	}
+});
+var NamedType = Type.extend({
+	init: function NamedType(){
+		Type.prototype.init.call(this);
+		this.name = null;
+	}
+});
+var Array = NamedType.extend({
+	init: function Array(){
+		NamedType.prototype.init.call(this);
+		this.mInitializer = null;
+		this.elementsType = null;
+		this.len = 0;
+	}
+});
+var Pointer = NamedType.extend({
+	init: function Pointer(){
+		NamedType.prototype.init.call(this);
+		this.base = null;
+	}
+});
+var Procedure = NamedType.extend({
+	init: function Procedure(){
+		NamedType.prototype.init.call(this);
+	}
+});
+var BasicType = NamedType.extend({
+	init: function BasicType(){
+		NamedType.prototype.init.call(this);
+		this.mInitializer = null;
+	}
+});
+var Field = RTL$.extend({
+	init: function Field(){
+		this.id = null;
+		this.exported = null;
+	}
+});
+var Record = NamedType.extend({
+	init: function Record(){
+		NamedType.prototype.init.call(this);
+		this.fields = null;
+		this.base = null;
+		this.cons = null;
+		this.scope = null;
+		this.notExported = null;
+	}
+});
+var NonExportedRecord = Record.extend({
+	init: function NonExportedRecord(){
+		Record.prototype.init.call(this);
+	}
+});
+var Nil = Id.extend({
+	init: function Nil(){
+		Id.prototype.init.call(this);
+	}
+});
+var Module = Id.extend({
+	init: function Module(){
+		Id.prototype.init.call(this);
+		this.name = null;
+	}
+});
+var anonymous$1 = RTL$.extend({
+	init: function anonymous$1(){
+		this.bool = null;
+		this.ch = null;
+		this.integer = null;
+		this.uint8 = null;
+		this.real = null;
+		this.set = null;
+	}
+});
+var basic = null;
+var numeric = null;
+var nil = null;
+TypeId.prototype.description = function(){
+	var t = null;
+	t = this.type();
+	return JsString.concat(JsString.make("type "), t.description());
+}
+TypeId.prototype.type = function(){
+	return this.mType;
+}
+
+function finalizeRecord(closure/*PType*/){
+	RTL$.typeGuard(closure, Record).finalize();
+}
+Record.prototype.finalize = function(){
+	var i = 0;
+	for (i = 0; i <= JsArray.stringsLen(this.notExported) - 1 | 0; ++i){
+		JsMap.erase(this.fields, JsArray.stringsAt(this.notExported, i));
+	}
+	this.notExported = null;
+}
+
+function initRecord(r/*PRecord*/, name/*Type*/, cons/*Type*/, scope/*PScope*/){
+	r.name = name;
+	r.cons = cons;
+	r.scope = scope;
+	r.fields = JsMap.make();
+	r.notExported = JsArray.makeStrings();
+	scope.addFinalizer(finalizeRecord, r);
+}
+
+function makeNonExportedRecord(cons/*Type*/, scope/*PScope*/, base/*PRecord*/){
+	var result = null;
+	result = new NonExportedRecord();
+	initRecord(result, null, cons, scope);
+	result.base = base;
+	return result;
+}
+TypeId.prototype.strip = function(){
+	var r = null;
+	if (this.mType instanceof Record){
+		r = RTL$.typeGuard(this.mType, Record);
+		this.mType = makeNonExportedRecord(r.cons, r.scope, r.base);
+	}
+	else {
+		this.mType = null;
+	}
+}
+
+function makeForwardTypeId(resolve/*ResolveTypeCallback*/){
+	var result = null;
+	result = new ForwardTypeId();
+	result.resolve = resolve;
+	return result;
+}
+ForwardTypeId.prototype.type = function(){
+	if (this.mType == null){
+		this.mType = this.resolve();
+	}
+	return this.mType;
+}
+
+function defineTypeId(tId/*VAR LazyTypeId*/, t/*PType*/){
+	tId.mType = t;
+}
+
+function typeName(type/*NamedType*/){
+	return type.name;
+}
+Procedure.prototype.idType = function(){
+	return JsString.make("procedure");
+}
+String.prototype.idType = function(){
+	return JsString.make("string");
+}
+String.prototype.description = function(){
+	var prefix = null;
+	if (JsString.len(this.s) == 1){
+		prefix = JsString.make("single-");
+	}
+	else {
+		prefix = JsString.make("multi-");
+	}
+	return JsString.concat(prefix, JsString.make("character string"));
+}
+
+function stringValue(s/*String*/){
+	return s.s;
+}
+
+function stringLen(s/*String*/){
+	return JsString.len(s.s);
+}
+
+function stringAsChar(s/*String*/, c/*VAR CHAR*/){
+	var result = false;
+	result = stringLen(s) == 1;
+	if (result){
+		c.set(JsString.at(s.s, 0));
+	}
+	return result;
+}
+Const.prototype.idType = function(){
+	return JsString.make("constant");
+}
+
+function constType(c/*Const*/){
+	return c.type;
+}
+
+function constValue(c/*Const*/){
+	return c.value;
+}
+Variable.prototype.idType = function(){
+	var result = null;
+	if (this.readOnly){
+		result = JsString.make("read-only variable");
+	}
+	else {
+		result = JsString.make("variable");
+	}
+	return result;
+}
+
+function variableType(v/*Variable*/){
+	return v.type;
+}
+
+function isVariableReadOnly(v/*Variable*/){
+	return v.readOnly;
+}
+ExportedVariable.prototype.idType = function(){
+	return JsString.make("imported variable");
+}
+BasicType.prototype.idType = function(){
+	return JsString.make("type");
+}
+BasicType.prototype.description = function(){
+	return this.name;
+}
+BasicType.prototype.initializer = function(cx/*Type*/){
+	return this.mInitializer;
+}
+Nil.prototype.idType = function(){
+	return JsString.make("NIL");
+}
+
+function isInt(t/*PType*/){
+	return t == basic.integer || t == basic.uint8;
+}
+
+function intsDescription(){
+	return JsString.make("'INTEGER' or 'BYTE'");
+}
+
+function isString(t/*PType*/){
+	return t instanceof Array && RTL$.typeGuard(t, Array).elementsType == basic.ch || t instanceof String;
+}
+
+function moduleName(m/*Module*/){
+	return m.name;
+}
+
+function makeBasic(name/*ARRAY OF CHAR*/, initializer/*ARRAY OF CHAR*/){
+	var result = null;
+	result = new BasicType();
+	result.name = JsString.make(name);
+	result.mInitializer = JsString.make(initializer);
+	return result;
+}
+Record.prototype.idType = function(){
+	return JsString.make("record");
+}
+Record.prototype.description = function(){
+	var result = null;
+	if (this.name != null){
+		result = this.name;
+	}
+	else {
+		result = JsString.make("anonymous RECORD");
+	}
+	return result;
+}
+Record.prototype.initializer = function(cx/*Type*/){
+	return JsString.concat(JsString.concat(JsString.concat(JsString.make("new "), cx.qualifyScope(this.scope)), this.cons), JsString.make("()"));
+}
+Record.prototype.addField = function(f/*Field*/, type/*PType*/){
+	if (JsMap.has(this.fields, f.id())){
+		Errors.raise(JsString.concat(JsString.concat(JsString.make("duplicated field: '"), f.id()), JsString.make("'")));
+	}
+	if (this.base != null && this.base.findSymbol(f.id()) != null){
+		Errors.raise(JsString.concat(JsString.concat(JsString.make("base record already has field: '"), f.id()), JsString.make("'")));
+	}
+	JsMap.put(this.fields, f.id(), type);
+	if (!f.exported()){
+		JsArray.stringsAdd(this.notExported, f.id());
+	}
+}
+Record.prototype.findSymbol = function(id/*Type*/){
+	var result = null;
+	if (!JsMap.find(this.fields, id, {set: function($v){result = $v;}, get: function(){return result;}}) && this.base != null){
+		result = this.base.findSymbol(id);
+	}
+	return result;
+}
+
+function recordBase(r/*Record*/){
+	return r.base;
+}
+
+function setRecordBase(r/*Record*/, type/*PRecord*/){
+	r.base = type;
+}
+
+function recordScope(r/*Record*/){
+	return r.scope;
+}
+
+function recordConstructor(r/*Record*/){
+	return r.cons;
+}
+
+function recordOwnFields(r/*Record*/){
+	return r.fields;
+}
+Pointer.prototype.idType = function(){
+	return JsString.make("pointer");
+}
+
+function pointerBase(p/*Pointer*/){
+	var result = null;
+	result = p.base.type();
+	return RTL$.typeGuard(result, Record);
+}
+Pointer.prototype.description = function(){
+	var base = null;
+	var result = null;
+	if (this.name != null){
+		result = this.name;
+	}
+	else {
+		base = pointerBase(this);
+		result = JsString.concat(JsString.make("POINTER TO "), base.description());
+	}
+	return result;
+}
+Pointer.prototype.initializer = function(cx/*Type*/){
+	return JsString.make("null");
+}
+Array.prototype.idType = function(){
+	return JsString.make("array");
+}
+
+function foldArrayDimensions(a/*Array*/, sizes/*VAR Type*/, of/*VAR Type*/){
+	if (a.len != openArrayLength && a.elementsType instanceof Array){
+		foldArrayDimensions(RTL$.typeGuard(a.elementsType, Array), sizes, of);
+		sizes.set(JsString.concat(JsString.concat(JsString.fromInt(a.len), JsString.make(", ")), sizes.get()));
+	}
+	else {
+		if (a.len != openArrayLength){
+			sizes.set(JsString.fromInt(a.len));
+		}
+		of.set(a.elementsType.description());
+	}
+}
+Array.prototype.description = function(){
+	var result = null;
+	var sizes = null;var of = null;
+	if (this.elementsType == null){
+		result = this.name;
+	}
+	else {
+		foldArrayDimensions(this, {set: function($v){sizes = $v;}, get: function(){return sizes;}}, {set: function($v){of = $v;}, get: function(){return of;}});
+		if (sizes == null){
+			sizes = JsString.make("");
+		}
+		else {
+			sizes = JsString.concat(JsString.make(" "), sizes);
+		}
+		result = JsString.concat(JsString.concat(JsString.concat(JsString.make("ARRAY"), sizes), JsString.make(" OF ")), of);
+	}
+	return result;
+}
+Array.prototype.initializer = function(cx/*Type*/){
+	return this.mInitializer;
+}
+
+function arrayElementsType(a/*Array*/){
+	return a.elementsType;
+}
+
+function arrayLength(a/*Array*/){
+	return a.len;
+}
+String.prototype.initializer = function(cx/*Type*/){
+	return JsString.make("null");
+}
+Procedure.prototype.initializer = function(cx/*Type*/){
+	return JsString.make("null");
+}
+Procedure.prototype.description = function(){
+	return this.name;
+}
+Module.prototype.idType = function(){
+	return JsString.make("module");
+}
+
+function makeTypeId(type/*PType*/){
+	var result = null;
+	result = new TypeId();
+	result.mType = type;
+	return result;
+}
+
+function makeLazyTypeId(){
+	var result = null;
+	result = new LazyTypeId();
+	return result;
+}
+
+function makeString(s/*Type*/){
+	var result = null;
+	result = new String();
+	result.s = s;
+	return result;
+}
+
+function makeArray(name/*Type*/, initializer/*Type*/, elementsType/*PType*/, len/*INTEGER*/){
+	var result = null;
+	result = new Array();
+	result.name = name;
+	result.mInitializer = initializer;
+	result.elementsType = elementsType;
+	result.len = len;
+	return result;
+}
+
+function makePointer(name/*Type*/, base/*PTypeId*/){
+	var result = null;
+	result = new Pointer();
+	result.name = name;
+	result.base = base;
+	return result;
+}
+
+function makeRecord(name/*Type*/, cons/*Type*/, scope/*PScope*/){
+	var result = null;
+	result = new Record();
+	initRecord(result, name, cons, scope);
+	return result;
+}
+
+function makeConst(type/*PType*/, value/*JS.var*/){
+	var result = null;
+	result = new Const();
+	result.type = type;
+	result.value = value;
+	return result;
+}
+
+function makeVariable(type/*PType*/, readOnly/*BOOLEAN*/){
+	var result = null;
+	result = new Variable();
+	result.type = type;
+	result.readOnly = readOnly;
+	return result;
+}
+
+function makeVariableRef(type/*PType*/){
+	var result = null;
+	result = new VariableRef();
+	result.type = type;
+	result.readOnly = false;
+	return result;
+}
+
+function makeExportedVariable(v/*Variable*/){
+	var result = null;
+	result = new ExportedVariable();
+	result.type = v.type;
+	result.readOnly = true;
+	return result;
+}
+
+function initProcedure(p/*Procedure*/, name/*Type*/){
+	p.name = name;
+}
+
+function initModule(m/*Module*/, name/*Type*/){
+	m.name = name;
+}
+basic = new anonymous$1();
+basic.bool = makeBasic("BOOLEAN", "false");
+basic.ch = makeBasic("CHAR", "0");
+basic.integer = makeBasic("INTEGER", "0");
+basic.uint8 = makeBasic("BYTE", "0");
+basic.real = makeBasic("REAL", "0");
+basic.set = makeBasic("SET", "0");
+numeric = JsArray.make();
+JsArray.add(numeric, basic.integer);
+JsArray.add(numeric, basic.uint8);
+JsArray.add(numeric, basic.real);
+nil = new Nil();
+exports.openArrayLength = openArrayLength;
+exports.Type = Type;
+exports.TypeId = TypeId;
+exports.ForwardTypeId = ForwardTypeId;
+exports.Const = Const;
+exports.Variable = Variable;
+exports.VariableRef = VariableRef;
+exports.String = String;
+exports.Array = Array;
+exports.Pointer = Pointer;
+exports.Procedure = Procedure;
+exports.Record = Record;
+exports.NonExportedRecord = NonExportedRecord;
+exports.Module = Module;
+exports.basic = function(){return basic;};
+exports.numeric = function(){return numeric;};
+exports.nil = function(){return nil;};
+exports.initRecord = initRecord;
+exports.makeForwardTypeId = makeForwardTypeId;
+exports.defineTypeId = defineTypeId;
+exports.typeName = typeName;
+exports.stringValue = stringValue;
+exports.stringLen = stringLen;
+exports.stringAsChar = stringAsChar;
+exports.constType = constType;
+exports.constValue = constValue;
+exports.variableType = variableType;
+exports.isVariableReadOnly = isVariableReadOnly;
+exports.isInt = isInt;
+exports.intsDescription = intsDescription;
+exports.isString = isString;
+exports.moduleName = moduleName;
+exports.recordBase = recordBase;
+exports.setRecordBase = setRecordBase;
+exports.recordScope = recordScope;
+exports.recordConstructor = recordConstructor;
+exports.recordOwnFields = recordOwnFields;
+exports.pointerBase = pointerBase;
+exports.arrayElementsType = arrayElementsType;
+exports.arrayLength = arrayLength;
+exports.makeTypeId = makeTypeId;
+exports.makeLazyTypeId = makeLazyTypeId;
+exports.makeString = makeString;
+exports.makeArray = makeArray;
+exports.makePointer = makePointer;
+exports.makeRecord = makeRecord;
+exports.makeConst = makeConst;
+exports.makeVariable = makeVariable;
+exports.makeVariableRef = makeVariableRef;
+exports.makeExportedVariable = makeExportedVariable;
+exports.initProcedure = initProcedure;
+exports.initModule = initModule;

+ 10 - 7
src/module.js

@@ -4,7 +4,7 @@ var Code = require("code.js");
 var Errors = require("js/Errors.js");
 var Procedure = require("procedure.js");
 var Symbol = require("symbol.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
 var AnyTypeProc = Type.Procedure.extend({
     init: function AnyTypeProc(){
@@ -15,10 +15,12 @@ var AnyTypeProc = Type.Procedure.extend({
 
 var anyProc = new AnyTypeProc();
 
-var AnyType = Type.Basic.extend({
+var AnyType = Type.Type.extend({
     init: function AnyType(){
-        Type.Basic.prototype.init.call(this, "JS.var");
+        Type.Type.prototype.init.call(this);
     },
+    description: function(){return "JS.var";},
+    initializer: function(){return undefined;},
     findSymbol: function(){return this;},
     callGenerator: function(context, id){
         return new Procedure.CallGenerator(context, id, anyProc);
@@ -46,7 +48,7 @@ var doProcSymbol = (function(){
                     "string is expected as an argument of " + description
                     + ", got " + type.description());
             
-            this.__code = type.value();
+            this.__code = Type.stringValue(type);
             return Procedure.CallGenerator.prototype.checkArgument.call(this, pos, e);
         },
         epilog: function(){return "";},
@@ -59,7 +61,8 @@ var doProcSymbol = (function(){
     var args = [new Procedure.Arg(undefined, false)];
     var ProcType = Type.Procedure.extend({
         init: function(){
-            Type.Procedure.prototype.init.call(this, doProcId);
+            Type.Procedure.prototype.init.call(this);
+            Type.initProcedure(this, doProcId);
         },
         description: function(){return description;},
         args: function(){return args;},
@@ -73,14 +76,14 @@ var doProcSymbol = (function(){
 })();
 
 var varTypeSymbol = function(){
-    return new Symbol.Symbol(varTypeId, new Type.TypeId(any));
+    return new Symbol.Symbol(varTypeId, Type.makeTypeId(any));
 }();
 
 var JSModule = Type.Module.extend({
     init: function Module$JSModule(){
         Type.Module.prototype.init.call(this);
+        Type.initModule(this, "this");
     },
-    name: function(){return "this";},
     findSymbol: function(id){
         return new Symbol.Found(
             id == doProcId ? doProcSymbol

+ 8 - 4
src/nodejs.js

@@ -10,18 +10,21 @@ var fs = require("fs");
 var path = require("path");
 
 var ModuleGenerator = Class.extend({
-    init: function Nodejs$ModuleGenerator(name, imports){
+    init: function Nodejs$ModuleGenerator(name, imports, importDir){
         this.__name = name;
         this.__imports = imports;
+        this.__importDir = importDir;
     },
     prolog: function(){
         var result = "";            
         var modules = this.__imports;
         for(var name in modules){
             var alias = modules[name];
+            var importName = this.__importDir ? this.__importDir + "/" + name
+                                              : name;
             result += "var " + alias + " = " + (name == "this" 
                 ? "GLOBAL"
-                : "require(\"" + name + ".js\")") + ";\n";
+                : "require(\"" + importName + ".js\")") + ";\n";
         }
         return result;
     },
@@ -50,10 +53,11 @@ function writeCompiledModule(name, code, outDir){
     fs.writeFileSync(filePath, code);
     }
 
-function compile(sources, grammar, handleErrors, outDir){
+function compile(sources, grammar, handleErrors, outDir, importDir){
     var rtlCodeWatcher = new RtlCodeUsingWatcher();
     var rtl = new RTL(rtlCodeWatcher.using.bind(rtlCodeWatcher));
-    var moduleCode = function(name, imports){return new ModuleGenerator(name, imports);};
+    var moduleCode = function(name, imports){
+        return new ModuleGenerator(name, imports, importDir);};
 
     var compiledFilesStack = [];
     oc.compileModules(

+ 1 - 1
src/ob/Context.ob

@@ -4,7 +4,7 @@ TYPE
     FinalizerProc = PROCEDURE(closure: Object.PType);
 
     Scope* = RECORD
-        PROCEDURE addFinalizer*(closure: Object.PType; finalizer: FinalizerProc)
+        PROCEDURE addFinalizer*(finalizer: FinalizerProc; closure: Object.PType)
     END;
     PScope* = POINTER TO Scope;
 

+ 44 - 13
src/ob/JsArray.ob

@@ -1,19 +1,50 @@
 MODULE JsArray;
 IMPORT JS, Object, JsString;
 TYPE
-    Methods* = RECORD
-        PROCEDURE len*(): INTEGER;
-        PROCEDURE add*(s: Object.PType);
-        PROCEDURE at*(i: INTEGER): Object.PType
-    END;
-    Type* = POINTER TO Methods;
-
-    StringMethods* = RECORD
-        PROCEDURE len*(): INTEGER;
-        PROCEDURE add*(s: JsString.Type);
-        PROCEDURE at*(i: INTEGER): JsString.Type
-    END;
-    Strings* = POINTER TO StringMethods;
+    Type* = POINTER TO RECORD END;
+    Strings* = POINTER TO RECORD END;
+
+PROCEDURE len*(a: Type): INTEGER;
+VAR
+    result: INTEGER;
+BEGIN
+    JS.do("result = a.length");
+    RETURN result
+END len;
+
+PROCEDURE stringsLen*(a: Strings): INTEGER;
+VAR
+    result: INTEGER;
+BEGIN
+    JS.do("result = a.length");
+    RETURN result
+END stringsLen;
+
+PROCEDURE add*(a: Type; o: Object.PType);
+BEGIN
+    JS.do("a.push(o)");
+END add;
+
+PROCEDURE stringsAdd*(a: Strings; o: JsString.Type);
+BEGIN
+    JS.do("a.push(o)");
+END stringsAdd;
+
+PROCEDURE at*(a: Strings; i: INTEGER): Object.PType;
+VAR
+    result: Object.PType;
+BEGIN
+    JS.do("result = a[i]");
+    RETURN result
+END at;
+
+PROCEDURE stringsAt*(a: Strings; i: INTEGER): JsString.Type;
+VAR
+    result: JsString.Type;
+BEGIN
+    JS.do("result = a[i]");
+    RETURN result
+END stringsAt;
 
 PROCEDURE make*(): Type;
 VAR

+ 215 - 99
src/ob/Types.ob

@@ -1,5 +1,8 @@
 MODULE Types;
-IMPORT Context, Errors, JS, JsArray, JsMap, JsString, Object;
+IMPORT
+    Context, Errors, JS, JsArray, JsMap, JsString, Object;
+CONST
+    openArrayLength* = 0;
 
 TYPE
     Id = RECORD (Object.Type)
@@ -8,23 +11,24 @@ TYPE
 
     PId = POINTER TO Id;
     
-    Type = RECORD(Id)
+    Type* = RECORD(Id)
         PROCEDURE description(): JsString.Type;
         PROCEDURE initializer(cx: Context.Type): JsString.Type
     END;
     PType = POINTER TO Type;
     
-    TypeId = RECORD
+    TypeId* = RECORD
         PROCEDURE type(): PType;
         PROCEDURE description(): JsString.Type;
         PROCEDURE strip();
 
         mType: PType
     END;
+    PTypeId = POINTER TO TypeId;
 
     ResolveTypeCallback = PROCEDURE(): PType;
 
-    ForwardTypeId = RECORD(TypeId)
+    ForwardTypeId* = RECORD(TypeId)
         resolve: ResolveTypeCallback
     END;
 
@@ -35,30 +39,42 @@ TYPE
 
     PLazyTypeId = POINTER TO LazyTypeId;
 
-    Const = RECORD(Id)
-        mType: PType;
-        mValue: JS.var
+    Const* = RECORD(Id)
+        type: PType;
+        value: JS.var
     END;
 
-    Variable = RECORD(Id)
-        mType: PType;
-        isReadOnly: BOOLEAN
+    PConst = POINTER TO Const;
+
+    Variable* = RECORD(Id)
+        type: PType;
+        readOnly: BOOLEAN
     END;
 
-    VariableRef = RECORD(Variable)
+    PVariable = POINTER TO Variable;
+
+    VariableRef* = RECORD(Variable)
     END;
 
+    PVariableRef = POINTER TO VariableRef;
+
     ExportedVariable = RECORD(Variable)
     END;
 
-    String = RECORD(Type)
+    PExportedVariable = POINTER TO ExportedVariable;
+
+    String* = RECORD(Type)
         s: JsString.Type
     END;
 
     PString = POINTER TO String;
 
-    Array = RECORD(Type)
-        name: JsString.Type;
+    NamedType = RECORD(Type)
+        name: JsString.Type
+    END;
+
+    Array* = RECORD(NamedType)
+        mInitializer: JsString.Type;
         elementsType: PType;
         len: INTEGER
     END;
@@ -67,33 +83,33 @@ TYPE
 
     PRecord = POINTER TO Record;
 
-    Pointer = RECORD(Type)
-        name: JsString.Type;
-        base: PRecord
+    Pointer* = RECORD(NamedType)
+        base: PTypeId
     END;
 
     PPointer = POINTER TO Pointer;
 
-    Procedure = RECORD(Type)
+    Procedure* = RECORD(NamedType)
     END;
 
-    BasicType = RECORD(Type)
-        name: JsString.Type;
+    PProcedure = POINTER TO Procedure;
+
+    BasicType = RECORD(NamedType)
         mInitializer: JsString.Type
     END;
 
     PBasicType = POINTER TO BasicType;
 
     Field = RECORD
-        id: JsString.Type;
-        exported: BOOLEAN
+        id: PROCEDURE(): JsString.Type;
+        exported: PROCEDURE(): BOOLEAN
     END;
 
-    Record = RECORD(Type)
+    Record* = RECORD(NamedType)
         PROCEDURE addField(f: Field; type: PType);
         PROCEDURE findSymbol(id: JsString.Type): PType;
+        PROCEDURE finalize();
 
-        name:   JsString.Type;
         fields: JsMap.Type;
         base:   PRecord;
         cons:   JsString.Type;
@@ -101,17 +117,19 @@ TYPE
         notExported: JsArray.Strings
     END;
     
-    NonExportedRecord = RECORD(Record)
+    NonExportedRecord* = RECORD(Record)
     END;
     PNonExportedRecord = POINTER TO NonExportedRecord;
 
     Nil = RECORD(Id)
     END;
 
-    Module = RECORD(Id)
+    Module* = RECORD(Id)
         name: JsString.Type
     END;
 
+    PModule = POINTER TO Module;
+
 VAR
     basic*: POINTER TO RECORD
         bool, ch, integer, uint8, real, set: PBasicType
@@ -128,13 +146,41 @@ BEGIN
     RETURN JsString.concat(JsString.make("type "), t.description())
 END TypeId.description;
 
+PROCEDURE TypeId.type(): PType;
+    RETURN SELF.mType
+END TypeId.type;
+
+PROCEDURE finalizeRecord(closure: Object.PType);
+BEGIN
+    closure(PRecord).finalize();
+END finalizeRecord;
+
+PROCEDURE Record.finalize();
+VAR
+    i: INTEGER;
+BEGIN
+    FOR i := 0 TO JsArray.stringsLen(SELF.notExported) - 1 DO
+        JsMap.erase(SELF.fields, JsArray.stringsAt(SELF.notExported, i))
+    END;
+    SELF.notExported := NIL;
+END Record.finalize;
+
+PROCEDURE initRecord*(r: PRecord; name: JsString.Type; cons: JsString.Type; scope: Context.PScope);
+BEGIN
+    r.name := name;
+    r.cons := cons;
+    r.scope := scope;
+    r.fields := JsMap.make();
+    r.notExported := JsArray.makeStrings();
+    scope.addFinalizer(finalizeRecord, r);
+END initRecord;
+
 PROCEDURE makeNonExportedRecord(cons: JsString.Type; scope: Context.PScope; base: PRecord): PNonExportedRecord;
 VAR
     result: PNonExportedRecord;
 BEGIN
     NEW(result);
-    result.cons := cons;
-    result.scope := scope;
+    initRecord(result, NIL, cons, scope);
     result.base := base;
     RETURN result
 END makeNonExportedRecord;    
@@ -151,7 +197,7 @@ BEGIN
     END;
 END TypeId.strip;
 
-PROCEDURE makeForwardTypeId(resolve: ResolveTypeCallback): PForwardTypeId;
+PROCEDURE makeForwardTypeId*(resolve: ResolveTypeCallback): PForwardTypeId;
 VAR
     result: PForwardTypeId;
 BEGIN
@@ -168,15 +214,15 @@ BEGIN
     RETURN SELF.mType
 END ForwardTypeId.type;
 
-PROCEDURE LazyTypeId.type(): PType;
-    RETURN SELF.mType
-END LazyTypeId.type;
-
-PROCEDURE defineTypeId(VAR tId: LazyTypeId; t: PType);
+PROCEDURE defineTypeId*(VAR tId: LazyTypeId; t: PType);
 BEGIN
     tId.mType := t;
 END defineTypeId;
 
+PROCEDURE typeName*(type: NamedType): JsString.Type;
+    RETURN type.name
+END typeName;
+
 PROCEDURE Procedure.idType(): JsString.Type;
     RETURN JsString.make("procedure")
 END Procedure.idType;
@@ -197,15 +243,15 @@ BEGIN
     RETURN JsString.concat(prefix, JsString.make("character string"))
 END String.description;
 
-PROCEDURE stringValue(s: String): JsString.Type;
+PROCEDURE stringValue*(s: String): JsString.Type;
     RETURN s.s
 END stringValue;
 
-PROCEDURE stringLen(s: String): INTEGER;
+PROCEDURE stringLen*(s: String): INTEGER;
     RETURN JsString.len(s.s)
 END stringLen;
 
-PROCEDURE stringAsChar(s: String; VAR c: CHAR): BOOLEAN;
+PROCEDURE stringAsChar*(s: String; VAR c: CHAR): BOOLEAN;
 VAR
     result: BOOLEAN;
 BEGIN
@@ -220,19 +266,19 @@ PROCEDURE Const.idType(): JsString.Type;
     RETURN JsString.make("constant")
 END Const.idType;
 
-PROCEDURE constType(c: Const): PType;
-    RETURN c.mType
+PROCEDURE constType*(c: Const): PType;
+    RETURN c.type
 END constType;
 
-PROCEDURE constValue(c: Const): JS.var;
-    RETURN c.mValue
+PROCEDURE constValue*(c: Const): JS.var;
+    RETURN c.value
 END constValue;
 
 PROCEDURE Variable.idType(): JsString.Type;
 VAR
     result: JsString.Type;
 BEGIN
-    IF SELF.isReadOnly THEN
+    IF SELF.readOnly THEN
         result := JsString.make("read-only variable");
     ELSE
         result := JsString.make("variable");
@@ -240,12 +286,12 @@ BEGIN
     RETURN result
 END Variable.idType;
 
-PROCEDURE variableType(v: Variable): PType;
-    RETURN v.mType
+PROCEDURE variableType*(v: Variable): PType;
+    RETURN v.type
 END variableType;
 
-PROCEDURE isVariableReadOnly(v: Variable): BOOLEAN;
-    RETURN v.isReadOnly
+PROCEDURE isVariableReadOnly*(v: Variable): BOOLEAN;
+    RETURN v.readOnly
 END isVariableReadOnly;
 
 PROCEDURE ExportedVariable.idType(): JsString.Type;
@@ -272,20 +318,20 @@ PROCEDURE Nil.description(): JsString.Type;
     RETURN SELF.idType()
 END Nil.description;
 *)
-PROCEDURE isInt(t: PType): BOOLEAN;
+PROCEDURE isInt*(t: PType): BOOLEAN;
     RETURN (t = basic.integer) OR (t = basic.uint8)
 END isInt;
 
-PROCEDURE intsDescription(): JsString.Type;
+PROCEDURE intsDescription*(): JsString.Type;
     RETURN JsString.make("'INTEGER' or 'BYTE'")
 END intsDescription;
 
-PROCEDURE isString(t: PType): BOOLEAN;
+PROCEDURE isString*(t: PType): BOOLEAN;
     RETURN ((t^ IS Array) & (t^(Array).elementsType = basic.ch))
            OR (t^ IS String)
 END isString;
 
-PROCEDURE moduleName(m: Module): JsString.Type;
+PROCEDURE moduleName*(m: Module): JsString.Type;
     RETURN m.name
 END moduleName;
 
@@ -294,6 +340,7 @@ VAR
     result: PBasicType;
 BEGIN
     NEW(result);
+    result.name := JsString.make(name);
     result.mInitializer := JsString.make(initializer);
     RETURN result
 END makeBasic;
@@ -324,21 +371,21 @@ END Record.initializer;
 
 PROCEDURE Record.addField(f: Field; type: PType);
 BEGIN
-    IF JsMap.has(SELF.fields, f.id) THEN
+    IF JsMap.has(SELF.fields, f.id()) THEN
         Errors.raise(JsString.concat(JsString.concat(
             JsString.make("duplicated field: '"), 
-            f.id), 
+            f.id()), 
             JsString.make("'")));
     END;
-    IF (SELF.base # NIL) & (SELF.base.findSymbol(f.id) # NIL) THEN
+    IF (SELF.base # NIL) & (SELF.base.findSymbol(f.id()) # NIL) THEN
         Errors.raise(JsString.concat(JsString.concat(
             JsString.make("base record already has field: '"),
-            f.id),
+            f.id()),
             JsString.make("'")));
     END;
-    JsMap.put(SELF.fields, f.id, type);
-    IF ~f.exported THEN
-        SELF.notExported.add(f.id);
+    JsMap.put(SELF.fields, f.id(), type);
+    IF ~f.exported() THEN
+        JsArray.stringsAdd(SELF.notExported, f.id());
     END;
 END Record.addField;
 
@@ -352,51 +399,49 @@ BEGIN
     RETURN result
 END Record.findSymbol;
 
-PROCEDURE recordBase(r: Record): PType;
+PROCEDURE recordBase*(r: Record): PType;
     RETURN r.base
 END recordBase;
 
-PROCEDURE setRecordBase(r: Record; type: PRecord);
+PROCEDURE setRecordBase*(r: Record; type: PRecord);
 BEGIN
     r.base := type;
 END setRecordBase;
 
-PROCEDURE recordScope(r: Record): Context.PScope;
+PROCEDURE recordScope*(r: Record): Context.PScope;
     RETURN r.scope
 END recordScope;
 
-PROCEDURE recordConstructor(r: Record): JsString.Type;
+PROCEDURE recordConstructor*(r: Record): JsString.Type;
     RETURN r.cons
 END recordConstructor;
 
-PROCEDURE recordOwnFields(r: Record): JsMap.Type;
+PROCEDURE recordOwnFields*(r: Record): JsMap.Type;
     RETURN r.fields
 END recordOwnFields;
 
-PROCEDURE finalizeRecord(closure: Object.PType);
-VAR
-    record: PRecord;
-    i: INTEGER;
-BEGIN
-    record := closure(PRecord);
-    FOR i := 0 TO record.notExported.len() - 1 DO
-        JsMap.erase(record.fields, record.notExported.at(i))
-    END;
-    record.notExported := NIL;
-END finalizeRecord;
-
 PROCEDURE Pointer.idType(): JsString.Type;
     RETURN JsString.make("pointer")
 END Pointer.idType;
 
+PROCEDURE pointerBase*(p: Pointer): PRecord;
+VAR
+    result: PType;
+BEGIN
+    result := p.base.type();
+    RETURN result(PRecord)
+END pointerBase;
+
 PROCEDURE Pointer.description(): JsString.Type;
 VAR
+    base: PRecord;
     result: JsString.Type;
 BEGIN
     IF SELF.name # NIL THEN
         result := SELF.name;
     ELSE
-        result := JsString.concat(JsString.make("POINTER TO "), SELF.base.description());
+        base := pointerBase(SELF);
+        result := JsString.concat(JsString.make("POINTER TO "), base.description());
     END;
     RETURN result
 END Pointer.description;
@@ -405,24 +450,20 @@ PROCEDURE Pointer.initializer(cx: Context.Type): JsString.Type;
     RETURN JsString.make("null")
 END Pointer.initializer;
 
-PROCEDURE pointerBase(p: Pointer): PRecord;
-    RETURN p.base
-END pointerBase;
-
 PROCEDURE Array.idType(): JsString.Type;
     RETURN JsString.make("array")
 END Array.idType;
 
 PROCEDURE foldArrayDimensions(a: Array; VAR sizes, of: JsString.Type);
 BEGIN  
-    IF (a.len # 0) & (a.elementsType IS PArray) THEN
+    IF (a.len # openArrayLength) & (a.elementsType IS PArray) THEN
         foldArrayDimensions(a.elementsType^(Array), sizes, of);
         sizes := JsString.concat(JsString.concat(
             JsString.fromInt(a.len),
             JsString.make(", ")),
             sizes);
     ELSE
-        IF a.len # 0 THEN
+        IF a.len # openArrayLength THEN
             sizes := JsString.fromInt(a.len);
         END;
         of := a.elementsType.description();
@@ -446,21 +487,21 @@ BEGIN
         result := JsString.concat(JsString.concat(JsString.concat(
             JsString.make("ARRAY"),
             sizes),
-            JsString.make(" OF")),
+            JsString.make(" OF ")),
             of);
     END;
     RETURN result
 END Array.description;
 
 PROCEDURE Array.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.make("null")
+    RETURN SELF.mInitializer
 END Array.initializer;
 
-PROCEDURE arrayElementsType(a: Array): PType;
+PROCEDURE arrayElementsType*(a: Array): PType;
     RETURN a.elementsType
 END arrayElementsType;
 
-PROCEDURE arrayLength(a: Array): INTEGER;
+PROCEDURE arrayLength*(a: Array): INTEGER;
     RETURN a.len
 END arrayLength;
 
@@ -468,7 +509,28 @@ PROCEDURE String.initializer(cx: Context.Type): JsString.Type;
     RETURN JsString.make("null")
 END String.initializer;
 
-PROCEDURE makeLazyTypeId(): PLazyTypeId;
+PROCEDURE Procedure.initializer(cx: Context.Type): JsString.Type;
+    RETURN JsString.make("null")
+END Procedure.initializer;
+
+PROCEDURE Procedure.description(): JsString.Type;
+    RETURN SELF.name
+END Procedure.description;
+
+PROCEDURE Module.idType(): JsString.Type;
+    RETURN JsString.make("module")
+END Module.idType;
+
+PROCEDURE makeTypeId*(type: PType): PTypeId;
+VAR
+    result: PTypeId;
+BEGIN
+    NEW(result);
+    result.mType := type;
+    RETURN result
+END makeTypeId;
+
+PROCEDURE makeLazyTypeId*(): PLazyTypeId;
 VAR
     result: PLazyTypeId;
 BEGIN
@@ -476,7 +538,7 @@ BEGIN
     RETURN result
 END makeLazyTypeId;
 
-PROCEDURE makeString(s: JsString.Type): PString;
+PROCEDURE makeString*(s: JsString.Type): PString;
 VAR
     result: PString;
 BEGIN
@@ -485,15 +547,24 @@ BEGIN
     RETURN result
 END makeString;
 
-PROCEDURE makeArray(): PArray;
+PROCEDURE makeArray*(
+    name: JsString.Type;
+    initializer: JsString.Type;
+    elementsType: PType;
+    len: INTEGER (* see openArrayLength *)
+    ): PArray;
 VAR
     result: PArray;
 BEGIN
     NEW(result);
+    result.name := name;
+    result.mInitializer := initializer;
+    result.elementsType := elementsType;
+    result.len := len;
     RETURN result
 END makeArray;
 
-PROCEDURE makePointer(name: JsString.Type; base: PRecord): PPointer;
+PROCEDURE makePointer*(name: JsString.Type; base: PTypeId): PPointer;
 VAR
     result: PPointer;
 BEGIN
@@ -503,20 +574,65 @@ 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: Context.PScope): PRecord;
 VAR
     result: PRecord;
 BEGIN
     NEW(result);
-    result.name := name;
-    result.fields := JsMap.make();
-    result.cons := cons;
-    result.scope := scope;
-    result.notExported := JsArray.makeStrings();
-    scope.addFinalizer(result, finalizeRecord);
+    initRecord(result, name, cons, scope);
     RETURN result
 END makeRecord;
 
+PROCEDURE makeConst*(type: PType; value: JS.var): PConst;
+VAR
+    result: PConst;
+BEGIN
+    NEW(result);
+    result.type := type;
+    result.value := value;
+    RETURN result
+END makeConst;
+
+PROCEDURE makeVariable*(type: PType; readOnly: BOOLEAN): PVariable;
+VAR
+    result: PVariable;
+BEGIN
+    NEW(result);
+    result.type := type;
+    result.readOnly := readOnly;
+    RETURN result
+END makeVariable;
+
+PROCEDURE makeVariableRef*(type: PType): PVariableRef;
+VAR
+    result: PVariableRef;
+BEGIN
+    NEW(result);
+    result.type := type;
+    result.readOnly := FALSE;
+    RETURN result
+END makeVariableRef;
+
+PROCEDURE makeExportedVariable*(v: Variable): PExportedVariable;
+VAR
+    result: PExportedVariable;
+BEGIN
+    NEW(result);
+    result.type := v.type;
+    result.readOnly := TRUE;
+    RETURN result
+END makeExportedVariable;
+
+PROCEDURE initProcedure*(p: Procedure; name: JsString.Type);
+BEGIN
+    p.name := name;
+END initProcedure;
+
+PROCEDURE initModule*(m: Module; name: JsString.Type);
+BEGIN
+    m.name := name;
+END initModule;
+
 BEGIN
     NEW(basic);
     basic.bool := makeBasic("BOOLEAN", "false");
@@ -527,9 +643,9 @@ BEGIN
     basic.set := makeBasic("SET", "0");
 
     numeric := JsArray.make();
-    numeric.add(basic.integer);
-    numeric.add(basic.uint8);
-    numeric.add(basic.real);
+    JsArray.add(numeric, basic.integer);
+    JsArray.add(numeric, basic.uint8);
+    JsArray.add(numeric, basic.real);
 
     NEW(nil);
 END Types.

+ 2 - 2
src/oberon/oberon_context.js

@@ -1,11 +1,11 @@
 "use strict";
 
-var Type = require("type.js");
+var Type = require("js/Types.js");
 var Context = require("context.js");
 
 var RecordDecl = Context.RecordDecl.extend({
     init: function OberonContext$RecordDecl(context){
-        Context.RecordDecl.prototype.init.call(this, context, Type.Record);
+        Context.RecordDecl.prototype.init.call(this, context, Type.makeRecord);
     }
 });
 

+ 30 - 5
src/oc_nodejs.js

@@ -3,16 +3,41 @@
 var grammar = require("eberon/eberon_grammar.js").grammar;
 var nodejs = require("nodejs.js");
 
+var options = {
+    "--out-dir": "outDir",
+    "--import-dir": "importDir"
+};
+
+function parseOption(a, result){
+    for(var o in options){
+        var optionPrefix = o + "=";
+        if (a.indexOf(optionPrefix) === 0){
+            result[options[o]] = a.substr(optionPrefix.length);
+            return;
+        }
+    }
+    result.notParsed.push(a);
+}
+
+function parseOptions(argv){
+    var result = {notParsed: []};
+    for(var i = 0; i < argv.length; ++i)
+        parseOption(argv[i], result);
+    return result;
+}
+
 function main(){
-    if (process.argv.length <= 3){
-        console.info("Usage: <oc_nodejs> <output dir> <input oberon module file(s)>");
+    var args = parseOptions(process.argv.splice(2));
+    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>");
         return -1;
     }
+    var outDir = args.outDir || ".";
 
-    var outDir = process.argv[2];
-    var sources = process.argv.slice(3);
     var errors = "";
-    nodejs.compile(sources, grammar, function(e){errors += e;}, outDir);
+    nodejs.compile(sources, grammar, function(e){errors += e;}, outDir, args.importDir);
     if (errors.length){
         console.error(errors);
         return -2;

+ 16 - 14
src/operator.js

@@ -3,7 +3,9 @@
 var Cast = require("cast.js");
 var Code = require("code.js");
 var Errors = require("js/Errors.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
+
+var basicTypes = Type.basic();
 
 var precedence = {
     unary: 4,
@@ -53,7 +55,7 @@ function makeUnary(op, code){
 var mul = makeBinary(function(x, y){return x * y;}, " * ", precedence.mulDivMod);
 var div = makeBinary(function(x, y){return x / y;}, " / ", precedence.mulDivMod);
 
-var openArrayChar = new Type.Array(undefined, undefined, Type.basic.ch);
+var openArrayChar = Type.makeArray(undefined, undefined, basicTypes.ch, 0);
 
 function castToStr(e, context){
     var type = e.type();
@@ -67,24 +69,24 @@ function makeStrCmp(op){
             context.rtl().strCmp(castToStr(left, context),
                                  castToStr(right, context))
                 + op + "0",
-            Type.basic.bool
+            basicTypes.bool
         );
     };
 }
 
 function pow2(e){
     return new Code.Expression("Math.pow(2, " + e.deref().code() + ")",
-                               Type.basic.real);
+                               basicTypes.real);
 }
 
 function log2(e){
     return new Code.Expression("(Math.log(" + e.deref().code() + ") / Math.LN2) | 0",
-                               Type.basic.integer, undefined, undefined, precedence.bitOr);
+                               basicTypes.integer, undefined, undefined, precedence.bitOr);
 }
 
 function assign(left, right, context){
     var info = left.designator().info();
-    if (!(info instanceof Type.Variable) || info.isReadOnly())
+    if (!(info instanceof Type.Variable) || Type.isVariableReadOnly(info))
         throw new Errors.Error("cannot assign to " + info.idType());
 
     var leftCode = left.lval();
@@ -94,14 +96,14 @@ function assign(left, right, context){
 
     var isArray = leftType instanceof Type.Array;
     if (isArray
-        && leftType.elementsType() == Type.basic.ch
+        && Type.arrayElementsType(leftType) == basicTypes.ch
         && rightType instanceof Type.String){
-        if (leftType.length() === undefined)
+        if (Type.arrayLength(leftType) == Type.openArrayLength)
             throw new Errors.Error("string cannot be assigned to open " + leftType.description());
-        if (rightType.length() > leftType.length())
+        if (Type.stringLen(rightType) > Type.arrayLength(leftType))
             throw new Errors.Error(
-                leftType.length() + "-character ARRAY is too small for "
-                + rightType.length() + "-character string");
+                Type.arrayLength(leftType) + "-character ARRAY is too small for "
+                + Type.stringLen(rightType) + "-character string");
         return context.rtl().assignArrayFromString(leftCode, rightCode);
     }
 
@@ -112,7 +114,7 @@ function assign(left, right, context){
                              + "' and cannot be assigned to '" + rightType.description() + "' expression");
 
     if (isArray && rightType instanceof Type.Array){
-        if (leftType.length() === undefined)
+        if (Type.arrayLength(leftType) == Type.openArrayLength)
             throw new Errors.Error("'" + leftCode
                                  + "' is open '" + leftType.description()
                                  + "' and cannot be assigned");
@@ -140,10 +142,10 @@ function makeInplace(code, altOp){
 function promoteToWideIfNeeded(op){
     return function(){
         var result = op.apply(this, arguments);
-        if (result.type() == Type.basic.uint8)
+        if (result.type() == basicTypes.uint8)
             result = new Code.Expression(
                 result.code(),
-                Type.basic.integer,
+                basicTypes.integer,
                 result.designator(),
                 result.constValue(),
                 result.maxPrecedence());

+ 51 - 47
src/procedure.js

@@ -7,7 +7,9 @@ var Errors = require("js/Errors.js");
 var op = require("operator.js");
 var precedence = require("operator.js").precedence;
 var Symbol = require("symbol.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
+
+var basicTypes = Type.basic();
 
 var Arg = Class.extend({
     init: function(type, isVar){
@@ -101,7 +103,7 @@ var ProcCallGenerator = Class.extend({
             var info = designator.info();
             if (info instanceof Type.Const)
                 throw new Errors.Error("constant cannot be used as VAR parameter");
-            if (info.isReadOnly())
+            if (Type.isVariableReadOnly(info))
                 throw new Errors.Error(info.idType() + " cannot be used as VAR parameter");
         }
         return new CheckArgumentResult(arg.type, arg.isVar, castOperation);
@@ -112,7 +114,8 @@ var ProcCallGenerator = Class.extend({
 
 var DefinedProc = Type.Procedure.extend({
     init: function DefinedProc(name){
-        Type.Procedure.prototype.init.call(this, name);
+        Type.Procedure.prototype.init.call(this);
+        Type.initProcedure(this, name);
         this.__arguments = undefined;
         this.__result = undefined;
     },
@@ -123,11 +126,11 @@ var DefinedProc = Type.Procedure.extend({
     args: function(){return this.__arguments;},
     result: function(){return this.__result;},
     description: function(){
-        var name = this.name();
+        var name = Type.typeName(this);
         if (name)
             return name;
         return 'PROCEDURE' + this.__dumpProcArgs()
-            + (this.__result ? ": " + this.__result.name() : "");
+            + (this.__result ? ": " + Type.typeName(this.__result) : "");
         },
     callGenerator: function(context, id){
         return new ProcCallGenerator(context, id, this);
@@ -149,12 +152,13 @@ var DefinedProc = Type.Procedure.extend({
 
 var Std = Type.Procedure.extend({
     init: function Std(name, args, result, callGeneratorFactory){
-        Type.Procedure.prototype.init.call(this, name);
+        Type.Procedure.prototype.init.call(this);
+        Type.initProcedure(this, name);
         this.__arguments = args;
         this.__result = result;
         this.__callGeneratorFactory = callGeneratorFactory;
     },
-    description: function(){return "standard procedure " + this.name();},
+    description: function(){return "standard procedure " + Type.typeName(this);},
     args: function(){return this.__arguments;},
     result: function(){return this.__result;},
     callGenerator: function(context, id){
@@ -189,8 +193,8 @@ var TwoArgToOperatorProcCallGenerator = ExpCallGenerator.extend({
 });
 
 function setBitImpl(name, bitOp){
-    var args = [new Arg(Type.basic.set, true),
-                new Arg(Type.basic.integer, false)];
+    var args = [new Arg(basicTypes.set, true),
+                new Arg(basicTypes.integer, false)];
     function operator(x, y){
         var value = y.constValue();
         var valueCode;
@@ -218,8 +222,8 @@ function setBitImpl(name, bitOp){
 }
 
 function incImpl(name, unary, op){
-    var args = [new Arg(Type.basic.integer, true),
-                new Arg(Type.basic.integer, false)];
+    var args = [new Arg(basicTypes.integer, true),
+                new Arg(basicTypes.integer, false)];
     function operator(x, y){
         if (!y)
             return unary + x.code();
@@ -264,13 +268,13 @@ function bitShiftImpl(name, op){
             return op(args[0], args[1]);
         }
     });
-    var args = [new Arg(Type.basic.integer, false),
-                new Arg(Type.basic.integer, false)
+    var args = [new Arg(basicTypes.integer, false),
+                new Arg(basicTypes.integer, false)
                ];
     var proc = new Std(
         name,
         args,
-        Type.basic.integer,
+        basicTypes.integer,
         function(context, id, type){
             return new CallGenerator(context, id, type);
         });
@@ -299,8 +303,8 @@ exports.predefined = [
                 var type = e.type();
                 if (!(type instanceof Type.Pointer))
                     throw new Errors.Error("POINTER variable expected, got '"
-                                         + type.name() + "'");
-                this.__baseType = type.baseType();
+                                         + Type.typeName(type) + "'");
+                this.__baseType = Type.pointerBase(type);
                 if (this.__baseType instanceof Type.NonExportedRecord)
                     throw new Errors.Error("non-exported RECORD type cannot be used in NEW");
                 return new CheckArgumentResult(type, false);
@@ -340,11 +344,11 @@ exports.predefined = [
         });
 
         var name = "LEN";
-        var args = [new Arg(new Type.Array("ARRAY OF any type"), false)];
+        var args = [new Arg(Type.makeArray("ARRAY OF any type", undefined, undefined, 0), false)];
         var type = new Std(
             name,
             args,
-            Type.basic.integer,
+            basicTypes.integer,
             function(context, id, type){
                 return new LenProcCallGenerator(context, id, type);
             });
@@ -359,15 +363,15 @@ exports.predefined = [
             callExpression: function(){
                 var e = this.args()[0];
                 var code = Code.adjustPrecedence(e, precedence.bitAnd);
-                return new Code.Expression(code + " & 1", Type.basic.bool, undefined, e.constValue(), precedence.bitAnd);
+                return new Code.Expression(code + " & 1", basicTypes.bool, undefined, e.constValue(), precedence.bitAnd);
             }
         });
         var name = "ODD";
-        var args = [new Arg(Type.basic.integer, false)];
+        var args = [new Arg(basicTypes.integer, false)];
         var type = new Std(
             "ODD",
             args,
-            Type.basic.bool,
+            basicTypes.bool,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
@@ -382,7 +386,7 @@ exports.predefined = [
             prolog: function(){return this.context().rtl().assertId() + "(";}
         });
 
-        var args = [new Arg(Type.basic.bool)];
+        var args = [new Arg(basicTypes.bool)];
         var proc = new Std(
             "ASSERT",
             args,
@@ -406,7 +410,7 @@ exports.predefined = [
             prolog: function(){return "Math.abs(";},
             checkArgument: function(pos, e){
                 var type = e.type();
-                if (Type.numeric.indexOf(type) == -1)
+                if (Type.numeric().indexOf(type) == -1)
                     throw new Errors.Error("type mismatch: expected numeric type, got '" +
                                            type.description() + "'");
                 this.__argType = type;
@@ -432,11 +436,11 @@ exports.predefined = [
             },
             prolog: function(){return "Math.floor(";}
         });
-        var args = [new Arg(Type.basic.real, false)];
+        var args = [new Arg(basicTypes.real, false)];
         var proc = new Std(
             "FLOOR",
             args,
-            Type.basic.integer,
+            basicTypes.integer,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
@@ -450,14 +454,14 @@ exports.predefined = [
             },
             callExpression: function(){
                 var e = this.args()[0];
-                return new Code.Expression(e.code(), Type.basic.real, undefined, e.constValue(), e.maxPrecedence());
+                return new Code.Expression(e.code(), basicTypes.real, undefined, e.constValue(), e.maxPrecedence());
             }
         });
-        var args = [new Arg(Type.basic.integer, false)];
+        var args = [new Arg(basicTypes.integer, false)];
         var proc = new Std(
             "FLT",
             args,
-            Type.basic.real,
+            basicTypes.real,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
@@ -477,39 +481,39 @@ exports.predefined = [
             epilog: function(){return "";},
             checkArgument: function(pos, e){
                 var type = e.type();
-                if (type == Type.basic.ch || type == Type.basic.set)
+                if (type == basicTypes.ch || type == basicTypes.set)
                     this.__callExpression = new Code.Expression(
-                        e.code(), Type.basic.integer, undefined, e.constValue());
-                else if (type == Type.basic.bool){
+                        e.code(), basicTypes.integer, undefined, e.constValue());
+                else if (type == basicTypes.bool){
                     var code = Code.adjustPrecedence(e, precedence.conditional) + " ? 1 : 0";
                     var value = e.constValue();
                     if (value !== undefined)
                         value = value ? 1 : 0;
                     this.__callExpression = new Code.Expression(
-                        code, Type.basic.integer, undefined, value, precedence.conditional);
+                        code, basicTypes.integer, undefined, value, precedence.conditional);
                 }
                 else if (type instanceof Type.String){
-                    var ch = type.asChar();
-                    if (ch !== undefined)
+                    var ch;
+                    if (Type.stringAsChar(type, {set: function(v){ch = v;}}))
                         this.__callExpression = new Code.Expression(
-                            "" + ch, Type.basic.integer);
+                            "" + ch, basicTypes.integer);
                 }
                 
                 if (this.__callExpression)
                     return new CheckArgumentResult(type, false);
 
-                // should throw error
-                return ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+                throw new Errors.Error(
+                      "ORD function expects CHAR or BOOLEAN or SET as an argument, got '" + type.description()+ "'");
             },
             callExpression: function(){return this.__callExpression;}
         });
         var name = "ORD";
-        var argType = new Type.Basic("CHAR or BOOLEAN or SET");
-        var args = [new Arg(argType, false)];
+        //var argType = new basicTypes("CHAR or BOOLEAN or SET");
+        var args = [new Arg(undefined, false)];
         var type = new Std(
             name,
             args,
-            Type.basic.integer,
+            basicTypes.integer,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
@@ -522,14 +526,14 @@ exports.predefined = [
                 ExpCallGenerator.prototype.init.call(this, context, id, type);
             },
             callExpression: function(){
-                return new Code.Expression(this.args()[0].code(), Type.basic.ch);
+                return new Code.Expression(this.args()[0].code(), basicTypes.ch);
             }
         });
         var name = "CHR";
         var type = new Std(
             name,
-            [new Arg(Type.basic.integer, false)],
-            Type.basic.ch,
+            [new Arg(basicTypes.integer, false)],
+            basicTypes.ch,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
@@ -537,8 +541,8 @@ exports.predefined = [
         return symbol;
     }(),
     function(){
-        var args = [new Arg(Type.basic.real, true),
-                    new Arg(Type.basic.integer, false)];
+        var args = [new Arg(basicTypes.real, true),
+                    new Arg(basicTypes.integer, false)];
         function operator(x, y){
             return op.mulInplace(x, op.pow2(y));
         }
@@ -555,8 +559,8 @@ exports.predefined = [
         return symbol;
     }(),
     function(){
-        var args = [new Arg(Type.basic.real, true),
-                    new Arg(Type.basic.integer, true)];
+        var args = [new Arg(basicTypes.real, true),
+                    new Arg(basicTypes.integer, true)];
         function operator(x, y){
             return op.assign(y, op.log2(x)) +
                    "; " +

+ 10 - 7
src/scope.js

@@ -4,13 +4,15 @@ var Class = require("rtl.js").Class;
 var Errors = require("js/Errors.js");
 var Procedure = require("procedure.js");
 var Symbol = require("symbol.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
 var stdSymbols = function(){
     var symbols = {};
-    for(var t in Type.basic){
-        var type = Type.basic[t];
-        symbols[type.name()] = new Symbol.Symbol(type.name(), new Type.TypeId(type));
+    var basicTypes = Type.basic();
+    for(var t in basicTypes){
+        var type = basicTypes[t];
+        var name = Type.typeName(type);
+        symbols[name] = new Symbol.Symbol(name, Type.makeTypeId(type));
     }
     
     var predefined = Procedure.predefined;
@@ -37,7 +39,7 @@ var Scope = Class.extend({
             throw new Errors.Error( "'" + id + "' already declared");
         this.__symbols[id] = symbol;
     },
-    addFinalizer: function(f){this.__finalizers.push(f);},
+    addFinalizer: function(f, closure){this.__finalizers.push(f.bind(undefined, closure));},
     resolve: function(symbol){
         var id = symbol.id();
         var i = this.__unresolved.indexOf(id);
@@ -79,7 +81,8 @@ var ProcedureScope = Scope.extend({
 
 var CompiledModule = Type.Module.extend({
     init: function Scope$CompiledModule(id){
-        Type.Module.prototype.init.call(this, id);
+        Type.Module.prototype.init.call(this);
+        Type.initModule(this, id);
         this.__exports = {};
     },
     defineExports: function(exports){
@@ -88,7 +91,7 @@ var CompiledModule = Type.Module.extend({
             if (symbol.isVariable())
                 symbol = new Symbol.Symbol(
                     id,
-                    new Type.ExportedVariable(symbol.info()));
+                    Type.makeExportedVariable(symbol.info()));
             this.__exports[id] = symbol;
         }
     },  

+ 1 - 1
src/symbol.js

@@ -2,7 +2,7 @@
 
 var Class = require("rtl.js").Class;
 var Errors = require("js/Errors.js");
-var Type = require("type.js");
+var Type = require("js/Types.js");
 
 var Symbol = Class.extend({
 	init: function Symbol(id, info){

+ 0 - 255
src/type.js

@@ -1,255 +0,0 @@
-"use strict";
-
-var Class = require("rtl.js").Class;
-var Errors = require("js/Errors.js");
-
-var Id = Class.extend({
-    init: function Id(){}
-});
-
-var Type = Id.extend({
-    init: function Type(){
-        Id.prototype.init.call(this);
-    }
-});
-
-var TypeId = Id.extend({
-    init: function TypeId(type){
-        Id.prototype.init.call(this);
-        this._type = type;
-    },
-    type: function(){return this._type;},
-    description: function(){return 'type ' + this._type.description();},
-    strip: function(){
-        this._type = this._type instanceof Record 
-                   ? new NonExportedRecord(this._type.cons(), this._type.scope(), this._type.baseType())
-                   : undefined;
-    }
-});
-
-var ForwardTypeId = TypeId.extend({
-    init: function Type$ForwardTypeId(resolve){
-        TypeId.prototype.init.call(this);
-        this.__resolve = resolve;
-    },
-    type: function(){
-        if (!this._type)
-            this._type = this.__resolve();
-        return this._type;
-    }
-});
-
-var LazyTypeId = TypeId.extend({
-    init: function LazyTypeId(){
-        TypeId.prototype.init.call(this);
-    },
-    define: function(type){return this._type = type;}
-});
-
-exports.String = Type.extend({
-    init: function TypeString(s){
-        Type.prototype.init.call(this);
-        this.__s = s;
-    },
-    idType: function(){return "string";},
-    description: function(){return (this.__s.length == 1 ? "single-" : "multi-") + "character string";},
-    value: function(){return this.__s;},
-    asChar: function(){return this.__s.length == 1 ? this.__s.charCodeAt(0) : undefined;},
-    length: function(){return this.__s.length;}
-});
-
-var BasicType = Type.extend({
-    init: function BasicType(name, initValue){
-        Type.prototype.init.call(this);
-        this.__name = name;
-        this.__initValue = initValue;
-    },
-    idType: function(){return "type";},
-    name: function() {return this.__name;},
-    description: function(){return this.name();},
-    initializer: function(context){return this.__initValue;}
-});
-
-exports.Basic = BasicType;
-
-function foldArrayDimensions(a){
-    var result = a.length();
-    var next = a.elementsType();
-    if (result !== undefined
-        && next instanceof ArrayType){
-        var r = foldArrayDimensions(next);
-        return [result + ", " + r[0], r[1]];
-    }
-    return [result, next.description()];
-}
-
-var ArrayType = BasicType.extend({
-    init: function ArrayType(name, initializer, elementsType, size){
-        BasicType.prototype.init.call(this, name, initializer);
-        this.__elementsType = elementsType;
-        this.__size = size;
-    },
-    elementsType: function(){return this.__elementsType;},
-    length: function(){return this.__size;},
-    description: function(){
-        if (this.__elementsType === undefined) // special arrays, see procedure "LEN"
-            return this.name();
-        var desc = foldArrayDimensions(this);
-        var sizes = (desc[0] === undefined ? "" : " " + desc[0]);
-        return "ARRAY" + sizes + " OF " + desc[1];
-    }
-});
-
-exports.Pointer = BasicType.extend({
-    init: function PointerType(name, base){
-        BasicType.prototype.init.call(this, name, "null");
-        this.__base = base;
-    },
-    description: function(){
-        return this.name() || "POINTER TO " + this.baseType().description();
-    },
-    baseType: function(){return this.__base.type();}
-});
-
-var Record = BasicType.extend({
-    init: function Type$Record(name, cons, scope){
-        BasicType.prototype.init.call(this, name);
-        this.__cons = cons;        
-        this.__scope = scope;
-        this.__fields = {};
-        this.__notExported = [];
-        this.__base = undefined;
-        scope.addFinalizer(this.finalize.bind(this));
-    },
-    initializer: function(context){
-        return "new " + context.qualifyScope(this.__scope) + this.__cons + "()";
-    },
-    scope: function(){return this.__scope;},
-    cons: function(){return this.__cons;},
-    addField: function(field, type){
-        var name = field.id();
-        if (this.__fields.hasOwnProperty(name))
-            throw new Errors.Error("duplicated field: '" + name + "'");
-        if (this.__base && this.__base.findSymbol(name))
-            throw new Errors.Error("base record already has field: '" + name + "'");
-        this.__fields[name] = type;
-        if (!field.exported())
-            this.__notExported.push(name);
-    },
-    ownFields: function() {return this.__fields;},
-    findSymbol: function(field){
-        var result = this.__fields[field];
-        if ( !result && this.__base)
-            result = this.__base.findSymbol(field);
-        return result;
-    },
-    baseType: function() {return this.__base;},
-    setBaseType: function(type) {this.__base = type;},
-    description: function(){
-        return this.name() || "anonymous RECORD";
-    },
-    finalize: function(){
-        for(var i = 0; i < this.__notExported.length; ++i)
-            delete this.__fields[this.__notExported[i]];
-        delete this.__notExported;
-    }
-});
-
-var NonExportedRecord = Record.extend({
-    init: function Scope$NonExportedRecord(cons, scope, base){
-        Record.prototype.init.call(this, undefined, cons, scope);
-        this.setBaseType(base);
-    }
-});
-
-var NilType = Type.extend({
-    init: function NilType(){Type.prototype.init.call(this);},
-    idType: function(){return "NIL";},
-    description: function(){return "NIL";}
-});
-
-var basic = {
-    bool: new BasicType("BOOLEAN", false),
-    ch: new BasicType("CHAR", 0),
-    integer: new BasicType("INTEGER", 0),
-    uint8: new BasicType("BYTE", 0),
-    real: new BasicType("REAL", 0),
-    set: new BasicType("SET", 0)
-};
-exports.basic = basic;
-exports.numeric = [basic.integer, basic.uint8, basic.real];
-
-exports.isInt = function(type){
-    return type == basic.integer || type == basic.uint8;
-};
-
-exports.intsDescription = function(){return "'INTEGER' or 'BYTE'";};
-
-exports.isString = function(type){
-    return (type instanceof ArrayType && type.elementsType() == basic.ch)
-            || type instanceof exports.String;
-};
-
-exports.nil = new NilType();
-
-exports.Const = Id.extend({
-    init: function Const(type, value){
-        Id.prototype.init.call(this);
-        this.__type = type;
-        this.__value = value;
-    },
-    idType: function(){return "constant";},
-    type: function(){return this.__type;},
-    value: function(){return this.__value;}
-});
-
-var Variable = Id.extend({
-    init: function Variable(type, isReadOnly){
-        Id.prototype.init.call(this);
-        this.__type = type;
-        this.__isReadOnly = isReadOnly;
-    },
-    idType: function(){return this.__isReadOnly ? "read-only variable" : "variable";},
-    type: function(){return this.__type;},
-    isReadOnly: function(){return this.__isReadOnly;}
-});
-
-var VariableRef = Variable.extend({
-    init: function Type$VariableRef(type){
-        Variable.prototype.init.call(this, type, false);
-    }
-});
-
-var ExportedVariable = Variable.extend({
-    init: function ExportedVariable(variable){
-        Variable.prototype.init.call(this, variable.type(), true);
-    },
-    idType: function(){return "imported variable";}
-});
-
-exports.Procedure = BasicType.extend({
-    init: function Procedure(name){
-        BasicType.prototype.init.call(this, name, "null");
-    },
-    idType: function(){return "procedure";}
-});
-
-var Module = Id.extend({
-    init: function Type$Module(name){
-        Id.prototype.init.call(this);
-        this.__name = name;
-    },
-    name: function(){return this.__name;}
-});
-
-exports.Array = ArrayType;
-exports.Variable = Variable;
-exports.VariableRef = VariableRef;
-exports.ExportedVariable = ExportedVariable;
-exports.Module = Module;
-exports.NonExportedRecord = NonExportedRecord;
-exports.Record = Record;
-exports.Type = Type;
-exports.TypeId = TypeId;
-exports.ForwardTypeId = ForwardTypeId;
-exports.LazyTypeId = LazyTypeId;

+ 10 - 7
test/test_unit.js

@@ -240,9 +240,9 @@ return {
          ["i(Derived)",
           "invalid type cast: 'Derived' is not an extension of 'INTEGER'"],
          ["vb(Derived)",
-          "invalid type cast: a value variable and cannot be used in typeguard"],
+          "invalid type cast: a value variable cannot be used in typeguard"],
          ["vb(PDerived)",
-          "invalid type cast: a value variable and cannot be used in typeguard"])
+          "invalid type cast: a value variable cannot be used in typeguard"])
     ),
 "POINTER relations": testWithContext(
     context(grammar.expression,
@@ -400,8 +400,8 @@ return {
          "i := ORD({1})",
          "i := ORD(\"a\")",
          "b := ORD(22X) = 022H"),
-    fail(["i := ORD(1.2)", "type mismatch for argument 1: 'REAL' cannot be converted to 'CHAR or BOOLEAN or SET'"],
-         ["i := ORD(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'CHAR or BOOLEAN or SET'"]
+    fail(["i := ORD(1.2)", "ORD function expects CHAR or BOOLEAN or SET as an argument, got 'REAL'"],
+         ["i := ORD(\"abc\")", "ORD function expects CHAR or BOOLEAN or SET as an argument, got 'multi-character string'"]
          )
 ),
 "CHR": testWithContext(
@@ -540,6 +540,8 @@ return {
          "CASE i1 OF | END",
          "CASE i1 OF | 0: b1 := TRUE END",
          "CASE i1 OF 0: b1 := TRUE END",
+         "CASE cc OF \"A\": b1 := TRUE END",
+         "CASE \"A\" OF \"A\": b1 := TRUE END",
          "CASE c1 OF \"A\": b1 := TRUE END",
          "CASE byte OF 3: b1 := TRUE END",
          "CASE i1 OF 0: b1 := TRUE | 1: b1 := FALSE END",
@@ -555,6 +557,7 @@ return {
          ["CASE i1 OF i2: b1 := TRUE END",
           "'i2' is not a constant"],
          ["CASE b1 OF END", "'INTEGER' or 'BYTE' or 'CHAR' expected as CASE expression"],
+         ["CASE \"AA\" OF \"A\": b1 := TRUE END", "'INTEGER' or 'BYTE' or 'CHAR' expected as CASE expression"],
          ["CASE i1 OF \"A\": b1 := TRUE END",
           "label must be 'INTEGER' (the same as case expression), got 'CHAR'"],
          ["CASE i1 OF p: b1 := TRUE END",
@@ -1009,11 +1012,11 @@ return {
             + "T = RECORD END; TD = RECORD(T) b: Base END;"),
     pass("PROCEDURE proc(VAR p: Base); BEGIN p(Derived).i := 1; END proc"),
     fail(["PROCEDURE proc(p: Base); BEGIN p(Derived).i := 1; END proc",
-          "invalid type cast: a value variable and cannot be used in typeguard"],
+          "invalid type cast: a value variable cannot be used in typeguard"],
          ["PROCEDURE proc(p: TD); BEGIN p.b(Derived).i := 1; END proc",
-          "invalid type cast: a value variable and cannot be used in typeguard"],
+          "invalid type cast: a value variable cannot be used in typeguard"],
          ["PROCEDURE proc(VAR p: T); BEGIN p(TD).b(Derived).i := 1; END proc",
-          "invalid type cast: a value variable and cannot be used in typeguard"])
+          "invalid type cast: a value variable cannot be used in typeguard"])
     ),
 "NEW for read only array element fails": testWithContext(
     context(grammar.procedureDeclaration,