瀏覽代碼

Get rid of JsString in favor of STRING

Vladislav Folts 11 年之前
父節點
當前提交
2a959f63db

二進制
bin/compiled.zip


+ 14 - 11
src/context.js

@@ -491,7 +491,10 @@ exports.FormalParameters = ChainedContext.extend({
         this.__result = undefined;
 
         var parent = this.parent();
-        this.__type = new Procedure.make(parent.typeName());
+        var name = parent.typeName();
+        if (name === undefined)
+            name = "";
+        this.__type = new Procedure.make(name);
         parent.setType(this.__type);
     },
     handleMessage: function(msg){
@@ -551,7 +554,7 @@ exports.ProcDecl = ChainedContext.extend({
         if (this.__id.id() != id)
             throw new Errors.Error("mismatched procedure names: '" + this.__id.id()
                                  + "' at the begining and '" + id + "' at the end");
-        this.codeGenerator().closeScope();
+        this.codeGenerator().closeScope("");
         this.parent().popScope();
     },
     _prolog: function(){return "\nfunction " + this.__id.id() + "(";},
@@ -663,7 +666,7 @@ exports.PointerDecl = ChainedContext.extend({
 
         var parent = this.parent();
         var name = parent.isAnonymousDeclaration() 
-            ? undefined
+            ? ""
             : parent.genTypeName();
         var pointerType = Type.makePointer(name, typeId);
         parent.setType(pointerType);
@@ -1269,7 +1272,7 @@ exports.ElseIf = IfContextBase.extend({
     init: function ElseIfContext(context){
         ChainedContext.prototype.init.call(this, context);
         var gen = this.codeGenerator();
-        gen.closeScope();
+        gen.closeScope("");
         gen.write("else if (");
     }
 });
@@ -1278,7 +1281,7 @@ exports.Else = ChainedContext.extend({
     init: function ElseContext(context){
         ChainedContext.prototype.init.call(this, context);
         var gen = this.codeGenerator();
-        gen.closeScope();
+        gen.closeScope("");
         gen.write("else ");
         gen.openScope();
     }
@@ -1289,7 +1292,7 @@ exports.emitEndStatement = function(context){
 };
 
 exports.emitIfEnd = function(context){
-    context.codeGenerator().closeScope();
+    context.codeGenerator().closeScope("");
 };
 
 exports.Case = ChainedContext.extend({
@@ -1364,7 +1367,7 @@ exports.CaseLabel = ChainedContext.extend({
     },
     handleLabelType: function(type){this.parent().handleLabelType(type);},
     handleRange: function(from, to){this.parent().handleRange(from, to);},
-    endParse: function(){this.codeGenerator().closeScope();}
+    endParse: function(){this.codeGenerator().closeScope("");}
 });
 
 exports.CaseRange = ChainedContext.extend({
@@ -1423,7 +1426,7 @@ exports.While = ChainedContext.extend({
 exports.emitWhileEnd = function(context){
     var gen = context.codeGenerator();
     gen.closeScope(" else break;\n");
-    gen.closeScope();
+    gen.closeScope("");
 };
 
 exports.Repeat = ChainedContext.extend({
@@ -1510,7 +1513,7 @@ exports.For = ChainedContext.extend({
         gen.write(s);
         gen.openScope();
     },
-    endParse: function(){this.codeGenerator().closeScope();}
+    endParse: function(){this.codeGenerator().closeScope("");}
 });
 
 exports.emitForBegin = function(context){context.handleBegin();};
@@ -1745,7 +1748,7 @@ exports.RecordDecl = ChainedContext.extend({
         ChainedContext.prototype.init.call(this, context);
         var parent = this.parent();
         var cons = parent.genTypeName();
-        var name = parent.isAnonymousDeclaration() ? undefined : cons;
+        var name = parent.isAnonymousDeclaration() ? "" : cons;
         this.__type = makeRecord(name, cons, context.currentScope());
         parent.setType(this.__type);
         parent.codeGenerator().write("var " + cons + " = ");
@@ -1787,7 +1790,7 @@ exports.RecordDecl = ChainedContext.extend({
         for(var f in ownFields)
             gen.write("this." + f + " = " + ownFields[f].initializer(this) + ";\n");
 
-        gen.closeScope();
+        gen.closeScope("");
         gen.closeScope(");\n");
     }
 });

+ 2 - 1
src/eberon/EberonCast.ob

@@ -5,7 +5,8 @@ PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Cast.Operations;
 VAR
     result: INTEGER;
 BEGIN
-    IF ((from = EberonString.string) & (to IS Types.PArray) & (Types.arrayLength(to(Types.PArray)^) = Types.openArrayLength))
+    IF ((from = EberonString.string) 
+            & ((to IS Types.PString) OR (to IS Types.PArray) & (Types.arrayLength(to(Types.PArray)^) = Types.openArrayLength)))
         OR (Types.isString(from) & (to = EberonString.string)) THEN
         IF toVar THEN 
             result := Cast.errVarParameter;

+ 11 - 7
src/eberon/EberonOperator.ob

@@ -1,9 +1,9 @@
 MODULE EberonOperator;
-IMPORT Code, CodePrecedence, JsString, OberonRtl, Operator;
+IMPORT Code, CodePrecedence, OberonRtl, Operator;
 
 PROCEDURE opAddStr(left, right: Code.PConst): Code.PConst;
-    RETURN Code.makeStringConst(JsString.concat(left^(Code.StringConst).value, 
-                                                right^(Code.StringConst).value))
+    RETURN Code.makeStringConst(left^(Code.StringConst).value
+                              + right^(Code.StringConst).value)
 END opAddStr;
 
 PROCEDURE opEqualStr(left, right: Code.PConst): Code.PConst;
@@ -17,19 +17,23 @@ PROCEDURE opNotEqualStr(left, right: Code.PConst): Code.PConst;
 END opNotEqualStr;
 
 PROCEDURE opLessStr(left, right: Code.PConst): Code.PConst;
-    RETURN Code.makeIntConst(0) (*to fix*)
+    RETURN Code.makeIntConst(ORD(left^(Code.StringConst).value
+                               < right^(Code.StringConst).value))
 END opLessStr;
 
 PROCEDURE opGreaterStr(left, right: Code.PConst): Code.PConst;
-    RETURN Code.makeIntConst(0) (*to fix*)
+    RETURN Code.makeIntConst(ORD(left^(Code.StringConst).value
+                               > right^(Code.StringConst).value))
 END opGreaterStr;
 
 PROCEDURE opLessEqualStr(left, right: Code.PConst): Code.PConst;
-    RETURN Code.makeIntConst(0) (*to fix*)
+    RETURN Code.makeIntConst(ORD(left^(Code.StringConst).value
+                              <= right^(Code.StringConst).value))
 END opLessEqualStr;
 
 PROCEDURE opGraterEqualStr(left, right: Code.PConst): Code.PConst;
-    RETURN Code.makeIntConst(0) (*to fix*)
+    RETURN Code.makeIntConst(ORD(left^(Code.StringConst).value
+                              >= right^(Code.StringConst).value))
 END opGraterEqualStr;
 
 PROCEDURE addStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;

+ 3 - 3
src/eberon/EberonString.ob

@@ -1,13 +1,13 @@
 MODULE EberonString;
-IMPORT JsString, Types;
+IMPORT Types;
 TYPE
     ElementVariable = RECORD(Types.Variable)
     END;
 VAR
     string*: POINTER TO Types.BasicType;
 
-PROCEDURE ElementVariable.idType(): JsString.Type;
-    RETURN JsString.make("string element")
+PROCEDURE ElementVariable.idType(): STRING;
+    RETURN "string element"
 END ElementVariable.idType;
 
 PROCEDURE ElementVariable.isReadOnly(): BOOLEAN;

+ 2 - 2
src/ob/Cast.ob

@@ -1,5 +1,5 @@
 MODULE Cast;
-IMPORT Code, OberonRtl, JsArray, JsString, Object, Types;
+IMPORT Code, OberonRtl, JsArray, Object, String, Types;
 CONST
     errNo* = 0;
     err* = 1;
@@ -118,7 +118,7 @@ BEGIN
 END areTypesExactlyMatchImpl;
 
 PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN Code.makeSimpleExpression(JsString.fromInt(ORD(SELF.c)), Types.basic.ch)
+    RETURN Code.makeSimpleExpression(String.fromInt(ORD(SELF.c)), Types.basic.ch)
 END CastOpStrToChar.make;
 
 PROCEDURE makeCastOpStrToChar(c: CHAR): PCastOp;

+ 89 - 126
src/ob/Code.ob

@@ -1,15 +1,22 @@
 MODULE Code;
-IMPORT JsMap, JsString, Object, Stream, ScopeBase, Symbols, Precedence := CodePrecedence, Types;
-
+IMPORT 
+    JsMap, 
+    Object, 
+    Stream, 
+    ScopeBase, 
+    Symbols, 
+    Precedence := CodePrecedence, 
+    String, 
+    Types;
 CONST
     kTab = 09X;
 
 TYPE
     IGenerator = RECORD
-        PROCEDURE write(s: JsString.Type);
+        PROCEDURE write(s: STRING);
         PROCEDURE openScope();
-        PROCEDURE closeScope(ending: JsString.Type);
-        PROCEDURE result(): JsString.Type
+        PROCEDURE closeScope(ending: STRING);
+        PROCEDURE result(): STRING
     END;
 
     PIGenerator = POINTER TO IGenerator;
@@ -18,25 +25,25 @@ TYPE
     END;
 
     SimpleGenerator = RECORD(NullGenerator)
-        mResult: JsString.Type
+        mResult: STRING
     END;
 
     Generator = RECORD(SimpleGenerator)
         indent: INTEGER
     END;
 
-    RefCodeProc = PROCEDURE(s: JsString.Type): JsString.Type;
+    RefCodeProc = PROCEDURE(s: STRING): STRING;
 
     Designator* = RECORD
-        PROCEDURE code(): JsString.Type;
-        PROCEDURE lval(): JsString.Type;
+        PROCEDURE code(): STRING;
+        PROCEDURE lval(): STRING;
         PROCEDURE refCode(): RefCodeProc;
         PROCEDURE type(): Types.PType;
         PROCEDURE info*(): Types.PId;
         PROCEDURE scope(): ScopeBase.PType;
 
-        mCode: JsString.Type;
-        mLval: JsString.Type;
+        mCode: STRING;
+        mLval: STRING;
         mRefCode: RefCodeProc;
         mType: Types.PType;
         mInfo: Types.PId;
@@ -62,19 +69,19 @@ TYPE
     END;
 
     StringConst* = RECORD (Const)
-        value*: JsString.Type
+        value*: STRING
     END;
 
     Expression* = RECORD(Object.Type)
-        PROCEDURE code*(): JsString.Type;
-        PROCEDURE lval*(): JsString.Type;
+        PROCEDURE code*(): STRING;
+        PROCEDURE lval*(): STRING;
         PROCEDURE type*(): Types.PType;
         PROCEDURE designator*(): PDesignator;
         PROCEDURE constValue*(): PConst;
         PROCEDURE maxPrecedence*(): INTEGER;
         PROCEDURE isTerm*(): BOOLEAN;
 
-        mCode: JsString.Type;
+        mCode: STRING;
         mType: Types.PType;
         mDesignator: PDesignator;
         mConstValue: PConst;
@@ -84,99 +91,96 @@ TYPE
     PExpression* = POINTER TO Expression;
 
     ModuleGenerator = RECORD
-        PROCEDURE prolog(): JsString.Type;
-        PROCEDURE epilog(exports: JsMap.Type): JsString.Type;
+        PROCEDURE prolog(): STRING;
+        PROCEDURE epilog(exports: JsMap.Type): STRING;
 
-        name: JsString.Type;
+        name: STRING;
         imports: JsMap.Strings
     END;
 
     PModuleGenerator = POINTER TO ModuleGenerator;
 
     Closure = RECORD(Object.Type)
-        result: JsString.Type
+        result: STRING
     END;
 
 VAR
     nullGenerator*: NullGenerator;
 
-PROCEDURE NullGenerator.write(s: JsString.Type);
+PROCEDURE NullGenerator.write(s: STRING);
 END NullGenerator.write;
 
 PROCEDURE NullGenerator.openScope();
 END NullGenerator.openScope;
 
-PROCEDURE NullGenerator.closeScope(ending: JsString.Type);
+PROCEDURE NullGenerator.closeScope(ending: STRING);
 END NullGenerator.closeScope;
 
-PROCEDURE NullGenerator.result(): JsString.Type;
-    RETURN NIL
+PROCEDURE NullGenerator.result(): STRING;
+    RETURN ""
 END NullGenerator.result;
 
-PROCEDURE SimpleGenerator.write(s: JsString.Type);
+PROCEDURE SimpleGenerator.write(s: STRING);
 BEGIN
-    SELF.mResult := JsString.concat(SELF.mResult, s);
+    SELF.mResult := SELF.mResult + s;
 END SimpleGenerator.write;
 
-PROCEDURE SimpleGenerator.result(): JsString.Type;
+PROCEDURE SimpleGenerator.result(): STRING;
     RETURN SELF.mResult
 END SimpleGenerator.result;
 
-PROCEDURE putIndent(s: JsString.Type; indent: INTEGER): JsString.Type;
+PROCEDURE putIndent(s: STRING; indent: INTEGER): STRING;
 VAR
     i: INTEGER;
 BEGIN
     FOR i := 0 TO indent - 1 DO
-        s := JsString.appendChar(s, kTab);
+        s := s + kTab;
     END;
     RETURN s
 END putIndent;
 
-PROCEDURE Generator.write(s: JsString.Type);
+PROCEDURE Generator.write(s: STRING);
 VAR
     pos: INTEGER;
     index: INTEGER;
 BEGIN
-    index := JsString.indexOf(s, Stream.kCR);
+    index := String.indexOf(s, Stream.kCR);
     WHILE index # -1 DO
         INC(index);
-        SELF.mResult := JsString.concat(SELF.mResult, JsString.substr(s, pos, index - pos));
+        SELF.mResult := SELF.mResult + String.substr(s, pos, index - pos);
         SELF.mResult := putIndent(SELF.mResult, SELF.indent);
         pos := index;
-        index := JsString.indexOfFrom(s, Stream.kCR, pos);
+        index := String.indexOfFrom(s, Stream.kCR, pos);
     END;
-    SELF.mResult := JsString.concat(SELF.mResult, 
-                                    JsString.substr(s, pos, JsString.len(s) - pos));
+    SELF.mResult := SELF.mResult + String.substr(s, pos, LEN(s) - pos);
 END Generator.write;
 
 PROCEDURE Generator.openScope();
 BEGIN
     INC(SELF.indent);
-    SELF.mResult := JsString.appendChar(SELF.mResult, "{");
-    SELF.mResult := JsString.appendChar(SELF.mResult, Stream.kCR);
+    SELF.mResult := SELF.mResult + "{" + Stream.kCR;
     SELF.mResult := putIndent(SELF.mResult, SELF.indent);
 END Generator.openScope;
 
-PROCEDURE Generator.closeScope(ending: JsString.Type);
+PROCEDURE Generator.closeScope(ending: STRING);
 BEGIN
     DEC(SELF.indent);
-    SELF.mResult := JsString.substr(SELF.mResult, 0, JsString.len(SELF.mResult) - 1);
-    SELF.mResult := JsString.appendChar(SELF.mResult, "}");
-    IF ending # NIL THEN
+    SELF.mResult := String.substr(SELF.mResult, 0, LEN(SELF.mResult) - 1) + "}";
+    IF LEN(ending) # 0 THEN
         SELF.write(ending);
     ELSE
-        SELF.mResult := JsString.appendChar(SELF.mResult, Stream.kCR);
+        SELF.mResult := SELF.mResult + Stream.kCR;
         SELF.mResult := putIndent(SELF.mResult, SELF.indent);
     END;
 END Generator.closeScope;
 
-PROCEDURE Expression.code(): JsString.Type;
+PROCEDURE Expression.code(): STRING;
     RETURN SELF.mCode
 END Expression.code;
 
-PROCEDURE Expression.lval(): JsString.Type;
+PROCEDURE Expression.lval(): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     IF SELF.mDesignator # NIL THEN
         result := SELF.mDesignator.mLval;
@@ -233,7 +237,7 @@ BEGIN
     RETURN result
 END makeSetConst;
 
-PROCEDURE makeStringConst*(s: JsString.Type): PConst;
+PROCEDURE makeStringConst*(s: STRING): PConst;
 VAR
     result: POINTER TO StringConst;
 BEGIN
@@ -243,7 +247,7 @@ BEGIN
 END makeStringConst;
 
 PROCEDURE makeExpressionWithPrecedence*(
-    code: JsString.Type; 
+    code: STRING; 
     type: Types.PType; 
     designator: PDesignator; 
     constValue: PConst; 
@@ -251,7 +255,6 @@ PROCEDURE makeExpressionWithPrecedence*(
 VAR
     result: PExpression;
 BEGIN
-    ASSERT(code # NIL);
     NEW(result);
     result.mCode := code;
     result.mType := type;
@@ -262,7 +265,7 @@ BEGIN
 END makeExpressionWithPrecedence;
 
 PROCEDURE makeExpression*(
-    code: JsString.Type; 
+    code: STRING; 
     type: Types.PType; 
     designator: PDesignator; 
     constValue: PConst)
@@ -271,17 +274,17 @@ PROCEDURE makeExpression*(
 END makeExpression;
 
 PROCEDURE makeSimpleExpression*(
-    code: JsString.Type; 
+    code: STRING; 
     type: Types.PType)
     : PExpression;
     RETURN makeExpression(code, type, NIL, NIL)
 END makeSimpleExpression;
 
-PROCEDURE Designator.code(): JsString.Type;
+PROCEDURE Designator.code(): STRING;
     RETURN SELF.mCode
 END Designator.code;
 
-PROCEDURE Designator.lval(): JsString.Type;
+PROCEDURE Designator.lval(): STRING;
     RETURN SELF.mLval
 END Designator.lval;
 
@@ -301,7 +304,7 @@ PROCEDURE Designator.scope(): ScopeBase.PType;
     RETURN SELF.mScope
 END Designator.scope;
 
-PROCEDURE makeDesignator*(code: JsString.Type; lval: JsString.Type; refCode: RefCodeProc; type: Types.PType; info: Types.PId; scope: ScopeBase.PType): PDesignator;
+PROCEDURE makeDesignator*(code: STRING; lval: STRING; refCode: RefCodeProc; type: Types.PType; info: Types.PId; scope: ScopeBase.PType): PDesignator;
 VAR
     result: PDesignator;
 BEGIN
@@ -324,8 +327,7 @@ BEGIN
         OR ~(e.mDesignator.mInfo IS Types.PVariableRef) THEN
         result := e;
     ELSE
-        result := makeSimpleExpression(JsString.concat(e.mCode, JsString.make(".get()")),
-                                       e.mType);
+        result := makeSimpleExpression(e.mCode + ".get()", e.mType);
     END;
     RETURN result
 END derefExpression;
@@ -344,35 +346,32 @@ BEGIN
     RETURN result
 END refExpression;
 
-PROCEDURE adjustPrecedence*(e: PExpression; precedence: INTEGER): JsString.Type;
+PROCEDURE adjustPrecedence*(e: PExpression; precedence: INTEGER): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     result := e.mCode;
     IF (precedence # Precedence.none) & (e.mMaxPrecedence > precedence) THEN
-        result := JsString.concat(JsString.concat(
-            JsString.make("("), 
-            result), 
-            JsString.make(")"));
+        result := "(" + result + ")";
     END;
     RETURN result
 END adjustPrecedence;
 
-PROCEDURE isPointerShouldBeExported(type: Types.Pointer): JsString.Type;
+PROCEDURE isPointerShouldBeExported(type: Types.Pointer): STRING;
 VAR
     r: Types.PRecord;
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     r := Types.pointerBase(type);
-    IF Types.typeName(r^) = NIL THEN
+    IF LEN(Types.typeName(r^)) = 0 THEN
         result := Types.recordConstructor(r^);
     END;
     RETURN result
 END isPointerShouldBeExported;
 
-PROCEDURE typeShouldBeExported(typeId: Types.PId; defaultId: JsString.Type): JsString.Type;
+PROCEDURE typeShouldBeExported(typeId: Types.PId; defaultId: STRING): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
     type: Types.PType;
 BEGIN
     type := typeId(Types.PTypeId).type();
@@ -384,15 +383,12 @@ BEGIN
     RETURN result
 END typeShouldBeExported;
 
-PROCEDURE genExport*(s: Symbols.Symbol): JsString.Type;
+PROCEDURE genExport*(s: Symbols.Symbol): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     IF s.isVariable() THEN
-        result := JsString.concat(JsString.concat(
-            JsString.make("function(){return "),
-            s.id()),
-            JsString.make(";}"));
+        result := "function(){return " + s.id() + ";}";
     ELSIF ~s.isType() THEN
         result := s.id();
     ELSE
@@ -401,105 +397,73 @@ BEGIN
     RETURN result
 END genExport;
 
-PROCEDURE genCommaList(name: JsString.Type; closure: Closure);
+PROCEDURE genCommaList(name: STRING; closure: Closure);
 BEGIN
-    IF JsString.len(closure.result) # 0 THEN
-        closure.result := JsString.concat(closure.result, JsString.make(", "));
+    IF LEN(closure.result) # 0 THEN
+        closure.result := closure.result + ", ";
     END;
-    closure.result := JsString.concat(closure.result, name);
+    closure.result := closure.result + name;
 END genCommaList;
 
-PROCEDURE genAliasesAdaptor(key: JsString.Type; value: JsString.Type; VAR closure: Object.Type);
+PROCEDURE genAliasesAdaptor(key: STRING; value: STRING; VAR closure: Object.Type);
 BEGIN
     genCommaList(value, closure(Closure));
 END genAliasesAdaptor;
 
-PROCEDURE ModuleGenerator.prolog(): JsString.Type;
+PROCEDURE ModuleGenerator.prolog(): STRING;
 VAR
     closure: Closure;
 BEGIN
-    closure.result := JsString.makeEmpty();
     JsMap.forEachString(SELF.imports, genAliasesAdaptor, closure);
-    RETURN JsString.appendChar(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-        JsString.make("var "),
-        SELF.name),
-        JsString.make(" = function (")),
-        closure.result),
-        JsString.make("){")),
-        Stream.kCR)
+    RETURN "var " + SELF.name + " = function (" + closure.result + "){" + Stream.kCR
 END ModuleGenerator.prolog;
 
 PROCEDURE genExports(s: Symbols.Symbol; closure: Closure);
 VAR
-    code: JsString.Type;
+    code: STRING;
 BEGIN
     code := genExport(s);
-    IF code # NIL THEN
-        IF JsString.len(closure.result) # 0 THEN
-            closure.result := JsString.appendChar(JsString.appendChar(
-                closure.result,
-                ","),
-                Stream.kCR);
+    IF LEN(code) # 0 THEN
+        IF LEN(closure.result) # 0 THEN
+            closure.result := closure.result + "," + Stream.kCR;
         END;
-        closure.result := JsString.concat(JsString.concat(JsString.concat(JsString.appendChar(
-            closure.result, 
-            kTab),
-            s.id()),
-            JsString.make(": ")),
-            code);
+        closure.result := closure.result + kTab + s.id() + ": " + code;
     END;
 END genExports;
 
-PROCEDURE genExportsAdaptor(key: JsString.Type; value: Object.PType; VAR closure: Object.Type);
+PROCEDURE genExportsAdaptor(key: STRING; value: Object.PType; VAR closure: Object.Type);
 BEGIN
     genExports(value^(Symbols.Symbol), closure(Closure));
 END genExportsAdaptor;
 
-PROCEDURE genImportListAdaptor(key: JsString.Type; value: JsString.Type; VAR closure: Object.Type);
+PROCEDURE genImportListAdaptor(key: STRING; value: STRING; VAR closure: Object.Type);
 BEGIN
     genCommaList(key, closure(Closure));
 END genImportListAdaptor;
 
-PROCEDURE ModuleGenerator.epilog(exports: JsMap.Type): JsString.Type;
+PROCEDURE ModuleGenerator.epilog(exports: JsMap.Type): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
     closure: Closure;
 BEGIN
-    closure.result := JsString.makeEmpty();
     JsMap.forEach(exports, genExportsAdaptor, closure);
     result := closure.result;
-    IF JsString.len(result) # 0 THEN
-        result := JsString.appendChar(JsString.appendChar(JsString.appendChar(JsString.concat(JsString.appendChar(
-            JsString.make("return {"),
-            Stream.kCR),
-            result),
-            Stream.kCR),
-            "}"),
-            Stream.kCR);
+    IF LEN(result) # 0 THEN
+        result := "return {" + Stream.kCR + result + Stream.kCR + "}" + Stream.kCR;
     END;
-    result := JsString.concat(result, JsString.make("}("));
+    result := result + "}(";
 
-    closure.result := JsString.makeEmpty();
+    closure.result := "";
     JsMap.forEachString(SELF.imports, genImportListAdaptor, closure);
-    result := JsString.appendChar(JsString.concat(JsString.concat(
-        result, 
-        closure.result), 
-        JsString.make(");")), 
-        Stream.kCR);
+    result := result + closure.result + ");" + Stream.kCR;
     RETURN result
 END ModuleGenerator.epilog;
 
-PROCEDURE initSimpleGenerator(g: SimpleGenerator);
-BEGIN
-    g.mResult := JsString.makeEmpty();
-END initSimpleGenerator;
-
 PROCEDURE makeSimpleGenerator*(): PIGenerator;
 VAR
     result: POINTER TO SimpleGenerator;
 BEGIN
     NEW(result);
-    initSimpleGenerator(result^);
     RETURN result
 END makeSimpleGenerator;
 
@@ -508,11 +472,10 @@ VAR
     result: POINTER TO Generator;
 BEGIN
     NEW(result);
-    initSimpleGenerator(result^);
     RETURN result
 END makeGenerator;
 
-PROCEDURE makeModuleGenerator*(name: JsString.Type; imports: JsMap.Strings): PModuleGenerator;
+PROCEDURE makeModuleGenerator*(name: STRING; imports: JsMap.Strings): PModuleGenerator;
 VAR
     result: PModuleGenerator;
 BEGIN

+ 5 - 5
src/ob/Context.ob

@@ -1,13 +1,13 @@
 MODULE Context;
-IMPORT JsString, OberonRtl, Object, ScopeBase;
+IMPORT OberonRtl, Object, ScopeBase;
 TYPE
     Type* = RECORD
         handleChar*:    PROCEDURE(c: CHAR);
-        handleLiteral*: PROCEDURE(s: JsString.Type): BOOLEAN;
-        handleString*:  PROCEDURE(s: JsString.Type);
-        handleIdent*:   PROCEDURE(s: JsString.Type);
+        handleLiteral*: PROCEDURE(s: STRING): BOOLEAN;
+        handleString*:  PROCEDURE(s: STRING);
+        handleIdent*:   PROCEDURE(s: STRING);
         isLexem*:       PROCEDURE(): BOOLEAN;
-        qualifyScope*:  PROCEDURE(scope: ScopeBase.PType): JsString.Type;
+        qualifyScope*:  PROCEDURE(scope: ScopeBase.PType): STRING;
         
         rtl*: OberonRtl.PType
     END;

+ 2 - 2
src/ob/Errors.ob

@@ -1,9 +1,9 @@
 MODULE Errors;
-IMPORT JS, JsString;
+IMPORT JS;
 
 TYPE Error* = RECORD END;
 
-PROCEDURE raise*(msg: JsString.Type);
+PROCEDURE raise*(msg: STRING);
 BEGIN
     JS.do("throw new Error(msg)")
 END raise;

+ 6 - 6
src/ob/JsArray.ob

@@ -1,5 +1,5 @@
 MODULE JsArray;
-IMPORT JS, Object, JsString;
+IMPORT JS, Object;
 TYPE
     Type* = POINTER TO RECORD END;
     Strings* = POINTER TO RECORD END;
@@ -25,7 +25,7 @@ BEGIN
     JS.do("a.push(o)");
 END add;
 
-PROCEDURE addString*(a: Strings; o: JsString.Type);
+PROCEDURE addString*(a: Strings; o: STRING);
 BEGIN
     JS.do("a.push(o)");
 END addString;
@@ -43,15 +43,15 @@ BEGIN
     RETURN result
 END at;
 
-PROCEDURE stringsAt*(a: Strings; i: INTEGER): JsString.Type;
+PROCEDURE stringsAt*(a: Strings; i: INTEGER): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     JS.do("result = a[i]");
     RETURN result
 END stringsAt;
 
-PROCEDURE stringsIndexOf*(a: Strings; x: JsString.Type): INTEGER;
+PROCEDURE stringsIndexOf*(a: Strings; x: STRING): INTEGER;
 VAR
     result: INTEGER;
 BEGIN
@@ -67,7 +67,7 @@ BEGIN
     RETURN result
 END contains;
 
-PROCEDURE containsString*(a: Strings; x: JsString.Type): BOOLEAN;
+PROCEDURE containsString*(a: Strings; x: STRING): BOOLEAN;
     RETURN stringsIndexOf(a, x) # -1
 END containsString;
 

+ 7 - 7
src/ob/JsMap.ob

@@ -1,11 +1,11 @@
 MODULE JsMap;
-IMPORT JS, JsString, Object;
+IMPORT JS, Object;
 TYPE
     Type* = POINTER TO RECORD END;
-    ForEachProc = PROCEDURE(key: JsString.Type; value: Object.PType; VAR closure: Object.Type);
+    ForEachProc = PROCEDURE(key: STRING; value: Object.PType; VAR closure: Object.Type);
     
     Strings* = POINTER TO RECORD END;
-    ForEachStringProc = PROCEDURE(key: JsString.Type; value: JsString.Type; VAR closure: Object.Type);
+    ForEachStringProc = PROCEDURE(key: STRING; value: STRING; VAR closure: Object.Type);
 
 PROCEDURE make*(): Type;
 VAR
@@ -15,7 +15,7 @@ BEGIN
     RETURN result    
 END make;
 
-PROCEDURE has*(m: Type; s: JsString.Type): BOOLEAN;
+PROCEDURE has*(m: Type; s: STRING): BOOLEAN;
 VAR
     result: BOOLEAN;
 BEGIN
@@ -23,7 +23,7 @@ BEGIN
     RETURN result
 END has;
 
-PROCEDURE find*(m: Type; s: JsString.Type; VAR r: Object.PType): BOOLEAN;
+PROCEDURE find*(m: Type; s: STRING; VAR r: Object.PType): BOOLEAN;
 VAR
     result: BOOLEAN;
 BEGIN
@@ -31,12 +31,12 @@ BEGIN
     RETURN result
 END find;
 
-PROCEDURE put*(m: Type; s: JsString.Type; o: Object.PType);
+PROCEDURE put*(m: Type; s: STRING; o: Object.PType);
 BEGIN
     JS.do("m[s] = o");
 END put;
 
-PROCEDURE erase*(m: Type; s: JsString.Type);
+PROCEDURE erase*(m: Type; s: STRING);
 BEGIN
     JS.do("delete m[s]");
 END erase;

+ 0 - 92
src/ob/JsString.ob

@@ -1,92 +0,0 @@
-MODULE JsString;
-IMPORT JS;
-
-TYPE
-    Type* = POINTER TO RECORD END;
-
-PROCEDURE make*(s: ARRAY OF CHAR): Type;
-VAR 
-    result: Type;
-    i: INTEGER;
-BEGIN
-    JS.do("result = ''");
-    FOR i := 0 TO LEN(s) - 1 DO
-        JS.do("result += JS.String.fromCharCode(s.charCodeAt(i))")
-    END
-    RETURN result
-END make;
-
-PROCEDURE makeEmpty*(): Type;
-VAR 
-    result: Type;
-BEGIN
-    JS.do("result = ''");
-    RETURN result
-END makeEmpty;
-
-PROCEDURE fromInt*(i: INTEGER): Type;
-VAR 
-    result: Type;
-BEGIN
-    JS.do("result = '' + i");
-    RETURN result
-END fromInt;
-
-PROCEDURE len*(self: Type): INTEGER;
-VAR result: INTEGER;
-BEGIN
-    JS.do("result = self.length");
-    RETURN result
-END len;
-
-PROCEDURE at*(self: Type; pos: INTEGER): CHAR;
-VAR result: CHAR;
-BEGIN
-    JS.do("result = self.charCodeAt(pos)")
-    RETURN result
-END at;
-
-PROCEDURE indexOf*(self: Type; c: CHAR): INTEGER;
-VAR result: INTEGER;
-BEGIN
-    JS.do("result = self.indexOf(JS.String.fromCharCode(c))")
-    RETURN result
-END indexOf;
-
-PROCEDURE indexOfFrom*(self: Type; c: CHAR; pos: INTEGER): INTEGER;
-VAR result: INTEGER;
-BEGIN
-    JS.do("result = self.indexOf(JS.String.fromCharCode(c), pos)")
-    RETURN result
-END indexOfFrom;
-
-PROCEDURE substr*(self: Type; pos: INTEGER; len: INTEGER): Type;
-VAR result: Type;
-BEGIN
-    JS.do("result = self.substr(pos, len)")
-    RETURN result
-END substr;
-
-PROCEDURE appendChar*(self: Type; c: CHAR): Type;
-VAR result: Type;
-BEGIN
-    result := self;
-    JS.do("result += JS.String.fromCharCode(c)")
-    RETURN result
-END appendChar;
-
-PROCEDURE concat*(self: Type; add: Type): Type;
-VAR result: Type;
-BEGIN
-    JS.do("result = self + add");
-    RETURN result
-END concat;
-
-PROCEDURE eq*(s1, s2: Type): BOOLEAN;
-VAR result: BOOLEAN;
-BEGIN
-    JS.do("result = s1 == s2");
-    RETURN result
-END eq;
-
-END JsString.

+ 1 - 1
src/ob/Language.ob

@@ -1,5 +1,5 @@
 MODULE Language;
-IMPORT Cast, JsString, T := Types;
+IMPORT Cast, T := Types;
 TYPE
     Types* = RECORD         
         PROCEDURE implicitCast*(from, to: T.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER

+ 32 - 25
src/ob/Lexer.ob

@@ -1,13 +1,20 @@
 MODULE Lexer;
-IMPORT Context, JS, JsString, Errors, Stream;
+IMPORT Context, JS, Errors, Stream, String;
 
 CONST
     quote = 22X; (* " *)
     commentBegin = "(*";
     commentEnd = "*)";
 
-    (* Math is used in generated code for some functions so it is reserved word from code generator standpoint *)
-    jsReservedWords = "break case catch continue debugger default delete do else finally for function if in instanceof new return switch this throw try typeof var void while with false true null class enum export extends import super implements interface let package private protected public static yield Math";
+    jsReservedWords 
+        = "break case catch continue debugger default delete do else finally "
+        + "for function if in instanceof new return switch this throw try typeof "
+        + "var void while with false true null class enum export extends "
+        + "import super implements interface let package private protected "
+        + "public static yield "
+        + "Math" (* Math is used in generated code for some functions so it is 
+                    reserved word from code generator standpoint *)
+        ;
 
 TYPE
     Literal = POINTER TO RECORD
@@ -50,10 +57,10 @@ BEGIN
     RETURN result
 END hexDigit;
 
-PROCEDURE handleLiteral(context: Context.Type; s: ARRAY OF CHAR): BOOLEAN;
+PROCEDURE handleLiteral(context: Context.Type; s: STRING): BOOLEAN;
 VAR result: BOOLEAN;
 BEGIN
-    JS.do("var r = context.handleLiteral(JsString.make(s)); result = (r === undefined || r)");
+    JS.do("var r = context.handleLiteral(s); result = (r === undefined || r)");
     RETURN result
 END handleLiteral;
 
@@ -73,23 +80,24 @@ PROCEDURE string*(stream: Stream.Type; context: Context.Type): BOOLEAN;
 VAR
     result: BOOLEAN;
     c: CHAR;
-    s: JsString.Type;
+    s: STRING;
 BEGIN
     IF ~Stream.eof(stream) THEN
         c := Stream.getChar(stream);
         IF c = quote THEN
             IF ~Stream.eof(stream) THEN
-                s := JsString.make("");
                 c := Stream.getChar(stream);
                 WHILE (c # quote) & ~Stream.eof(stream) DO
                     IF c # quote THEN
-                        s := JsString.appendChar(s, c);
+                        s := s + String.fromChar(c);
                     END;
                     c := Stream.getChar(stream);
                 END;
+            ELSE
+                c := 0X;
             END;
-            IF (s = NIL) OR (c # quote) THEN
-                Errors.raise(JsString.make("unexpected end of string"));
+            IF c # quote THEN
+                Errors.raise("unexpected end of string");
             END;
             context.handleString(s);
             result := TRUE;
@@ -98,47 +106,46 @@ BEGIN
     RETURN result
 END string;
 
-PROCEDURE isReservedWorld(s: JsString.Type; words: ARRAY OF CHAR): BOOLEAN;
+PROCEDURE isReservedWord(s: STRING; words: STRING): BOOLEAN;
 VAR
     i, w: INTEGER;
 BEGIN
     WHILE (w < LEN(words))
-        & (i < JsString.len(s))
-        & (words[w] = JsString.at(s, i))
+        & (i < LEN(s))
+        & (words[w] = s[i])
         & ((i # 0) OR (w = 0) OR (words[w - 1] = " ")) DO
         INC(w);
         INC(i);
     ELSIF (w < LEN(words)) 
-        & ((i < JsString.len(s)) OR (words[w] # " ")) DO
+        & ((i < LEN(s)) OR (words[w] # " ")) DO
         INC(w);
         i := 0;
     END;
-    RETURN i = JsString.len(s)
-END isReservedWorld;
+    RETURN i = LEN(s)
+END isReservedWord;
 
-PROCEDURE ident*(stream: Stream.Type; context: Context.Type; reservedWords: ARRAY OF CHAR): BOOLEAN;
+PROCEDURE ident*(stream: Stream.Type; context: Context.Type; reservedWords: STRING): BOOLEAN;
 VAR
     result: BOOLEAN;
     c: CHAR;
-    s: JsString.Type;
+    s: STRING;
 BEGIN
     IF ~Stream.eof(stream) THEN
         c := Stream.getChar(stream);
         IF isLetter(c) THEN
-            s := JsString.make("");
             WHILE ~Stream.eof(stream) & (isLetter(c) OR isDigit(c)) DO (* OR c = "_" *)
-                s := JsString.appendChar(s, c);
+                s := s + String.fromChar(c);
                 c := Stream.getChar(stream);
             END;
             IF isLetter(c) OR isDigit(c) THEN
-                s := JsString.appendChar(s, c);
+                s := s + String.fromChar(c);
             ELSE
                 Stream.next(stream, -1);
             END;
 
-            IF ~isReservedWorld(s, reservedWords) THEN
-                IF isReservedWorld(s, jsReservedWords) THEN
-                    s := JsString.appendChar(s, "$");
+            IF ~isReservedWord(s, reservedWords) THEN
+                IF isReservedWord(s, jsReservedWords) THEN
+                    s := s + "$";
                 END;
                 context.handleIdent(s);
                 result := TRUE;
@@ -158,7 +165,7 @@ BEGIN
             IF ~skipComment(stream, context) THEN
                 Stream.next(stream, 1);
                 IF Stream.eof(stream) THEN
-                    Errors.raise(JsString.make("comment was not closed"));
+                    Errors.raise("comment was not closed");
                 END
             END
         END;

+ 25 - 28
src/ob/Module.ob

@@ -1,14 +1,14 @@
 MODULE Module;
-IMPORT Code, Context, Errors, JsArray, JsString, Procedure, Symbols, Types;
+IMPORT Code, Context, Errors, JsArray, LanguageContext, Procedure, Symbols, Types;
 TYPE
     Type* = RECORD(Types.Module)
-        PROCEDURE findSymbol(id: JsString.Type): Symbols.PFoundSymbol
+        PROCEDURE findSymbol(id: STRING): Symbols.PFoundSymbol
     END;
     PType* = POINTER TO Type;
 
     AnyType* = RECORD(Types.StorageType)
-        PROCEDURE callGenerator(cx: Context.PType; id: JsString.Type): Procedure.PCallGenerator;
-        PROCEDURE findSymbol(id: JsString.Type): Types.PType
+        PROCEDURE callGenerator(cx: LanguageContext.PType; id: STRING): Procedure.PCallGenerator;
+        PROCEDURE findSymbol(id: STRING): Types.PType
     END;
 
     AnyTypeProc* = RECORD(Types.DefinedProcedure)
@@ -17,24 +17,24 @@ TYPE
     JS = RECORD(Type)
     END;
 VAR
-    doProcId, varTypeId: JsString.Type;
+    doProcId, varTypeId: STRING;
     any: POINTER TO AnyType;
     anyProc: AnyTypeProc;
     doProcSymbol, varTypeSymbol: Symbols.PSymbol;
 
-PROCEDURE AnyType.description(): JsString.Type;
-    RETURN JsString.make("JS.var")
+PROCEDURE AnyType.description(): STRING;
+    RETURN "JS.var"
 END AnyType.description;
 
-PROCEDURE AnyType.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.make("undefined")
+PROCEDURE AnyType.initializer(cx: Context.Type): STRING;
+    RETURN "undefined"
 END AnyType.initializer;
 
-PROCEDURE AnyType.callGenerator(cx: Context.PType; id: JsString.Type): Procedure.PCallGenerator;
+PROCEDURE AnyType.callGenerator(cx: LanguageContext.PType; id: STRING): Procedure.PCallGenerator;
     RETURN Procedure.makeProcCallGenerator(cx, id, anyProc)
 END AnyType.callGenerator;
 
-PROCEDURE AnyType.findSymbol(id: JsString.Type): Types.PType;
+PROCEDURE AnyType.findSymbol(id: STRING): Types.PType;
     RETURN any
 END AnyType.findSymbol;
 
@@ -46,13 +46,13 @@ PROCEDURE AnyTypeProc.result(): Types.PType;
     RETURN any
 END AnyTypeProc.result;
 
-PROCEDURE JS.findSymbol(id: JsString.Type): Symbols.PFoundSymbol;
+PROCEDURE JS.findSymbol(id: STRING): Symbols.PFoundSymbol;
 VAR
     result: Symbols.PSymbol;
 BEGIN
-    IF JsString.eq(id, doProcId) THEN
+    IF id = doProcId THEN
         result := doProcSymbol;
-    ELSIF JsString.eq(id, varTypeId) THEN
+    ELSIF id = varTypeId THEN
         result := varTypeSymbol;
     ELSE
         result := Symbols.makeSymbol(id, Types.makeProcedure(any));
@@ -71,38 +71,35 @@ TYPE
     Proc = RECORD(Procedure.Std)
     END;
 VAR
-    description: JsString.Type;
+    description: STRING;
     call: POINTER TO Call;
     proc: POINTER TO Proc;
 
-    PROCEDURE Call.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE Call.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         type: Types.PType;
     BEGIN
-        arg := Procedure.checkSingleArgument(args, SELF);
+        arg := Procedure.checkSingleArgument(args, SELF, cx.types);
         type := arg.type();
         IF ~(type IS Types.PString) THEN
-            Errors.raise(JsString.concat(JsString.concat(JsString.concat(
-                JsString.make("string is expected as an argument of "),
-                description),
-                JsString.make(", got ")),
-                type.description()));
+            Errors.raise("string is expected as an argument of "
+                       + description + ", got " + type.description());
         END;
         RETURN Code.makeSimpleExpression(Types.stringValue(type(Types.PString)^), NIL)
     END Call.make;
 
-    PROCEDURE Proc.description(): JsString.Type;
+    PROCEDURE Proc.description(): STRING;
         RETURN description
     END Proc.description;
 BEGIN
-    description := JsString.make("JS predefined procedure 'do'");
+    description := "JS predefined procedure 'do'";
     NEW(call);
     Procedure.initStdCall(call);
     Procedure.hasArgumentWithCustomType(call);
 
     NEW(proc);
-    Procedure.initStd(JsString.makeEmpty(), call, proc^);
+    Procedure.initStd("", call, proc^);
     RETURN Procedure.makeSymbol(proc)
 END makeDoProcSymbol;
 
@@ -111,13 +108,13 @@ VAR
     result: POINTER TO JS;
 BEGIN
     NEW(result);
-    Types.initModule(result^, JsString.make("this"));
+    Types.initModule(result^, "this");
     RETURN result
 END makeJS;
 
 BEGIN
-    doProcId := JsString.make("do$");
-    varTypeId := JsString.make("var$");
+    doProcId := "do$";
+    varTypeId := "var$";
     NEW(any);
     doProcSymbol := makeDoProcSymbol();
     varTypeSymbol := makeVarTypeSymbol();

+ 6 - 7
src/ob/OberonRtl.ob

@@ -1,13 +1,12 @@
 MODULE OberonRtl;
-IMPORT JsString;
 TYPE
     Type* = RECORD
-        copy*: PROCEDURE(s1, s2: JsString.Type): JsString.Type;
-        strCmp*: PROCEDURE(s1, s2: JsString.Type): JsString.Type;
-        assignArrayFromString*: PROCEDURE(s1, s2: JsString.Type): JsString.Type;
-        setInclL*: PROCEDURE(l, r: JsString.Type): JsString.Type;
-        setInclR*: PROCEDURE(l, r: JsString.Type): JsString.Type;
-        assertId*: PROCEDURE(): JsString.Type
+        copy*: PROCEDURE(s1, s2: STRING): STRING;
+        strCmp*: PROCEDURE(s1, s2: STRING): STRING;
+        assignArrayFromString*: PROCEDURE(s1, s2: STRING): STRING;
+        setInclL*: PROCEDURE(l, r: STRING): STRING;
+        setInclR*: PROCEDURE(l, r: STRING): STRING;
+        assertId*: PROCEDURE(): STRING
     END;
     PType* = POINTER TO Type;
 END OberonRtl.

+ 49 - 82
src/ob/Operator.ob

@@ -3,27 +3,27 @@ IMPORT
     Cast, 
     Code, 
     Errors, 
-    JsString, 
     Language, 
     LanguageContext, 
     OberonRtl, 
-    Precedence := CodePrecedence, 
+    Precedence := CodePrecedence,
+    String,
     Types;
 TYPE
     BinaryProc = PROCEDURE(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
 
     BinaryOp = PROCEDURE(left, right: Code.PConst): Code.PConst;
-    CodePredicate = PROCEDURE(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
+    CodePredicate = PROCEDURE(left, right: STRING; rtl: OberonRtl.PType): STRING;
 
     UnaryOp = PROCEDURE(value: Code.PConst): Code.PConst;
 
     CodeMaker = RECORD
-        PROCEDURE make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type
+        PROCEDURE make(left, right: STRING; rtl: OberonRtl.PType): STRING
     END;
     PCodeMaker = POINTER TO CodeMaker;
 
     SimpleCodeMaker = RECORD (CodeMaker)
-        code: JsString.Type
+        code: STRING
     END;
 
     IntCodeMaker = RECORD (SimpleCodeMaker)
@@ -53,7 +53,7 @@ PROCEDURE binary(
 VAR
     result: Code.PExpression;
     leftValue, rightValue, resultValue: Code.PConst;
-    leftCode, rightCode, resultCode: JsString.Type;
+    leftCode, rightCode, resultCode: STRING;
     resultType: Types.PType;
     resultPrecedence: INTEGER;
     rightExpDeref: Code.PExpression;
@@ -90,38 +90,35 @@ BEGIN
     RETURN Code.makeExpressionWithPrecedence(resultCode, resultType, NIL, resultValue, resultPrecedence)
 END binary;
 
-PROCEDURE SimpleCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
-    RETURN JsString.concat(JsString.concat(
-        left, 
-        SELF.code), 
-        right)
+PROCEDURE SimpleCodeMaker.make(left, right: STRING; rtl: OberonRtl.PType): STRING;
+    RETURN left + SELF.code + right
 END SimpleCodeMaker.make;
 
-PROCEDURE IntCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
+PROCEDURE IntCodeMaker.make(left, right: STRING; rtl: OberonRtl.PType): STRING;
 BEGIN
-    RETURN JsString.concat(SUPER(left, right, rtl), JsString.make(" | 0"))
+    RETURN SUPER(left, right, rtl) + " | 0"
 END IntCodeMaker.make;
 
-PROCEDURE PredCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
+PROCEDURE PredCodeMaker.make(left, right: STRING; rtl: OberonRtl.PType): STRING;
 BEGIN
     RETURN SELF.pred(left, right, rtl)
 END PredCodeMaker.make;
 
-PROCEDURE makeSimpleCodeMaker(code: ARRAY OF CHAR): PCodeMaker;
+PROCEDURE makeSimpleCodeMaker(code: STRING): PCodeMaker;
 VAR
     result: POINTER TO SimpleCodeMaker;
 BEGIN
     NEW(result);
-    result.code := JsString.make(code);
+    result.code := code;
     RETURN result
 END makeSimpleCodeMaker;
 
-PROCEDURE makeIntCodeMaker(code: ARRAY OF CHAR): PCodeMaker;
+PROCEDURE makeIntCodeMaker(code: STRING): PCodeMaker;
 VAR
     result: POINTER TO IntCodeMaker;
 BEGIN
     NEW(result);
-    result.code := JsString.make(code);
+    result.code := code;
     RETURN result
 END makeIntCodeMaker;
 
@@ -218,22 +215,21 @@ PROCEDURE binaryPred(
         )
 END binaryPred;
 
-PROCEDURE unary(e: Code.PExpression; op: UnaryOp; code: ARRAY OF CHAR): Code.PExpression;
+PROCEDURE unary(e: Code.PExpression; op: UnaryOp; code: STRING): Code.PExpression;
 VAR
     value: Code.PConst;
-    resultCode: JsString.Type;
+    resultCode: STRING;
 BEGIN
     value := e.constValue();
     IF value # NIL THEN
         value := op(value);
     END;
-    resultCode := JsString.concat(
-        JsString.make(code), 
-        Code.adjustPrecedence(Code.derefExpression(e), Precedence.unary));
+    resultCode := code 
+                + Code.adjustPrecedence(Code.derefExpression(e), Precedence.unary);
     RETURN Code.makeExpression(resultCode, e.type(), NIL, value)
 END unary;
 
-PROCEDURE castToStr(e: Code.PExpression; rtl: OberonRtl.PType): JsString.Type;
+PROCEDURE castToStr(e: Code.PExpression; rtl: OberonRtl.PType): STRING;
 VAR
     resultExpression: Code.PExpression;
     op: Cast.PCastOp;
@@ -438,48 +434,43 @@ PROCEDURE opRor(left, right: Code.PConst): Code.PConst;
                                  right^(Code.IntConst).value))
 END opRor;
 
-PROCEDURE codeSetInclL(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
+PROCEDURE codeSetInclL(left, right: STRING; rtl: OberonRtl.PType): STRING;
 BEGIN
     RETURN rtl.setInclL(left, right)
 END codeSetInclL;
 
-PROCEDURE codeSetInclR(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
+PROCEDURE codeSetInclR(left, right: STRING; rtl: OberonRtl.PType): STRING;
     RETURN rtl.setInclR(left, right)
 END codeSetInclR;
 
 PROCEDURE strCmp(op: ARRAY OF CHAR; left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
 BEGIN   
     RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                rtl.strCmp(castToStr(left, rtl), castToStr(right, rtl)),
-                JsString.make(op)), 
-                JsString.make("0")),
+            rtl.strCmp(castToStr(left, rtl), castToStr(right, rtl)) + op + "0",
             Types.basic.bool)
 END strCmp;
 
-PROCEDURE assign*(left, right: Code.PExpression; cx: LanguageContext.Type): JsString.Type;
+PROCEDURE assign*(left, right: Code.PExpression; cx: LanguageContext.Type): STRING;
 VAR
     designator: Code.PDesignator;
     info: Types.PId;
-    leftCode, rightCode: JsString.Type;
+    leftCode, rightCode: STRING;
     leftType, rightType: Types.PType;
     isArray: BOOLEAN;
     castOperation: Cast.PCastOp;
     castExp: Code.PExpression;
     ignored: BOOLEAN;
-    result: JsString.Type;
+    result: STRING;
 
-    PROCEDURE assignArrayFromString(a: Types.Array; s: Types.String): JsString.Type;
+    PROCEDURE assignArrayFromString(a: Types.Array; s: Types.String): STRING;
     BEGIN
         IF Types.arrayLength(a) = Types.openArrayLength THEN
-            Errors.raise(JsString.concat(JsString.make("string cannot be assigned to open "),
-                                         a.description()));
+            Errors.raise("string cannot be assigned to open " + a.description());
         ELSIF Types.stringLen(s) > Types.arrayLength(a) THEN
-            Errors.raise(JsString.concat(JsString.concat(JsString.concat(
-                JsString.fromInt(Types.arrayLength(a)), 
-                JsString.make("-character ARRAY is too small for ")),
-                JsString.fromInt(Types.stringLen(s))), 
-                JsString.make("-character string")));
+            Errors.raise(String.fromInt(Types.arrayLength(a)) 
+                         + "-character ARRAY is too small for "
+                         + String.fromInt(Types.stringLen(s))
+                         + "-character string");
         END;
         RETURN cx.rtl.assignArrayFromString(leftCode, rightCode)
     END assignArrayFromString;
@@ -488,7 +479,7 @@ BEGIN
     info := designator.info();
     IF ~(info IS Types.PVariable) 
         OR info(Types.PVariable).isReadOnly() THEN
-        Errors.raise(JsString.concat(JsString.make("cannot assign to "), info.idType()));
+        Errors.raise("cannot assign to " + info.idType());
     END; 
 
     leftCode := left.lval();
@@ -504,23 +495,12 @@ BEGIN
     ELSE
         IF cx.types.implicitCast(rightType, leftType, FALSE, castOperations, castOperation)
             # Cast.errNo THEN;
-            Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-                JsString.make("type mismatch: '"), 
-                leftCode),
-                JsString.make("' is '")),
-                leftType.description()),
-                JsString.make("' and cannot be assigned to '")),
-                rightType.description()),
-                JsString.make("' expression")));
+            Errors.raise("type mismatch: '" + leftCode + "' is '" + leftType.description()
+                         + "' and cannot be assigned to '" + rightType.description() + "' expression");
         END;
         IF isArray & (rightType IS Types.PArray) 
             & (Types.arrayLength(leftType(Types.PArray)^) = Types.openArrayLength) THEN
-            Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-                JsString.make("'"),
-                leftCode),
-                JsString.make("' is open '")),
-                leftType.description()),
-                JsString.make("' and cannot be assigned")));
+            Errors.raise("'" + leftCode + "' is open '" + leftType.description() + "' and cannot be assigned");
         END;
         IF isArray OR (rightType IS Types.PRecord) THEN
             result := cx.rtl.copy(rightCode, leftCode);
@@ -532,34 +512,28 @@ BEGIN
             END;
             rightCode := castExp.code();
             IF info IS Types.PVariableRef THEN
-                rightCode := JsString.concat(JsString.concat(
-                    JsString.make(".set("), 
-                    rightCode), 
-                    JsString.make(")"));
+                rightCode := ".set(" + rightCode + ")";
             ELSE
-                rightCode := JsString.concat(JsString.make(" = "), rightCode);
+                rightCode := " = " + rightCode;
             END;
-            result := JsString.concat(leftCode, rightCode);
+            result := leftCode + rightCode;
         END;
     END;
     RETURN result
 END assign;
     
-PROCEDURE inplace(left, right: Code.PExpression; cx: LanguageContext.Type; code: ARRAY OF CHAR; altOp: BinaryProc): JsString.Type;
+PROCEDURE inplace(left, right: Code.PExpression; cx: LanguageContext.Type; code: STRING; altOp: BinaryProc): STRING;
 VAR
     designator: Code.PDesignator;
     rightExp: Code.PExpression;
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     designator := left.designator();
     IF designator.info() IS Types.PVariableRef THEN
         result := assign(left, altOp(left, right, cx.rtl), cx);
     ELSE
         rightExp := Code.derefExpression(right);
-        result := JsString.concat(JsString.concat(
-            left.code(), 
-            JsString.make(code)), 
-            rightExp.code());
+        result := left.code() + code + rightExp.code();
     END;
     RETURN result
 END inplace;
@@ -757,11 +731,11 @@ PROCEDURE ror*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpre
     RETURN binaryWithCode(left, right, rtl, opRor, " >>> ", Precedence.shift)
 END ror;
 
-PROCEDURE mulInplace*(left, right: Code.PExpression; cx: LanguageContext.Type): JsString.Type;
+PROCEDURE mulInplace*(left, right: Code.PExpression; cx: LanguageContext.Type): STRING;
     RETURN inplace(left, right, cx, " *= ", mulReal)
 END mulInplace;
 
-PROCEDURE divInplace*(left, right: Code.PExpression; cx: LanguageContext.Type): JsString.Type;
+PROCEDURE divInplace*(left, right: Code.PExpression; cx: LanguageContext.Type): STRING;
     RETURN inplace(left, right, cx, " /= ", divReal)
 END divInplace;
 
@@ -770,12 +744,8 @@ VAR
     derefExp: Code.PExpression;
 BEGIN
     derefExp := Code.derefExpression(e);
-    RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                JsString.make("Math.pow(2, "),
-                derefExp.code()),
-                JsString.make(")")),
-            Types.basic.real)
+    RETURN Code.makeSimpleExpression("Math.pow(2, " + derefExp.code() + ")",
+                                     Types.basic.real)
 END pow2;
 
 PROCEDURE log2*(e: Code.PExpression): Code.PExpression;
@@ -784,10 +754,7 @@ VAR
 BEGIN
     derefExp := Code.derefExpression(e);
     RETURN Code.makeExpressionWithPrecedence(
-            JsString.concat(JsString.concat(
-                JsString.make("(Math.log("),
-                derefExp.code()),
-                JsString.make(") / Math.LN2) | 0")),
+            "(Math.log(" + derefExp.code() + ") / Math.LN2) | 0",
             Types.basic.integer,
             NIL,
             NIL,
@@ -802,7 +769,7 @@ END opCastToUint8;
 PROCEDURE CastToUint8.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
     RETURN binaryWithCode(
         e, 
-        Code.makeExpression(JsString.make("0xFF"), 
+        Code.makeExpression("0xFF", 
                             Types.basic.integer, 
                             NIL, 
                             Code.makeIntConst(0FFH)), 
@@ -813,7 +780,7 @@ PROCEDURE CastToUint8.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExp
 END CastToUint8.make;
 
 BEGIN
-    openArrayChar := Types.makeArray(NIL, NIL, Types.basic.ch, Types.openArrayLength);
+    openArrayChar := Types.makeArray("", "", Types.basic.ch, Types.openArrayLength);
 
     NEW(castToUint8);
     castOperations.castToUint8 := castToUint8;

+ 119 - 189
src/ob/Procedure.ob

@@ -5,13 +5,13 @@ IMPORT
     Context, 
     Errors, 
     JsArray, 
-    JsString, 
     Language,
     LanguageContext,
     OberonRtl,
     Object, 
     Operator, 
     Precedence := CodePrecedence, 
+    String,
     Symbols, 
     Types;
 TYPE
@@ -45,7 +45,7 @@ TYPE
     END;
 
     Type* = RECORD(Types.DefinedProcedure)
-        PROCEDURE callGenerator(cx: LanguageContext.PType; id: JsString.Type): PCallGenerator;
+        PROCEDURE callGenerator(cx: LanguageContext.PType; id: STRING): PCallGenerator;
         PROCEDURE define(args: JsArray.Type; result: Types.PType);
 
         mArgs: JsArray.Type;
@@ -62,17 +62,17 @@ TYPE
                         expected: Types.PProcedureArgument; 
                         cast: Cast.PCastOp
                         );
-        PROCEDURE result(): JsString.Type
+        PROCEDURE result(): STRING
     END;
     PArgumentsCode = POINTER TO ArgumentsCode;
 
     GenArgCode = RECORD(ArgumentsCode)
-        code: JsString.Type;
+        code: STRING;
         cx: Context.PType
     END;
 
     BinaryOp = PROCEDURE(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
-    BinaryOpStr = PROCEDURE (x, y: JsString.Type): JsString.Type;
+    BinaryOpStr = PROCEDURE (x, y: STRING): STRING;
 VAR
     predefined*: JsArray.Type;
 
@@ -95,37 +95,27 @@ BEGIN
         actualType := actual.type();
         castErr := types.implicitCast(actualType, expectType, expected.isVar, Operator.castOperations, result);
         IF castErr = Cast.errVarParameter THEN
-                Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-                    JsString.make("type mismatch for argument "),
-                    JsString.fromInt(pos + 1)),
-                    JsString.make(": cannot pass '")),
-                    actualType.description()),
-                    JsString.make("' as VAR parameter of type '")),
-                    expectType.description()),
-                    JsString.make("'")));
+            Errors.raise("type mismatch for argument " + String.fromInt(pos + 1)
+                         + ": cannot pass '" + actualType.description()
+                         + "' as VAR parameter of type '" + expectType.description() + "'");
         ELSIF castErr # Cast.errNo THEN
-                Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-                    JsString.make("type mismatch for argument "),
-                    JsString.fromInt(pos + 1)),
-                    JsString.make(": '")),
-                    actualType.description()),
-                    JsString.make("' cannot be converted to '")),
-                    expectType.description()),
-                    JsString.make("'")));
+            Errors.raise("type mismatch for argument " + String.fromInt(pos + 1)
+                         + ": '" + actualType.description() + "' cannot be converted to '"
+                         + expectType.description() + "'");
         END;
     END;
     IF expected.isVar THEN
         designator := actual.designator();
         IF designator = NIL THEN
-            Errors.raise(JsString.make("expression cannot be used as VAR parameter"));
+            Errors.raise("expression cannot be used as VAR parameter");
         END;
         info := designator.info();
         IF info IS Types.PConst THEN
-            Errors.raise(JsString.make("constant cannot be used as VAR parameter"));
+            Errors.raise("constant cannot be used as VAR parameter");
         END;
         IF (info IS Types.PVariable) 
          & info(Types.PVariable).isReadOnly() THEN
-            Errors.raise(JsString.concat(info.idType(), JsString.make(" cannot be used as VAR parameter")));
+            Errors.raise(info.idType() + " cannot be used as VAR parameter");
         END;
     END;
     IF code # NIL THEN
@@ -157,10 +147,9 @@ END checkArgumentsType;
 PROCEDURE checkArgumentsCount*(actual, expected: INTEGER);
 BEGIN
     IF actual # expected THEN
-        Errors.raise(JsString.concat(JsString.concat(
-            JsString.fromInt(expected),
-            JsString.make(" argument(s) expected, got ")),
-            JsString.fromInt(actual)));
+        Errors.raise(
+            String.fromInt(expected) + " argument(s) expected, got " 
+            + String.fromInt(actual));
     END;
 END checkArgumentsCount;
 
@@ -180,13 +169,13 @@ BEGIN
     processArguments(actual, expected, NIL, types);
 END checkArguments;
 
-PROCEDURE initStd*(name: JsString.Type; call: PCall; result: Std);
+PROCEDURE initStd*(name: STRING; call: PCall; result: Std);
 BEGIN
     Types.initProcedure(result, name);
     result.call := call;
 END initStd;
 
-PROCEDURE makeStd(name: JsString.Type; call: PCall): Types.PProcedure;
+PROCEDURE makeStd(name: STRING; call: PCall): Types.PProcedure;
 VAR
     result: POINTER TO Std;
 BEGIN
@@ -225,30 +214,30 @@ BEGIN
     ELSE
         actual := Code.derefExpression(actual);
     END;
-    IF JsString.len(SELF.code) # 0 THEN
-        SELF.code := JsString.concat(SELF.code, JsString.make(", "));
+    IF LEN(SELF.code) # 0 THEN
+        SELF.code := SELF.code + ", ";
     END;
     IF cast # NIL THEN
         e := cast.make(SELF.cx.rtl, actual);
     ELSE
         e := actual;
     END;
-    SELF.code := JsString.concat(SELF.code, e.code());
+    SELF.code := SELF.code + e.code();
 END GenArgCode.write;
 
-PROCEDURE GenArgCode.result(): JsString.Type;
+PROCEDURE GenArgCode.result(): STRING;
     RETURN SELF.code
 END GenArgCode.result;
 
 PROCEDURE makeProcCallGeneratorWithCustomArgs*(
     cx: LanguageContext.PType; 
-    id: JsString.Type; 
+    id: STRING; 
     type: Types.DefinedProcedure;
     argumentsCode: PArgumentsCode
     ) : PCallGenerator;
 TYPE
     CallImpl = RECORD(Call)
-        id: JsString.Type;
+        id: STRING;
         args: JsArray.Type;
         result: Types.PType;
         argumentsCode: PArgumentsCode
@@ -273,13 +262,9 @@ VAR
             END;
         END;
         RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(JsString.concat(
-                SELF.id,
-                JsString.make("(")),
-                SELF.argumentsCode.result()),
-                JsString.make(")")),
-            SELF.result
-            )
+                SELF.id + "(" + SELF.argumentsCode.result() + ")",
+                SELF.result
+                )
     END CallImpl.make;
 BEGIN
     NEW(call);
@@ -295,21 +280,20 @@ VAR
     result: POINTER TO GenArgCode;
 BEGIN
     NEW(result);
-    result.code := JsString.makeEmpty();
     result.cx := cx;
     RETURN result
 END makeArgumentsCode;
 
 PROCEDURE makeProcCallGenerator*(
     cx: LanguageContext.PType; 
-    id: JsString.Type; 
+    id: STRING; 
     type: Types.DefinedProcedure
     ) : PCallGenerator;
     RETURN makeProcCallGeneratorWithCustomArgs(cx, id, type, makeArgumentsCode(cx))
 END makeProcCallGenerator;
 
-PROCEDURE Std.description(): JsString.Type;
-    RETURN JsString.concat(JsString.make("standard procedure "), Types.typeName(SELF))
+PROCEDURE Std.description(): STRING;
+    RETURN "standard procedure " + Types.typeName(SELF)
 END Std.description;
 
 PROCEDURE Std.callGenerator(cx: LanguageContext.PType): PCallGenerator;
@@ -393,27 +377,22 @@ PROCEDURE makeNew(): Symbols.PSymbol;
         arg := checkSingleArgument(args, SELF, cx.types);
         argType := arg.type();
         IF ~(argType IS Types.PPointer) THEN
-            Errors.raise(JsString.concat(JsString.concat(
-                JsString.make("POINTER variable expected, got '"),
-                argType.description()),
-                JsString.make("'")));
+            Errors.raise("POINTER variable expected, got '" 
+                         + argType.description() + "'");
         END;
         baseType := Types.pointerBase(argType(Types.PPointer)^);
         IF baseType IS Types.PNonExportedRecord THEN
-            Errors.raise(JsString.make("non-exported RECORD type cannot be used in NEW"));
+            Errors.raise("non-exported RECORD type cannot be used in NEW");
         END;
         RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                arg.code(), 
-                JsString.make(" = ")),
-                baseType.initializer(cx)),
-            NIL)
+                arg.code() + " = " + baseType.initializer(cx),
+                NIL)
     END CallImpl.make;
 BEGIN
     NEW(call);
     initStdCall(call);
     hasVarArgumnetWithCustomType(call);
-    RETURN makeSymbol(makeStd(JsString.make("NEW"), call))
+    RETURN makeSymbol(makeStd("NEW", call))
 END makeNew;
 
 PROCEDURE lenArgumentCheck*(argType: Types.PType): BOOLEAN;
@@ -428,13 +407,11 @@ BEGIN
     arg := checkSingleArgument(args, SELF, cx.types);
     argType := arg.type();
     IF ~SELF.check(argType) THEN
-        Errors.raise(JsString.concat(JsString.concat(
-            JsString.make("ARRAY or string is expected as an argument of LEN, got '"),
-            argType.description()),
-            JsString.make("'")));
+        Errors.raise("ARRAY or string is expected as an argument of LEN, got '"
+                     + argType.description() + "'");
     END;
     RETURN Code.makeSimpleExpression(
-        JsString.concat(arg.code(), JsString.make(".length")),
+        arg.code() + ".length",
         Types.basic.integer)
 END CallLen.make;
 
@@ -446,7 +423,7 @@ BEGIN
     initStdCall(call);
     call.check := check;
     hasArgumentWithCustomType(call);
-    RETURN makeSymbol(makeStd(JsString.make("LEN"), call))
+    RETURN makeSymbol(makeStd("LEN", call))
 END makeLen;
 
 PROCEDURE makeOdd(): Symbols.PSymbol;
@@ -459,7 +436,7 @@ PROCEDURE makeOdd(): Symbols.PSymbol;
     PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
-        code: JsString.Type;
+        code: STRING;
         constValue: Code.PConst;
     BEGIN
         arg := checkSingleArgument(args, SELF, cx.types);
@@ -472,7 +449,7 @@ PROCEDURE makeOdd(): Symbols.PSymbol;
         END;
 
         RETURN Code.makeExpressionWithPrecedence(
-            JsString.concat(code, JsString.make(" & 1")),
+            code + " & 1",
             Types.basic.bool,
             NIL,
             constValue,
@@ -482,7 +459,7 @@ BEGIN
     NEW(call);
     initStdCall(call);
     hasArgument(call, Types.basic.integer);
-    RETURN makeSymbol(makeStd(JsString.make("ODD"), call))
+    RETURN makeSymbol(makeStd("ODD", call))
 END makeOdd;
 
 PROCEDURE makeAssert(): Symbols.PSymbol;
@@ -498,24 +475,20 @@ PROCEDURE makeAssert(): Symbols.PSymbol;
     BEGIN
         arg := checkSingleArgument(args, SELF, cx.types);
         RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(JsString.concat(
-                cx.rtl.assertId(), 
-                JsString.make("(")), 
-                arg.code()), 
-                JsString.make(")")),
-            NIL)
+                cx.rtl.assertId() + "(" + arg.code() + ")",
+                NIL)
     END CallImpl.make;
 BEGIN
     NEW(call);
     initStdCall(call);
     hasArgument(call, Types.basic.bool);
-    RETURN makeSymbol(makeStd(JsString.make("ASSERT"), call))
+    RETURN makeSymbol(makeStd("ASSERT", call))
 END makeAssert;
 
-PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
+PROCEDURE setBitImpl(name: STRING; bitOp: BinaryOpStr): Symbols.PSymbol;
     TYPE
         CallImpl = RECORD(StdCall)
-            name: JsString.Type;
+            name: STRING;
             bitOp: BinaryOpStr
         END;
     VAR
@@ -527,8 +500,8 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
         yValue: INTEGER;
         value: Code.PConst;
         valueCodeExp: Code.PExpression;
-        valueCode: JsString.Type;
-        comment: JsString.Type;
+        valueCode: STRING;
+        comment: STRING;
     BEGIN
         checkArguments(args, SELF.args, cx.types);
         ASSERT(JsArray.len(args) = 2);
@@ -538,7 +511,7 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
         IF value = NIL THEN
             valueCodeExp := Operator.lsl(
                 Code.makeExpression(
-                    JsString.make("1"), 
+                    "1", 
                     Types.basic.integer,
                     NIL,
                     Code.makeIntConst(1)), 
@@ -548,24 +521,17 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
         ELSE
             yValue := value^(Code.IntConst).value;
             IF (yValue < 0) OR (yValue > 31) THEN
-                Errors.raise(JsString.concat(JsString.concat(JsString.concat(
-                    JsString.make("value (0..31) expected as a second argument of "),
-                    SELF.name),
-                    JsString.make(", got ")),
-                    JsString.fromInt(yValue)));
+                Errors.raise("value (0..31) expected as a second argument of " 
+                             + SELF.name + ", got " + String.fromInt(yValue));
             END;
-            comment := JsString.make("bit: ");
+            comment := "bit: ";
             IF y.isTerm() THEN
-                comment := JsString.concat(comment, JsString.fromInt(yValue));
+                comment := comment + String.fromInt(yValue);
             ELSE
-                comment := JsString.concat(comment, Code.adjustPrecedence(y, Precedence.shift));
+                comment := comment + Code.adjustPrecedence(y, Precedence.shift);
             END;
             yValue := LSL(1, yValue);
-            valueCode := JsString.concat(JsString.concat(JsString.concat(
-                JsString.fromInt(yValue), 
-                JsString.make("/*")),
-                comment),
-                JsString.make("*/"));
+            valueCode := String.fromInt(yValue) + "/*" + comment + "*/";
         END;
 
         RETURN Code.makeSimpleExpression(
@@ -575,7 +541,7 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
 BEGIN
     NEW(call);
     initStdCall(call);
-    call.name := JsString.make(name);
+    call.name := name;
     call.bitOp := bitOp;
     hasVarArgument(call, Types.basic.set);
     hasArgument(call, Types.basic.integer);
@@ -588,25 +554,19 @@ VAR
 BEGIN
     len := JsArray.len(actual);
     IF len < min THEN
-        Errors.raise(JsString.concat(JsString.concat(JsString.concat(
-            JsString.make("at least "), 
-            JsString.fromInt(min)), 
-            JsString.make(" argument expected, got ")), 
-            JsString.fromInt(len)));
+        Errors.raise("at least " + String.fromInt(min) + " argument expected, got " 
+                     + String.fromInt(len));
     ELSIF len > max THEN
-        Errors.raise(JsString.concat(JsString.concat(JsString.concat(
-            JsString.make("at most "),
-            JsString.fromInt(max)),
-            JsString.make(" arguments expected, got ")),
-            JsString.fromInt(len)));
+        Errors.raise("at most " + String.fromInt(max) + " arguments expected, got "
+                     + String.fromInt(len));
     END;
 END checkVariableArgumentsCount;
 
-PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr): Symbols.PSymbol;
+PROCEDURE incImpl(name: STRING; unary: STRING; incOp: BinaryOpStr): Symbols.PSymbol;
     TYPE
         CallImpl = RECORD(StdCall)
-            name: JsString.Type;
-            unary: JsString.Type;
+            name: STRING;
+            unary: STRING;
             incOp: BinaryOpStr
         END;
     VAR
@@ -615,28 +575,24 @@ PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr)
     PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
-        code: JsString.Type;
+        code: STRING;
         value: Code.PConst;
-        valueCode: JsString.Type;
+        valueCode: STRING;
     BEGIN
         checkVariableArgumentsCount(1, 2, args);
         checkArgumentsType(args, SELF.args, NIL, cx.types);
         x := nthArgument(args, 0);
         IF JsArray.len(args) = 1 THEN
-            code := JsString.concat(SELF.unary, x.code());
+            code := SELF.unary + x.code();
         ELSE
             y := nthArgument(args, 1);
             value := y.constValue();
             IF value = NIL THEN
                 valueCode := y.code();
             ELSE
-                valueCode := JsString.fromInt(value^(Code.IntConst).value);
+                valueCode := String.fromInt(value^(Code.IntConst).value);
                 IF ~y.isTerm() THEN
-                    valueCode := JsString.concat(JsString.concat(JsString.concat(
-                        valueCode, 
-                        JsString.make("/*")), 
-                        y.code()), 
-                        JsString.make("*/"));
+                    valueCode := valueCode + "/*" + y.code() + "*/";
                 END;
             END;
             code := SELF.incOp(x.code(), valueCode);
@@ -646,32 +602,28 @@ PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr)
 BEGIN
     NEW(call);
     initStdCall(call);
-    call.name := JsString.make(name);
-    call.unary := JsString.make(unary);
+    call.name := name;
+    call.unary := unary;
     call.incOp := incOp;
     hasVarArgument(call, Types.basic.integer);
     hasArgument(call, Types.basic.integer);
     RETURN makeSymbol(makeStd(call.name, call))
 END incImpl;
 
-PROCEDURE inclOp(x, y: JsString.Type): JsString.Type;
-    RETURN JsString.concat(JsString.concat(x, JsString.make(" |= ")), y)
+PROCEDURE inclOp(x, y: STRING): STRING;
+    RETURN x + " |= " + y
 END inclOp;
 
-PROCEDURE exclOp(x, y: JsString.Type): JsString.Type;
-    RETURN JsString.concat(JsString.concat(JsString.concat(
-        x, 
-        JsString.make(" &= ~(" )), 
-        y), 
-        JsString.make(")"))
+PROCEDURE exclOp(x, y: STRING): STRING;
+    RETURN x + " &= ~(" + y + ")"
 END exclOp;
 
-PROCEDURE incOp(x, y: JsString.Type): JsString.Type;
-    RETURN JsString.concat(JsString.concat(x, JsString.make(" += ")), y)
+PROCEDURE incOp(x, y: STRING): STRING;
+    RETURN x + " += " + y
 END incOp;
 
-PROCEDURE decOp(x, y: JsString.Type): JsString.Type;
-    RETURN JsString.concat(JsString.concat(x, JsString.make(" -= ")), y)
+PROCEDURE decOp(x, y: STRING): STRING;
+    RETURN x + " -= " + y
 END decOp;
 
 PROCEDURE makeAbs(): Symbols.PSymbol;
@@ -689,23 +641,16 @@ PROCEDURE makeAbs(): Symbols.PSymbol;
         arg := checkSingleArgument(args, SELF, cx.types);
         argType := arg.type();
         IF ~JsArray.contains(Types.numeric, argType) THEN
-            Errors.raise(JsString.concat(JsString.concat(
-                JsString.make("type mismatch: expected numeric type, got '"),
-                argType.description()),
-                JsString.make("'")));
+            Errors.raise("type mismatch: expected numeric type, got '"
+                         + argType.description() + "'");
         END;
-        RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                JsString.make("Math.abs("), 
-                arg.code()), 
-                JsString.make(")")),
-            argType)
+        RETURN Code.makeSimpleExpression("Math.abs(" + arg.code() + ")", argType)
     END CallImpl.make;
 BEGIN
     NEW(call);
     initStdCall(call);
     hasArgumentWithCustomType(call);
-    RETURN makeSymbol(makeStd(JsString.make("ABS"), call))
+    RETURN makeSymbol(makeStd("ABS", call))
 END makeAbs;
 
 PROCEDURE makeFloor(): Symbols.PSymbol;
@@ -721,17 +666,14 @@ PROCEDURE makeFloor(): Symbols.PSymbol;
     BEGIN
         arg := checkSingleArgument(args, SELF, cx.types);
         RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                JsString.make("Math.floor("), 
-                arg.code()), 
-                JsString.make(")")),
+            "Math.floor(" + arg.code() + ")",
             Types.basic.integer)
     END CallImpl.make;
 BEGIN
     NEW(call);
     initStdCall(call);
     hasArgument(call, Types.basic.real);
-    RETURN makeSymbol(makeStd(JsString.make("FLOOR"), call))
+    RETURN makeSymbol(makeStd("FLOOR", call))
 END makeFloor;
 
 PROCEDURE makeFlt(): Symbols.PSymbol;
@@ -762,13 +704,13 @@ BEGIN
     NEW(call);
     initStdCall(call);
     hasArgument(call, Types.basic.integer);
-    RETURN makeSymbol(makeStd(JsString.make("FLT"), call))
+    RETURN makeSymbol(makeStd("FLT", call))
 END makeFlt;
 
-PROCEDURE bitShiftImpl(name: ARRAY OF CHAR; op: BinaryOp): Symbols.PSymbol;
+PROCEDURE bitShiftImpl(name: STRING; op: BinaryOp): Symbols.PSymbol;
     TYPE
         CallImpl = RECORD(StdCall)
-            name: JsString.Type;
+            name: STRING;
             op: BinaryOp
         END;
     VAR
@@ -787,7 +729,7 @@ PROCEDURE bitShiftImpl(name: ARRAY OF CHAR; op: BinaryOp): Symbols.PSymbol;
 BEGIN
     NEW(call);
     initStdCall(call);
-    call.name := JsString.make(name);
+    call.name := name;
     call.op := op;
     hasArgument(call, Types.basic.integer);
     hasArgument(call, Types.basic.integer);
@@ -806,7 +748,7 @@ PROCEDURE makeOrd(): Symbols.PSymbol;
         arg: Code.PExpression;
         argType: Types.PType;
         value: Code.PConst;
-        code: JsString.Type;
+        code: STRING;
         ch: CHAR;
         result: Code.PExpression;
     BEGIN
@@ -819,9 +761,8 @@ PROCEDURE makeOrd(): Symbols.PSymbol;
             END;
             result := Code.makeExpression(arg.code(), Types.basic.integer, NIL, value);
         ELSIF argType = Types.basic.bool THEN
-            code := JsString.concat(
-                Code.adjustPrecedence(arg, Precedence.conditional), 
-                JsString.make(" ? 1 : 0"));
+            code := Code.adjustPrecedence(arg, Precedence.conditional) 
+                  + " ? 1 : 0";
             result := Code.makeExpressionWithPrecedence(
                 code, 
                 Types.basic.integer, 
@@ -831,15 +772,14 @@ PROCEDURE makeOrd(): Symbols.PSymbol;
         ELSIF (argType IS Types.PString) 
             & (Types.stringAsChar(argType(Types.PString)^, ch)) THEN
             result := Code.makeExpression(
-                JsString.fromInt(ORD(ch)), 
+                String.fromInt(ORD(ch)), 
                 Types.basic.integer,
                 NIL,
                 Code.makeIntConst(ORD(ch)));
         ELSE
-            Errors.raise(JsString.concat(JsString.concat(
-                JsString.make("ORD function expects CHAR or BOOLEAN or SET as an argument, got '"),
-                argType.description()),
-                JsString.make("'")));
+            Errors.raise(
+                "ORD function expects CHAR or BOOLEAN or SET as an argument, got '"
+                + argType.description() + "'");
         END;
         RETURN result
     END CallImpl.make;
@@ -847,7 +787,7 @@ BEGIN
     NEW(call);
     initStdCall(call);
     hasArgumentWithCustomType(call);
-    RETURN makeSymbol(makeStd(JsString.make("ORD"), call))
+    RETURN makeSymbol(makeStd("ORD", call))
 END makeOrd;
 
 PROCEDURE makeChr(): Symbols.PSymbol;
@@ -868,7 +808,7 @@ BEGIN
     NEW(call);
     initStdCall(call);
     hasArgument(call, Types.basic.integer);
-    RETURN makeSymbol(makeStd(JsString.make("CHR"), call))
+    RETURN makeSymbol(makeStd("CHR", call))
 END makeChr;
 
 PROCEDURE makePack(): Symbols.PSymbol;
@@ -894,7 +834,7 @@ BEGIN
     initStdCall(call);
     hasVarArgument(call, Types.basic.real);
     hasArgument(call, Types.basic.integer);
-    RETURN makeSymbol(makeStd(JsString.make("PACK"), call))
+    RETURN makeSymbol(makeStd("PACK", call))
 END makePack;
 
 PROCEDURE makeUnpk(): Symbols.PSymbol;
@@ -912,10 +852,9 @@ PROCEDURE makeUnpk(): Symbols.PSymbol;
         x := nthArgument(args, 0);
         y := nthArgument(args, 1);
         RETURN Code.makeSimpleExpression(
-            JsString.concat(JsString.concat(
-                Operator.assign(y, Operator.log2(x), cx),
-                JsString.make("; ")),
-                Operator.divInplace(x, Operator.pow2(y), cx)),
+                Operator.assign(y, Operator.log2(x), cx) 
+                + "; "
+                + Operator.divInplace(x, Operator.pow2(y), cx),
             NIL)
     END CallImpl.make;
 BEGIN
@@ -923,12 +862,12 @@ BEGIN
     initStdCall(call);
     hasVarArgument(call, Types.basic.real);
     hasVarArgument(call, Types.basic.integer);
-    RETURN makeSymbol(makeStd(JsString.make("UNPK"), call))
+    RETURN makeSymbol(makeStd("UNPK", call))
 END makeUnpk;
 
-PROCEDURE dumpProcArgs(proc: Type): JsString.Type;
+PROCEDURE dumpProcArgs(proc: Type): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
     len: INTEGER;
     i: INTEGER;
     arg: Object.PType;
@@ -936,46 +875,37 @@ BEGIN
     len := JsArray.len(proc.mArgs);
     IF len = 0 THEN
         IF proc.mResult # NIL THEN
-            result := JsString.make("()");
-        ELSE
-            result := JsString.makeEmpty();
+            result := "()";
         END;
     ELSE
-        result := JsString.make("(");
+        result := "(";
         FOR i := 0 TO len - 1 DO
             IF i # 0 THEN
-                result := JsString.concat(result, JsString.make(", "));
+                result := result + ", ";
             END;
             arg := JsArray.at(proc.mArgs, i);
-            result := JsString.concat(
-                result, 
-                arg(Types.PProcedureArgument).type.description());
+            result := result + arg(Types.PProcedureArgument).type.description();
         END;
-        result := JsString.concat(result, JsString.make(")"));
+        result := result + ")";
     END;
     RETURN result
 END dumpProcArgs;
 
-PROCEDURE Type.description(): JsString.Type;
+PROCEDURE Type.description(): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     result := Types.typeName(SELF);
-    IF result = NIL THEN
-        result := JsString.concat(
-            JsString.make("PROCEDURE"), 
-            dumpProcArgs(SELF));
+    IF LEN(result) = 0 THEN
+        result := "PROCEDURE" + dumpProcArgs(SELF);
         IF SELF.mResult # NIL THEN
-            result := JsString.concat(JsString.concat(
-                result, 
-                JsString.make(": ")), 
-                SELF.mResult.description());
+            result := result + ": " + SELF.mResult.description();
         END;
     END;
     RETURN result
 END Type.description;
 
-PROCEDURE Type.callGenerator(cx: LanguageContext.PType; id: JsString.Type): PCallGenerator;
+PROCEDURE Type.callGenerator(cx: LanguageContext.PType; id: STRING): PCallGenerator;
     RETURN makeProcCallGenerator(cx, id, SELF)
 END Type.callGenerator;
 
@@ -993,7 +923,7 @@ PROCEDURE Type.result(): Types.PType;
     RETURN SELF.mResult
 END Type.result;
 
-PROCEDURE make*(name: JsString.Type): PType;
+PROCEDURE make*(name: STRING): PType;
 VAR
     result: PType;
 BEGIN

+ 18 - 26
src/ob/Scope.ob

@@ -3,7 +3,6 @@ IMPORT
     Errors, 
     JsArray, 
     JsMap, 
-    JsString, 
     Object, 
     Procedures := Procedure, 
     ScopeBase,
@@ -12,7 +11,7 @@ IMPORT
 TYPE
     Type = RECORD(ScopeBase.Type)
         PROCEDURE addSymbol(s: Symbols.PSymbol; exported: BOOLEAN);
-        PROCEDURE findSymbol(id: JsString.Type): Symbols.PSymbol;
+        PROCEDURE findSymbol(id: STRING): Symbols.PSymbol;
         PROCEDURE close();
 
         stdSymbols: JsMap.Type;
@@ -26,7 +25,7 @@ TYPE
     END;
 
     CompiledModule = RECORD(Types.Module)
-        PROCEDURE findSymbol(id: JsString.Type): Symbols.PFoundSymbol;
+        PROCEDURE findSymbol(id: STRING): Symbols.PFoundSymbol;
 
         exports: JsMap.Type
     END;
@@ -45,7 +44,7 @@ TYPE
 
 PROCEDURE addSymbolForType*(t: Types.PBasicType; result: JsMap.Type);
 VAR
-    name: JsString.Type;
+    name: STRING;
 BEGIN
     name := Types.typeName(t^);
     JsMap.put(result, name, Symbols.makeSymbol(name, Types.makeTypeId(t)));
@@ -85,7 +84,7 @@ BEGIN
     scope.finalizers := JsArray.make();
 END init;
 
-PROCEDURE makeCompiledModule(name: JsString.Type): PCompiledModule;
+PROCEDURE makeCompiledModule(name: STRING): PCompiledModule;
 VAR
     result: PCompiledModule;
 BEGIN
@@ -95,7 +94,7 @@ BEGIN
     RETURN result
 END makeCompiledModule;
 
-PROCEDURE makeModule*(name: JsString.Type; stdSymbols: JsMap.Type): PModule;
+PROCEDURE makeModule*(name: STRING; stdSymbols: JsMap.Type): PModule;
 VAR
     result: PModule;
 BEGIN
@@ -107,7 +106,7 @@ BEGIN
     RETURN result
 END makeModule;
 
-PROCEDURE addUnresolved*(s: Type; id: JsString.Type);
+PROCEDURE addUnresolved*(s: Type; id: STRING);
 BEGIN
     IF ~JsArray.containsString(s.unresolved, id) THEN
         JsArray.addString(s.unresolved, id);
@@ -116,7 +115,7 @@ END addUnresolved;
 
 PROCEDURE resolve*(s: Type; symbol: Symbols.PSymbol);
 VAR
-    id: JsString.Type;
+    id: STRING;
     i: INTEGER;
     info: Types.PId;
     type: Types.PType;
@@ -127,10 +126,10 @@ BEGIN
         info := symbol.info();
         type := info(Types.PTypeId).type();
         IF (type # NIL) & ~(type IS Types.PRecord) THEN
-            Errors.raise(JsString.concat(JsString.concat(
-                JsString.make("'"),
-                id),
-                JsString.make("' must be of RECORD type because it was used before in the declation of POINTER")));
+            Errors.raise(
+                "'" 
+                + id
+                + "' must be of RECORD type because it was used before in the declation of POINTER");
         END;
         JsArray.removeString(s.unresolved, i);
     END;
@@ -174,19 +173,16 @@ END close;
 
 PROCEDURE Type.addSymbol(s: Symbols.PSymbol; exported: BOOLEAN);
 VAR
-    id: JsString.Type;
+    id: STRING;
 BEGIN
     id := s.id();
     IF SELF.findSymbol(id) # NIL THEN
-        Errors.raise(JsString.concat(JsString.concat(
-            JsString.make("'"),
-            id),
-            JsString.make("' already declared")));
+        Errors.raise("'" + id + "' already declared");
     END;
     JsMap.put(SELF.symbols, id, s);
 END Type.addSymbol;
 
-PROCEDURE Type.findSymbol(id: JsString.Type): Symbols.PSymbol;
+PROCEDURE Type.findSymbol(id: STRING): Symbols.PSymbol;
 VAR
     result: Object.PType;
     void: BOOLEAN;
@@ -203,12 +199,8 @@ VAR
 BEGIN
     IF exported THEN
         info := s.info();
-        Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
-            JsString.make("cannot export from within procedure: "),
-            info.idType()),
-            JsString.make(" '")),
-            s.id()),
-            JsString.make("'")));
+        Errors.raise("cannot export from within procedure: "
+                     + info.idType() + " '" + s.id() + "'");
     END;
     SUPER(s, exported);
 END Procedure.addSymbol;
@@ -222,7 +214,7 @@ BEGIN
     RETURN result
 END makeProcedure;
 
-PROCEDURE addExport(id: JsString.Type; value: Object.PType; VAR closure: Object.Type);
+PROCEDURE addExport(id: STRING; value: Object.PType; VAR closure: Object.Type);
 VAR
     symbol: Symbols.PSymbol;
     info: Types.PId;
@@ -240,7 +232,7 @@ BEGIN
     JsMap.forEach(exports, addExport, m);
 END defineExports;
 
-PROCEDURE CompiledModule.findSymbol(id: JsString.Type): Symbols.PFoundSymbol;
+PROCEDURE CompiledModule.findSymbol(id: STRING): Symbols.PFoundSymbol;
 VAR
     s: Object.PType;
     result: Symbols.PFoundSymbol;

+ 13 - 13
src/ob/Stream.ob

@@ -1,18 +1,18 @@
 MODULE Stream;
-IMPORT JsString;
-
+IMPORT
+    String;
 CONST
     kCR* = 0AX;
 
 TYPE
     Type* = POINTER TO RECORD
-        s: JsString.Type;
+        s: STRING;
         pos: INTEGER
     END;
 
     ReaderProc = PROCEDURE(c: CHAR): BOOLEAN;
 
-PROCEDURE make*(text: JsString.Type): Type;
+PROCEDURE make*(text: STRING): Type;
 VAR result: Type;
 BEGIN
     NEW(result);
@@ -21,7 +21,7 @@ BEGIN
 END make;
 
 PROCEDURE eof*(self: Type): BOOLEAN;
-    RETURN self.pos = JsString.len(self.s)
+    RETURN self.pos = LEN(self.s)
 END eof;
 
 PROCEDURE pos*(self: Type): INTEGER;
@@ -30,27 +30,27 @@ END pos;
 
 PROCEDURE setPos*(self: Type; pos: INTEGER);
 BEGIN
-    ASSERT(pos <= JsString.len(self.s));
+    ASSERT(pos <= LEN(self.s));
     self.pos := pos
 END setPos;
 
 PROCEDURE next*(self: Type; n: INTEGER);
 BEGIN
-    ASSERT(self.pos + n <= JsString.len(self.s));
+    ASSERT(self.pos + n <= LEN(self.s));
     self.pos := self.pos + n;
 END next;
 
 PROCEDURE peekChar*(self: Type): CHAR;
 BEGIN
     ASSERT(~eof(self));
-    RETURN JsString.at(self.s, self.pos)
+    RETURN self.s[self.pos]
 END peekChar;
 
 PROCEDURE getChar*(self: Type): CHAR;
 VAR result: CHAR;
 BEGIN
     ASSERT(~eof(self));
-    result := JsString.at(self.s, self.pos);
+    result := self.s[self.pos];
     INC(self.pos);
     RETURN result
 END getChar;
@@ -60,8 +60,8 @@ VAR
     result: BOOLEAN;
     i: INTEGER;
 BEGIN
-    IF LEN(s) <= JsString.len(self.s) - self.pos THEN
-        WHILE (i < LEN(s)) & (s[i] = JsString.at(self.s, self.pos + i)) DO
+    IF LEN(s) <= LEN(self.s) - self.pos THEN
+        WHILE (i < LEN(s)) & (s[i] = self.s[self.pos + i]) DO
             INC(i)
         END;
         result := i = LEN(s);
@@ -82,10 +82,10 @@ VAR
     line: INTEGER;
     lastPos: INTEGER;
 BEGIN
-    lastPos := JsString.indexOf(self.s, kCR);
+    lastPos := String.indexOf(self.s, kCR);
     WHILE (lastPos # -1) & (lastPos < self.pos) DO
         INC(line);
-        lastPos := JsString.indexOfFrom(self.s, kCR, lastPos + 1);
+        lastPos := String.indexOfFrom(self.s, kCR, lastPos + 1);
     END;
     RETURN line + 1
 END lineNumber;

+ 44 - 0
src/ob/String.ob

@@ -0,0 +1,44 @@
+MODULE String;
+IMPORT JS;
+
+PROCEDURE fromChar*(c: CHAR): STRING;
+VAR
+    result: STRING;
+BEGIN
+    JS.do("result = JS.String.fromCharCode(c)")
+    RETURN result
+END fromChar;
+
+PROCEDURE fromInt*(i: INTEGER): STRING;
+VAR 
+    result: STRING;
+BEGIN
+    JS.do("result = '' + i");
+    RETURN result
+END fromInt;
+
+PROCEDURE indexOf*(self: STRING; c: CHAR): INTEGER;
+VAR 
+    result: INTEGER;
+BEGIN
+    JS.do("result = self.indexOf(JS.String.fromCharCode(c))")
+    RETURN result
+END indexOf;
+
+PROCEDURE indexOfFrom*(self: STRING; c: CHAR; pos: INTEGER): INTEGER;
+VAR 
+    result: INTEGER;
+BEGIN
+    JS.do("result = self.indexOf(JS.String.fromCharCode(c), pos)")
+    RETURN result
+END indexOfFrom;
+
+PROCEDURE substr*(self: STRING; pos: INTEGER; len: INTEGER): STRING;
+VAR 
+    result: STRING;
+BEGIN
+    JS.do("result = self.substr(pos, len)")
+    RETURN result
+END substr;
+
+END String.

+ 5 - 5
src/ob/Symbols.ob

@@ -1,8 +1,8 @@
 MODULE Symbols;
-IMPORT JsString, Object, ScopeBase, Types;
+IMPORT Object, ScopeBase, Types;
 TYPE
     Symbol* = RECORD(Object.Type)
-        PROCEDURE id*(): JsString.Type;
+        PROCEDURE id*(): STRING;
         PROCEDURE info*(): Types.PId;
         PROCEDURE isModule*(): BOOLEAN;
         PROCEDURE isVariable*(): BOOLEAN;
@@ -10,7 +10,7 @@ TYPE
         PROCEDURE isType*(): BOOLEAN;
         PROCEDURE isProcedure*(): BOOLEAN;
 
-        mId: JsString.Type;
+        mId: STRING;
         mInfo: Types.PId
     END;
 
@@ -26,7 +26,7 @@ TYPE
 
     PFoundSymbol* = POINTER TO FoundSymbol;
 
-PROCEDURE Symbol.id(): JsString.Type;
+PROCEDURE Symbol.id(): STRING;
     RETURN SELF.mId
 END Symbol.id;
 
@@ -62,7 +62,7 @@ PROCEDURE FoundSymbol.symbol(): PSymbol;
     RETURN SELF.mSymbol
 END FoundSymbol.symbol;
 
-PROCEDURE makeSymbol*(id: JsString.Type; info: Types.PId): PSymbol;
+PROCEDURE makeSymbol*(id: STRING; info: Types.PId): PSymbol;
 VAR
     result: PSymbol;
 BEGIN

+ 98 - 139
src/ob/Types.ob

@@ -1,28 +1,28 @@
 MODULE Types;
 IMPORT
-    Context, Errors, JS, JsArray, JsMap, JsString, Object, ScopeBase;
+    Context, Errors, JS, JsArray, JsMap, Object, ScopeBase, Str := String;
 CONST
     openArrayLength* = 0;
 
 TYPE
     Id* = RECORD(Object.Type)
-        PROCEDURE idType*(): JsString.Type
+        PROCEDURE idType*(): STRING
     END;
 
     PId* = POINTER TO Id;
     
     Type* = RECORD(Object.Type)
-        PROCEDURE description*(): JsString.Type
+        PROCEDURE description*(): STRING
     END;
     PType* = POINTER TO Type;
 
     StorageType* = RECORD(Type)    
-        PROCEDURE initializer*(cx: Context.Type): JsString.Type
+        PROCEDURE initializer*(cx: Context.Type): STRING
     END;
 
     TypeId* = RECORD(Id)
         PROCEDURE type*(): PType;
-        PROCEDURE description(): JsString.Type;
+        PROCEDURE description(): STRING;
         PROCEDURE strip();
 
         mType: PType
@@ -81,17 +81,17 @@ TYPE
     PProcedureId* = POINTER TO ProcedureId;
 
     String* = RECORD(Type)
-        s: JsString.Type
+        s: STRING
     END;
 
     PString* = POINTER TO String;
 
     NamedType* = RECORD(StorageType)
-        name*: JsString.Type
+        name*: STRING
     END;
 
     Array* = RECORD(NamedType)
-        mInitializer: JsString.Type;
+        mInitializer: STRING;
         elementsType: PType;
         len: INTEGER
     END;
@@ -117,7 +117,7 @@ TYPE
     PDefinedProcedure* = POINTER TO DefinedProcedure;
 
     ProcedureArgument* = RECORD (Object.Type)
-        PROCEDURE description(): JsString.Type;
+        PROCEDURE description(): STRING;
 
         type*: PType;
         isVar*: BOOLEAN
@@ -126,24 +126,24 @@ TYPE
     PProcedureArgument* = POINTER TO ProcedureArgument;
 
     BasicType* = RECORD(NamedType)
-        mInitializer: JsString.Type
+        mInitializer: STRING
     END;
 
     PBasicType* = POINTER TO BasicType;
 
     Field = RECORD
-        id: PROCEDURE(): JsString.Type;
+        id: PROCEDURE(): STRING;
         exported: PROCEDURE(): BOOLEAN
     END;
 
     Record* = RECORD(NamedType)
         PROCEDURE addField(f: Field; type: PType);
-        PROCEDURE findSymbol(id: JsString.Type): PType;
+        PROCEDURE findSymbol(id: STRING): PType;
         PROCEDURE finalize();
 
         fields: JsMap.Type;
         base:   PRecord;
-        cons:   JsString.Type;
+        cons:   STRING;
         scope:  ScopeBase.PType;
         notExported: JsArray.Strings
     END;
@@ -156,7 +156,7 @@ TYPE
     END;
 
     Module* = RECORD(Id)
-        name: JsString.Type
+        name: STRING
     END;
 
     PModule* = POINTER TO Module;
@@ -169,12 +169,12 @@ VAR
     numeric*: JsArray.Type;
     nil*: POINTER TO Nil;
 
-PROCEDURE TypeId.description(): JsString.Type;
+PROCEDURE TypeId.description(): STRING;
 VAR
     t: PType;
 BEGIN
     t := SELF.type();
-    RETURN JsString.concat(JsString.make("type "), t.description())
+    RETURN "type " + t.description()
 END TypeId.description;
 
 PROCEDURE TypeId.type(): PType;
@@ -196,7 +196,7 @@ BEGIN
     SELF.notExported := NIL;
 END Record.finalize;
 
-PROCEDURE initRecord*(r: PRecord; name: JsString.Type; cons: JsString.Type; scope: ScopeBase.PType);
+PROCEDURE initRecord*(r: PRecord; name: STRING; cons: STRING; scope: ScopeBase.PType);
 BEGIN
     r.name := name;
     r.cons := cons;
@@ -206,12 +206,12 @@ BEGIN
     scope.addFinalizer(finalizeRecord, r);
 END initRecord;
 
-PROCEDURE makeNonExportedRecord(cons: JsString.Type; scope: ScopeBase.PType; base: PRecord): PNonExportedRecord;
+PROCEDURE makeNonExportedRecord(cons: STRING; scope: ScopeBase.PType; base: PRecord): PNonExportedRecord;
 VAR
     result: PNonExportedRecord;
 BEGIN
     NEW(result);
-    initRecord(result, NIL, cons, scope);
+    initRecord(result, "", cons, scope);
     result.base := base;
     RETURN result
 END makeNonExportedRecord;    
@@ -250,32 +250,32 @@ BEGIN
     tId.mType := t;
 END defineTypeId;
 
-PROCEDURE typeName*(type: NamedType): JsString.Type;
+PROCEDURE typeName*(type: NamedType): STRING;
     RETURN type.name
 END typeName;
 
-PROCEDURE ProcedureId.idType(): JsString.Type;
-    RETURN JsString.make("procedure")
+PROCEDURE ProcedureId.idType(): STRING;
+    RETURN "procedure"
 END ProcedureId.idType;
 
-PROCEDURE String.description(): JsString.Type;
+PROCEDURE String.description(): STRING;
 VAR
-    prefix: JsString.Type;
+    prefix: STRING;
 BEGIN
-    IF JsString.len(SELF.s) = 1 THEN
-        prefix := JsString.make("single-");
+    IF LEN(SELF.s) = 1 THEN
+        prefix := "single-";
     ELSE
-        prefix := JsString.make("multi-");
+        prefix := "multi-";
     END;
-    RETURN JsString.concat(prefix, JsString.make("character string"))
+    RETURN prefix + "character string"
 END String.description;
 
-PROCEDURE stringValue*(s: String): JsString.Type;
+PROCEDURE stringValue*(s: String): STRING;
     RETURN s.s
 END stringValue;
 
 PROCEDURE stringLen*(s: String): INTEGER;
-    RETURN JsString.len(s.s)
+    RETURN LEN(s.s)
 END stringLen;
 
 PROCEDURE stringAsChar*(s: String; VAR c: CHAR): BOOLEAN;
@@ -284,13 +284,13 @@ VAR
 BEGIN
     result := stringLen(s) = 1;
     IF result THEN
-        c := JsString.at(s.s, 0);
+        c := s.s[0];
     END;
     RETURN result
 END stringAsChar;
 
-PROCEDURE Const.idType(): JsString.Type;
-    RETURN JsString.make("constant")
+PROCEDURE Const.idType(): STRING;
+    RETURN "constant"
 END Const.idType;
 
 PROCEDURE constType*(c: Const): PType;
@@ -301,12 +301,12 @@ PROCEDURE constValue*(c: Const): JS.var;
     RETURN c.value
 END constValue;
 
-PROCEDURE Variable.idType(): JsString.Type;
-    RETURN JsString.make("variable")
+PROCEDURE Variable.idType(): STRING;
+    RETURN "variable"
 END Variable.idType;
 
-PROCEDURE ReadOnlyVariable.idType(): JsString.Type;
-    RETURN JsString.make("read-only variable")
+PROCEDURE ReadOnlyVariable.idType(): STRING;
+    RETURN "read-only variable"
 END ReadOnlyVariable.idType;
 
 PROCEDURE VariableImpl.type(): PType;
@@ -325,36 +325,32 @@ PROCEDURE ReadOnlyVariable.isReadOnly(): BOOLEAN;
     RETURN TRUE
 END ReadOnlyVariable.isReadOnly;
 
-PROCEDURE ExportedVariable.idType(): JsString.Type;
-    RETURN JsString.make("imported variable")
+PROCEDURE ExportedVariable.idType(): STRING;
+    RETURN "imported variable"
 END ExportedVariable.idType;
 
-PROCEDURE TypeId.idType(): JsString.Type;
-    RETURN JsString.make("type")
+PROCEDURE TypeId.idType(): STRING;
+    RETURN "type"
 END TypeId.idType;
 
-PROCEDURE BasicType.description(): JsString.Type;
+PROCEDURE BasicType.description(): STRING;
     RETURN SELF.name
 END BasicType.description;
 
-PROCEDURE BasicType.initializer(cx: Context.Type): JsString.Type;
+PROCEDURE BasicType.initializer(cx: Context.Type): STRING;
     RETURN SELF.mInitializer
 END BasicType.initializer;
-(*
-PROCEDURE Nil.idType(): JsString.Type;
-    RETURN JsString.make("NIL")
-END Nil.idType;
-*)
-PROCEDURE Nil.description(): JsString.Type;
-    RETURN JsString.make("NIL")
+
+PROCEDURE Nil.description(): STRING;
+    RETURN "NIL"
 END Nil.description;
 
 PROCEDURE isInt*(t: PType): BOOLEAN;
     RETURN (t = basic.integer) OR (t = basic.uint8)
 END isInt;
 
-PROCEDURE intsDescription*(): JsString.Type;
-    RETURN JsString.make("'INTEGER' or 'BYTE'")
+PROCEDURE intsDescription*(): STRING;
+    RETURN "'INTEGER' or 'BYTE'"
 END intsDescription;
 
 PROCEDURE isString*(t: PType): BOOLEAN;
@@ -362,57 +358,43 @@ PROCEDURE isString*(t: PType): BOOLEAN;
            OR (t^ IS String)
 END isString;
 
-PROCEDURE moduleName*(m: Module): JsString.Type;
+PROCEDURE moduleName*(m: Module): STRING;
     RETURN m.name
 END moduleName;
 
-PROCEDURE makeBasic*(name: ARRAY OF CHAR; initializer: ARRAY OF CHAR): PBasicType;
+PROCEDURE makeBasic*(name: STRING; initializer: STRING): PBasicType;
 VAR
     result: PBasicType;
 BEGIN
     NEW(result);
-    result.name := JsString.make(name);
-    result.mInitializer := JsString.make(initializer);
+    result.name := name;
+    result.mInitializer := initializer;
     RETURN result
 END makeBasic;
-(*
-PROCEDURE Record.idType(): JsString.Type;
-    RETURN JsString.make("record")
-END Record.idType;
-*)
-PROCEDURE Record.description(): JsString.Type;
+
+PROCEDURE Record.description(): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
-    IF SELF.name # NIL THEN
+    IF LEN(SELF.name) # 0 THEN
         result := SELF.name;
     ELSE
-        result := JsString.make("anonymous RECORD");
+        result := "anonymous RECORD";
     END;
     RETURN result
 END Record.description;
 
-PROCEDURE Record.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.concat(JsString.concat(JsString.concat(
-        JsString.make("new "), 
-        cx.qualifyScope(SELF.scope)), 
-        SELF.cons), 
-        JsString.make("()"))
+PROCEDURE Record.initializer(cx: Context.Type): STRING;
+    RETURN "new " + cx.qualifyScope(SELF.scope) + SELF.cons + "()"
 END Record.initializer;
 
 PROCEDURE Record.addField(f: Field; type: PType);
 BEGIN
     IF JsMap.has(SELF.fields, f.id()) THEN
-        Errors.raise(JsString.concat(JsString.concat(
-            JsString.make("duplicated field: '"), 
-            f.id()), 
-            JsString.make("'")));
+        Errors.raise("duplicated field: '" + f.id() + "'");
     END;
     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()),
-            JsString.make("'")));
+        Errors.raise("base record already has field: '" + f.id() + "'");
     END;
     JsMap.put(SELF.fields, f.id(), type);
     IF ~f.exported() THEN
@@ -420,7 +402,7 @@ BEGIN
     END;
 END Record.addField;
 
-PROCEDURE Record.findSymbol(id: JsString.Type): PType;
+PROCEDURE Record.findSymbol(id: STRING): PType;
 VAR
     result: PType;
 BEGIN
@@ -443,18 +425,14 @@ PROCEDURE recordScope*(r: Record): ScopeBase.PType;
     RETURN r.scope
 END recordScope;
 
-PROCEDURE recordConstructor*(r: Record): JsString.Type;
+PROCEDURE recordConstructor*(r: Record): STRING;
     RETURN r.cons
 END recordConstructor;
 
 PROCEDURE recordOwnFields*(r: Record): JsMap.Type;
     RETURN r.fields
 END recordOwnFields;
-(*
-PROCEDURE Pointer.idType(): JsString.Type;
-    RETURN JsString.make("pointer")
-END Pointer.idType;
-*)
+
 PROCEDURE pointerBase*(p: Pointer): PRecord;
 VAR
     result: PType;
@@ -463,68 +441,55 @@ BEGIN
     RETURN result(PRecord)
 END pointerBase;
 
-PROCEDURE Pointer.description(): JsString.Type;
+PROCEDURE Pointer.description(): STRING;
 VAR
     base: PRecord;
-    result: JsString.Type;
+    result: STRING;
 BEGIN
-    IF SELF.name # NIL THEN
+    IF LEN(SELF.name) # 0 THEN
         result := SELF.name;
     ELSE
         base := pointerBase(SELF);
-        result := JsString.concat(JsString.make("POINTER TO "), base.description());
+        result := "POINTER TO " + base.description();
     END;
     RETURN result
 END Pointer.description;
 
-PROCEDURE Pointer.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.make("null")
+PROCEDURE Pointer.initializer(cx: Context.Type): STRING;
+    RETURN "null"
 END Pointer.initializer;
-(*
-PROCEDURE Array.idType(): JsString.Type;
-    RETURN JsString.make("array")
-END Array.idType;
-*)
-PROCEDURE foldArrayDimensions(a: Array; VAR sizes, of: JsString.Type);
+
+PROCEDURE foldArrayDimensions(a: Array; VAR sizes, of: STRING);
 BEGIN  
     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);
+        sizes := Str.fromInt(a.len) + ", " + sizes;
     ELSE
         IF a.len # openArrayLength THEN
-            sizes := JsString.fromInt(a.len);
+            sizes := Str.fromInt(a.len);
         END;
         of := a.elementsType.description();
     END
 END foldArrayDimensions;
 
-PROCEDURE Array.description(): JsString.Type;
+PROCEDURE Array.description(): STRING;
 VAR
-    result: JsString.Type;
-    sizes, of: JsString.Type;
+    result: STRING;
+    sizes, of: STRING;
 BEGIN
     IF SELF.elementsType = NIL THEN (* special arrays, see procedure "LEN" *)
         result := SELF.name;
     ELSE
         foldArrayDimensions(SELF, sizes, of);
-        IF sizes = NIL THEN
-            sizes := JsString.make("");
-        ELSE
-            sizes := JsString.concat(JsString.make(" "), sizes);
+        IF LEN(sizes) # 0 THEN
+            sizes := " " + sizes;
         END;
-        result := JsString.concat(JsString.concat(JsString.concat(
-            JsString.make("ARRAY"),
-            sizes),
-            JsString.make(" OF ")),
-            of);
+        result := "ARRAY" + sizes + " OF " + of;
     END;
     RETURN result
 END Array.description;
 
-PROCEDURE Array.initializer(cx: Context.Type): JsString.Type;
+PROCEDURE Array.initializer(cx: Context.Type): STRING;
     RETURN SELF.mInitializer
 END Array.initializer;
 
@@ -535,29 +500,23 @@ END arrayElementsType;
 PROCEDURE arrayLength*(a: Array): INTEGER;
     RETURN a.len
 END arrayLength;
-(*
-PROCEDURE String.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.make("null")
-END String.initializer;
-*)
-PROCEDURE Procedure.initializer(cx: Context.Type): JsString.Type;
-    RETURN JsString.make("null")
+
+PROCEDURE Procedure.initializer(cx: Context.Type): STRING;
+    RETURN "null"
 END Procedure.initializer;
 
-PROCEDURE Procedure.description(): JsString.Type;
+PROCEDURE Procedure.description(): STRING;
     RETURN SELF.name
 END Procedure.description;
 
-PROCEDURE ProcedureArgument.description(): JsString.Type;
+PROCEDURE ProcedureArgument.description(): STRING;
 VAR
-    result: JsString.Type;
+    result: STRING;
 BEGIN
     IF SELF.isVar THEN
-        result := JsString.make("VAR ");
-    ELSE
-        result := JsString.makeEmpty();
+        result := "VAR ";
     END;
-    RETURN JsString.concat(result, SELF.type.description())
+    RETURN result + SELF.type.description()
 END ProcedureArgument.description;
 
 PROCEDURE makeProcedureArgument*(type: PType; isVar: BOOLEAN): PProcedureArgument;
@@ -570,8 +529,8 @@ BEGIN
     RETURN result
 END makeProcedureArgument;
 
-PROCEDURE Module.idType(): JsString.Type;
-    RETURN JsString.make("MODULE")
+PROCEDURE Module.idType(): STRING;
+    RETURN "MODULE"
 END Module.idType;
 
 PROCEDURE makeTypeId*(type: PType): PTypeId;
@@ -591,7 +550,7 @@ BEGIN
     RETURN result
 END makeLazyTypeId;
 
-PROCEDURE makeString*(s: JsString.Type): PString;
+PROCEDURE makeString*(s: STRING): PString;
 VAR
     result: PString;
 BEGIN
@@ -601,8 +560,8 @@ BEGIN
 END makeString;
 
 PROCEDURE makeArray*(
-    name: JsString.Type;
-    initializer: JsString.Type;
+    name: STRING;
+    initializer: STRING;
     elementsType: PType;
     len: INTEGER (* see openArrayLength *)
     ): PArray;
@@ -617,7 +576,7 @@ BEGIN
     RETURN result
 END makeArray;
 
-PROCEDURE makePointer*(name: JsString.Type; base: PTypeId): PPointer;
+PROCEDURE makePointer*(name: STRING; base: PTypeId): PPointer;
 VAR
     result: PPointer;
 BEGIN
@@ -627,7 +586,7 @@ BEGIN
     RETURN result
 END makePointer;
 
-PROCEDURE makeRecord*(name: JsString.Type; cons: JsString.Type; scope: ScopeBase.PType): PRecord;
+PROCEDURE makeRecord*(name: STRING; cons: STRING; scope: ScopeBase.PType): PRecord;
 VAR
     result: PRecord;
 BEGIN
@@ -702,12 +661,12 @@ BEGIN
     RETURN result
 END makeProcedure;
 
-PROCEDURE initProcedure*(p: Procedure; name: JsString.Type);
+PROCEDURE initProcedure*(p: Procedure; name: STRING);
 BEGIN
     p.name := name;
 END initProcedure;
 
-PROCEDURE initModule*(m: Module; name: JsString.Type);
+PROCEDURE initModule*(m: Module; name: STRING);
 BEGIN
     m.name := name;
 END initModule;

+ 2 - 0
test/test_unit_eberon.js

@@ -199,6 +199,8 @@ exports.suite = {
     context(grammar.expression,
             "VAR s1, s2: STRING; a: ARRAY 10 OF CHAR;"),
     pass("s1 + s2",
+         "s1 + \"abc\"",
+         "\"abc\" + s1",
          "s1 = s2",
          "s1 # s2",
          "s1 < s2",