Browse Source

make possible to use JavaScript strings as ARRAY OF CHAR without convetion

Vladislav Folts 11 years ago
parent
commit
3516b3a649

+ 1 - 3
src/cast.js

@@ -80,9 +80,7 @@ function implicitCast(from, to, ops){
                 return function(){return new Code.Expression(v, to);};
                 return function(){return new Code.Expression(v, to);};
         }
         }
         else if (to instanceof Type.Array && to.elementsType() == Type.basic.ch)
         else if (to instanceof Type.Array && to.elementsType() == Type.basic.ch)
-            return function(context, e){
-                return new Code.Expression(context.rtl().strToArray(e.code()), to);
-            };
+            return doNoting;
     }
     }
     else if (from instanceof ArrayType && to instanceof ArrayType)
     else if (from instanceof ArrayType && to instanceof ArrayType)
         return (to.length() === undefined || to.length() === from.length())
         return (to.length() === undefined || to.length() === from.length())

+ 1 - 0
src/code.js

@@ -49,6 +49,7 @@ var Expression = Class.extend({
         this.__maxPrecedence = maxPrecedence;
         this.__maxPrecedence = maxPrecedence;
     },
     },
     code: function(){return this.__code;},
     code: function(){return this.__code;},
+    lval: function(){return this.__designator ? this.__designator.lval() : this.__code;},
     type: function(){return this.__type;},
     type: function(){return this.__type;},
     designator: function(){return this.__designator;},
     designator: function(){return this.__designator;},
     constValue: function(){return this.__constValue;},
     constValue: function(){return this.__constValue;},

+ 24 - 20
src/context.js

@@ -114,12 +114,7 @@ var ChainedContext = Class.extend({
     rtl: function(){return this.__parent.rtl();},
     rtl: function(){return this.__parent.rtl();},
     
     
     // called from oberon code
     // called from oberon code
-    raiseException: function(s){
-        var result = "";
-        for(var i = 0; i < s.length; ++i)
-            result += String.fromCharCode(s[i]);
-        throw new Errors.Error(result);
-    }
+    raiseException: function(s){throw new Errors.Error(s);}
 });
 });
 
 
 exports.Integer = ChainedContext.extend({
 exports.Integer = ChainedContext.extend({
@@ -213,14 +208,16 @@ exports.BaseType = ChainedContext.extend({
 });
 });
 
 
 var DesignatorInfo = Class.extend({
 var DesignatorInfo = Class.extend({
-    init: function(code, refCode, type, info, scope){
+    init: function(code, lval, refCode, type, info, scope){
         this.__code = code;
         this.__code = code;
+        this.__lval = lval;
         this.__refCode = refCode;
         this.__refCode = refCode;
         this.__type = type;
         this.__type = type;
         this.__info = info;
         this.__info = info;
         this.__scope = scope;
         this.__scope = scope;
     },
     },
     code: function(){return this.__code;},
     code: function(){return this.__code;},
+    lval: function(){return this.__lval;},
     refCode: function(){return this.__refCode(this.__code);},
     refCode: function(){return this.__refCode(this.__code);},
     type: function(){return this.__type;},
     type: function(){return this.__type;},
     info: function(){return this.__info;},
     info: function(){return this.__info;},
@@ -282,7 +279,8 @@ exports.Designator = ChainedContext.extend({
         this.__currentType = undefined;
         this.__currentType = undefined;
         this.__info = undefined;
         this.__info = undefined;
         this.__scope = undefined;
         this.__scope = undefined;
-        this.__code = new Code.SimpleGenerator();
+        this.__code = "";
+        this.__lval = undefined;
         this.__indexExpression = undefined;
         this.__indexExpression = undefined;
         this.__derefCode = undefined;
         this.__derefCode = undefined;
         this.__propCode = undefined;
         this.__propCode = undefined;
@@ -296,7 +294,7 @@ exports.Designator = ChainedContext.extend({
         else if (s.isVariable() || s.isConst())
         else if (s.isVariable() || s.isConst())
             this.__currentType = info.type();
             this.__currentType = info.type();
         this.__info = info;
         this.__info = info;
-        this.__code.write(code);
+        this.__code += code;
     },
     },
     handleIdent: function(id){
     handleIdent: function(id){
         var t = this.__currentType;
         var t = this.__currentType;
@@ -317,7 +315,6 @@ exports.Designator = ChainedContext.extend({
         this.__info = new Type.Variable(this.__currentType, isReadOnly);
         this.__info = new Type.Variable(this.__currentType, isReadOnly);
         this.__scope = undefined;
         this.__scope = undefined;
     },
     },
-    codeGenerator: function(){return this.__code;},
     handleExpression: function(e){this.__indexExpression = e;},
     handleExpression: function(e){this.__indexExpression = e;},
     __handleIndexExpression: function(){
     __handleIndexExpression: function(){
         var e = this.__indexExpression;
         var e = this.__indexExpression;
@@ -343,11 +340,16 @@ exports.Designator = ChainedContext.extend({
             this.__handleIndexExpression();
             this.__handleIndexExpression();
             var indexCode = this.__indexExpression.deref().code();
             var indexCode = this.__indexExpression.deref().code();
             this.__propCode = indexCode;
             this.__propCode = indexCode;
-            this.__code = new Code.SimpleGenerator(this.__derefCode + "[" + indexCode + "]");
+            var code = this.__derefCode + "[" + indexCode + "]";
+            if (this.__currentType == basicTypes.ch){
+                this.__lval = code;
+                code = this.__derefCode + ".charCodeAt(" + indexCode + ")";
+            }
+            this.__code += code;
             }
             }
         if (s == "[" || s == ","){
         if (s == "[" || s == ","){
-            this.__derefCode = this.__code.result();
-            this.__code = new Code.SimpleGenerator();
+            this.__derefCode = this.__code;
+            this.__code = "";
         }
         }
         else if (s == "^"){
         else if (s == "^"){
             this.__handleDeref();
             this.__handleDeref();
@@ -382,8 +384,8 @@ exports.Designator = ChainedContext.extend({
 
 
         var baseType = type instanceof Type.Pointer ? type.baseType() : type;
         var baseType = type instanceof Type.Pointer ? type.baseType() : type;
         var castName = this.qualifyScope(baseType.scope()) + baseType.cons();
         var castName = this.qualifyScope(baseType.scope()) + baseType.cons();
-        var code = this.rtl().typeGuard(this.__code.result(), castName);
-        this.__code = new Code.SimpleGenerator(code);
+        var code = this.rtl().typeGuard(this.__code, castName);
+        this.__code = code;
 
 
         this.__currentType = type;
         this.__currentType = type;
     },
     },
@@ -396,17 +398,17 @@ exports.Designator = ChainedContext.extend({
                 : t.description();
                 : t.description();
             throw new Errors.Error("type '" + typeDesc + "' has no '" + id + "' field");
             throw new Errors.Error("type '" + typeDesc + "' has no '" + id + "' field");
         }
         }
-        this.__derefCode = this.__code.result();
+        this.__derefCode = this.__code;
         this.__propCode = "\"" + id + "\"";
         this.__propCode = "\"" + id + "\"";
-        this.__code.write("." + id);
+        this.__code += "." + id;
         this.__currentType = fieldType;
         this.__currentType = fieldType;
     },
     },
     endParse: function(){
     endParse: function(){
-        var code = this.__code.result();
+        var code = this.__code;
         var self = this;
         var self = this;
         var refCode = function(code){return self.__makeRefCode(code);};
         var refCode = function(code){return self.__makeRefCode(code);};
         this.parent().setDesignator(
         this.parent().setDesignator(
-            new DesignatorInfo(code, refCode, this.__currentType, this.__info, this.__scope));
+            new DesignatorInfo(code, this.__lval ? this.__lval : code, refCode, this.__currentType, this.__info, this.__scope));
     },
     },
     __makeRefCode: function(code){
     __makeRefCode: function(code){
         if (   this.__currentType instanceof Type.Array
         if (   this.__currentType instanceof Type.Array
@@ -683,12 +685,14 @@ exports.ArrayDecl = HandleSymbolAsType.extend({
         var initializer = type instanceof Type.Array || type instanceof Type.Record
         var initializer = type instanceof Type.Array || type instanceof Type.Record
             ? "function(){return " + type.initializer(this) + ";}"
             ? "function(){return " + type.initializer(this) + ";}"
             : type.initializer(this);
             : type.initializer(this);
+        var isCharArray = (type == basicTypes.ch);
         var dimensions = "";
         var dimensions = "";
         for(var i = this.__dimensions.length; i-- ;){
         for(var i = this.__dimensions.length; i-- ;){
             var length = this.__dimensions[i];
             var length = this.__dimensions[i];
             dimensions = length + (dimensions.length ? ", " + dimensions : "");
             dimensions = length + (dimensions.length ? ", " + dimensions : "");
             var arrayInit = !i
             var arrayInit = !i
-                ? this.rtl().makeArray(dimensions + ", " + initializer)
+                ? isCharArray ? this.rtl().makeCharArray(dimensions)
+                              : this.rtl().makeArray(dimensions + ", " + initializer)
                 : undefined;
                 : undefined;
             type = new Type.Array("ARRAY OF " + type.name()
             type = new Type.Array("ARRAY OF " + type.name()
                                , arrayInit
                                , arrayInit

+ 0 - 0
src/lexer.js


+ 1 - 1
src/oberon.js/JsString.js

@@ -10,7 +10,7 @@ function make(s/*ARRAY OF CHAR*/){
 	var i = 0;
 	var i = 0;
 	result = '';
 	result = '';
 	for (i = 0; i <= s.length - 1 | 0; ++i){
 	for (i = 0; i <= s.length - 1 | 0; ++i){
-		result += JS.String.fromCharCode(s[i]);
+		result += JS.String.fromCharCode(s.charCodeAt(i));
 	}
 	}
 	return result;
 	return result;
 }
 }

+ 11 - 11
src/oberon.js/Lexer.js

@@ -17,7 +17,7 @@ var Context = RTL$.extend({
 });
 });
 var Literal = RTL$.extend({
 var Literal = RTL$.extend({
 	init: function Literal(){
 	init: function Literal(){
-		this.s = RTL$.makeArray(1, 0);
+		this.s = RTL$.makeCharArray(1);
 	}
 	}
 });
 });
 var reservedWords = null;
 var reservedWords = null;
@@ -64,7 +64,7 @@ function handleLiteral(context/*Context*/, s/*ARRAY OF CHAR*/){
 function point(stream/*Type*/, context/*Context*/){
 function point(stream/*Type*/, context/*Context*/){
 	var result = false;
 	var result = false;
 	if (!Stream.eof(stream) && Stream.getChar(stream) == 46 && (Stream.eof(stream) || Stream.peekChar(stream) != 46)){
 	if (!Stream.eof(stream) && Stream.getChar(stream) == 46 && (Stream.eof(stream) || Stream.peekChar(stream) != 46)){
-		result = handleLiteral(context, RTL$.strToArray("."));
+		result = handleLiteral(context, ".");
 	}
 	}
 	return result;
 	return result;
 }
 }
@@ -77,7 +77,7 @@ function string(stream/*Type*/, context/*Context*/){
 		c = Stream.getChar(stream);
 		c = Stream.getChar(stream);
 		if (c == 34){
 		if (c == 34){
 			if (!Stream.eof(stream)){
 			if (!Stream.eof(stream)){
-				s = JsString.make(RTL$.strToArray(""));
+				s = JsString.make("");
 				c = Stream.getChar(stream);
 				c = Stream.getChar(stream);
 				while (true){
 				while (true){
 					if (c != 34 && !Stream.eof(stream)){
 					if (c != 34 && !Stream.eof(stream)){
@@ -89,7 +89,7 @@ function string(stream/*Type*/, context/*Context*/){
 				}
 				}
 			}
 			}
 			if (s == null || c != 34){
 			if (s == null || c != 34){
-				context.raiseException(RTL$.strToArray("unexpected end of string"));
+				context.raiseException("unexpected end of string");
 			}
 			}
 			context.handleString(s);
 			context.handleString(s);
 			result = true;
 			result = true;
@@ -120,7 +120,7 @@ function ident(stream/*Type*/, context/*Context*/){
 	if (!Stream.eof(stream)){
 	if (!Stream.eof(stream)){
 		c = Stream.getChar(stream);
 		c = Stream.getChar(stream);
 		if (isLetter(c)){
 		if (isLetter(c)){
-			s = JsString.make(RTL$.strToArray(""));
+			s = JsString.make("");
 			while (true){
 			while (true){
 				if (!Stream.eof(stream) && (isLetter(c) || isDigit(c))){
 				if (!Stream.eof(stream) && (isLetter(c) || isDigit(c))){
 					s = JsString.appendChar(s, c);
 					s = JsString.appendChar(s, c);
@@ -147,14 +147,14 @@ function ident(stream/*Type*/, context/*Context*/){
 
 
 function skipComment(stream/*Type*/, context/*Context*/){
 function skipComment(stream/*Type*/, context/*Context*/){
 	var result = false;
 	var result = false;
-	if (Stream.peekStr(stream, RTL$.strToArray(commentBegin))){
+	if (Stream.peekStr(stream, commentBegin)){
 		Stream.next(stream, commentBegin.length);
 		Stream.next(stream, commentBegin.length);
 		while (true){
 		while (true){
-			if (!Stream.peekStr(stream, RTL$.strToArray(commentEnd))){
+			if (!Stream.peekStr(stream, commentEnd)){
 				if (!skipComment(stream, context)){
 				if (!skipComment(stream, context)){
 					Stream.next(stream, 1);
 					Stream.next(stream, 1);
 					if (Stream.eof(stream)){
 					if (Stream.eof(stream)){
-						context.raiseException(RTL$.strToArray("comment was not closed"));
+						context.raiseException("comment was not closed");
 					}
 					}
 				}
 				}
 			} else break;
 			} else break;
@@ -193,14 +193,14 @@ function literal(l/*Literal*/, stream/*Type*/, context/*Context*/){
 	var result = false;
 	var result = false;
 	if (Stream.peekStr(stream, l.s)){
 	if (Stream.peekStr(stream, l.s)){
 		Stream.next(stream, l.s.length);
 		Stream.next(stream, l.s.length);
-		if (context.isLexem != null && context.isLexem() || !isLetter(l.s[l.s.length - 1 | 0]) || Stream.eof(stream) || !isLetter(Stream.peekChar(stream)) && !isDigit(Stream.peekChar(stream))){
+		if (context.isLexem != null && context.isLexem() || !isLetter(l.s.charCodeAt(l.s.length - 1 | 0)) || Stream.eof(stream) || !isLetter(Stream.peekChar(stream)) && !isDigit(Stream.peekChar(stream))){
 			result = handleLiteral(context, l.s);
 			result = handleLiteral(context, l.s);
 		}
 		}
 	}
 	}
 	return result;
 	return result;
 }
 }
-reservedWords = JsString.make(RTL$.strToArray("ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONST MODULE UNTIL DIV NIL VAR DO OF WHILE ELSE OR ELSIF POINTER END PROCEDURE FALSE RECORD FOR REPEAT IF RETURN"));
-jsReservedWords = JsString.make(RTL$.strToArray("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 Math"));
+reservedWords = JsString.make("ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONST MODULE UNTIL DIV NIL VAR DO OF WHILE ELSE OR ELSIF POINTER END PROCEDURE FALSE RECORD FOR REPEAT IF RETURN");
+jsReservedWords = JsString.make("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 Math");
 exports.digit = digit;
 exports.digit = digit;
 exports.hexDigit = hexDigit;
 exports.hexDigit = hexDigit;
 exports.point = point;
 exports.point = point;

+ 1 - 1
src/oberon.js/Stream.js

@@ -50,7 +50,7 @@ function peekStr(self/*Type*/, s/*ARRAY OF CHAR*/){
 	var i = 0;
 	var i = 0;
 	if (s.length <= (JsString.len(self.s) - self.pos | 0)){
 	if (s.length <= (JsString.len(self.s) - self.pos | 0)){
 		while (true){
 		while (true){
-			if (i < s.length && s[i] == JsString.at(self.s, self.pos + i | 0)){
+			if (i < s.length && s.charCodeAt(i) == JsString.at(self.s, self.pos + i | 0)){
 				++i;
 				++i;
 			} else break;
 			} else break;
 		}
 		}

+ 1 - 1
src/oberon/JsString.ob

@@ -11,7 +11,7 @@ VAR
 BEGIN
 BEGIN
     JS.do("result = ''");
     JS.do("result = ''");
     FOR i := 0 TO LEN(s) - 1 DO
     FOR i := 0 TO LEN(s) - 1 DO
-        JS.do("result += JS.String.fromCharCode(s[i])")
+        JS.do("result += JS.String.fromCharCode(s.charCodeAt(i))")
     END
     END
     RETURN result
     RETURN result
 END make;
 END make;

+ 0 - 0
src/oberon/String.ob


+ 0 - 0
src/oberon/TestString.ob


+ 1 - 1
src/operator.js

@@ -87,7 +87,7 @@ function assign(left, right, context){
     if (!(info instanceof Type.Variable) || info.isReadOnly())
     if (!(info instanceof Type.Variable) || info.isReadOnly())
         throw new Errors.Error("cannot assign to " + info.idType());
         throw new Errors.Error("cannot assign to " + info.idType());
 
 
-    var leftCode = left.code();
+    var leftCode = left.lval();
     var leftType = left.type();
     var leftType = left.type();
     var rightCode = right.code();
     var rightCode = right.code();
     var rightType = right.type();
     var rightType = right.type();

+ 1 - 2
src/parser.js

@@ -4,10 +4,9 @@ var assert = require("assert.js").ok;
 var Errors = require("errors.js");
 var Errors = require("errors.js");
 var Lexer = require("oberon.js/lexer.js");
 var Lexer = require("oberon.js/lexer.js");
 var Stream = require("oberon.js/Stream.js");
 var Stream = require("oberon.js/Stream.js");
-var Rtl = require("rtl.js");
 
 
 function literal(s){
 function literal(s){
-	var l = Lexer.makeLiteral(Rtl.strToArray(s));
+	var l = Lexer.makeLiteral(s);
 	return function(stream, context){
 	return function(stream, context){
 		return Lexer.literal(l, stream, context);
 		return Lexer.literal(l, stream, context);
 	};
 	};

+ 32 - 7
src/rtl.js

@@ -41,6 +41,37 @@ var impl = {
                 result[i] = this.makeArray.apply(this, forward);
                 result[i] = this.makeArray.apply(this, forward);
         return result;
         return result;
     },
     },
+    makeCharArray: function(/*dimensions*/){
+        var forward = Array.prototype.slice.call(arguments);
+        var length = forward.pop();
+
+        if (!forward.length)
+            return makeCharArray(length);
+
+        function makeCharArray(length){
+            var result = new Uint16Array(length);
+            result.charCodeAt = function(i){return this[i];};
+            return result;
+        }
+
+        function makeArray(){
+            var forward = Array.prototype.slice.call(arguments);
+            var result = new Array(forward.shift());
+            var i;
+            if (forward.length == 1){
+                var init = forward[0];
+                for(i = 0; i < result.length; ++i)
+                    result[i] = init();
+            }
+            else
+                for(i = 0; i < result.length; ++i)
+                    result[i] = makeArray.apply(undefined, forward);
+            return result;
+        }
+
+        forward.push(makeCharArray.bind(undefined, length));
+        return makeArray.apply(undefined, forward);
+    },
     makeSet: function(/*...*/){
     makeSet: function(/*...*/){
         var result = 0;
         var result = 0;
         
         
@@ -82,17 +113,11 @@ var impl = {
         for(i = s.length; i < a.length; ++i)
         for(i = s.length; i < a.length; ++i)
             a[i] = 0;
             a[i] = 0;
     },
     },
-    strToArray: function(s){
-        var result = new Array(s.length);
-        for(var i = 0; i < s.length; ++i)
-            result[i] = s.charCodeAt(i);
-        return result;
-    },
     strCmp: function(s1, s2){
     strCmp: function(s1, s2){
         var cmp = 0;
         var cmp = 0;
         var i = 0;
         var i = 0;
         while (!cmp && i < s1.length && i < s2.length){
         while (!cmp && i < s1.length && i < s2.length){
-            cmp = s1[i] - s2[i];
+            cmp = s1.charCodeAt(i) - s2.charCodeAt(i);
             ++i;
             ++i;
         }
         }
         return cmp ? cmp : s1.length - s2.length;
         return cmp ? cmp : s1.length - s2.length;

+ 0 - 5
test/compile_oberon_source.cmd

@@ -1,5 +0,0 @@
-@SET NODE_PATH=%~dp0../snapshot/oberon.js
-
-cd %~dp0../src/oberon
-call %~dp0/run_nodejs.cmd %~dp0../snapshot/oc_nodejs.js %~dp0../src/oberon.js %~dp0../src/oberon/Stream.ob
-cd %~dp0

+ 27 - 14
test/expected/blur.js

@@ -1,21 +1,34 @@
 var RTL$ = {
 var RTL$ = {
-    makeArray: function (/*dimensions, initializer*/){
+    makeCharArray: function (/*dimensions*/){
         var forward = Array.prototype.slice.call(arguments);
         var forward = Array.prototype.slice.call(arguments);
-        var result = new Array(forward.shift());
-        var i;
-        if (forward.length == 1){
-            var init = forward[0];
-            if (typeof init == "function")
+        var length = forward.pop();
+
+        if (!forward.length)
+            return makeCharArray(length);
+
+        function makeCharArray(length){
+            var result = new Uint16Array(length);
+            result.charCodeAt = function(i){return this[i];};
+            return result;
+        }
+
+        function makeArray(){
+            var forward = Array.prototype.slice.call(arguments);
+            var result = new Array(forward.shift());
+            var i;
+            if (forward.length == 1){
+                var init = forward[0];
                 for(i = 0; i < result.length; ++i)
                 for(i = 0; i < result.length; ++i)
                     result[i] = init();
                     result[i] = init();
+            }
             else
             else
                 for(i = 0; i < result.length; ++i)
                 for(i = 0; i < result.length; ++i)
-                    result[i] = init;
+                    result[i] = makeArray.apply(undefined, forward);
+            return result;
         }
         }
-        else
-            for(i = 0; i < result.length; ++i)
-                result[i] = this.makeArray.apply(this, forward);
-        return result;
+
+        forward.push(makeCharArray.bind(undefined, length));
+        return makeArray.apply(undefined, forward);
     }
     }
 };
 };
 var Blur = function (){
 var Blur = function (){
@@ -25,7 +38,7 @@ var H = 480;
 var H1 = 480 - 3 | 0;
 var H1 = 480 - 3 | 0;
 var N = 13;
 var N = 13;
 var Frames = 1;
 var Frames = 1;
-var a = RTL$.makeArray(1920, 480, 0);var b = RTL$.makeArray(1920, 480, 0);
+var a = RTL$.makeCharArray(1920, 480);var b = RTL$.makeCharArray(1920, 480);
 var time = 0;
 var time = 0;
 
 
 function Blur2DArray(){
 function Blur2DArray(){
@@ -37,14 +50,14 @@ function Blur2DArray(){
 			for (y = 1; y <= H - 2 | 0; ++y){
 			for (y = 1; y <= H - 2 | 0; ++y){
 				for (x = 1; x <= W - 2 | 0; ++x){
 				for (x = 1; x <= W - 2 | 0; ++x){
 					for (color = 0; color <= 2; ++color){
 					for (color = 0; color <= 2; ++color){
-						b[(x * 3 | 0) + color | 0][y] = (((a[(x * 3 | 0) + color | 0][y + 1 | 0] + a[(x * 3 | 0) + color | 0][y - 1 | 0] | 0) + a[(x - 1 | 0) * 3 | 0][y] | 0) + a[(x + 1 | 0) * 3 | 0][y] | 0) / 4 | 0;
+						b[(x * 3 | 0) + color | 0][y] = (((a[(x * 3 | 0) + color | 0].charCodeAt(y + 1 | 0) + a[(x * 3 | 0) + color | 0].charCodeAt(y - 1 | 0) | 0) + a[(x - 1 | 0) * 3 | 0].charCodeAt(y) | 0) + a[(x + 1 | 0) * 3 | 0].charCodeAt(y) | 0) / 4 | 0;
 					}
 					}
 				}
 				}
 			}
 			}
 			for (y = 1; y <= H - 2 | 0; ++y){
 			for (y = 1; y <= H - 2 | 0; ++y){
 				for (x = 1; x <= W - 2 | 0; ++x){
 				for (x = 1; x <= W - 2 | 0; ++x){
 					for (color = 0; color <= 2; ++color){
 					for (color = 0; color <= 2; ++color){
-						a[(x * 3 | 0) + color | 0][y] = (((b[(x * 3 | 0) + color | 0][y + 1 | 0] + b[(x * 3 | 0) + color | 0][y - 1 | 0] | 0) + b[(x - 1 | 0) * 3 | 0][y] | 0) + b[(x + 1 | 0) * 3 | 0][y] | 0) / 4 | 0;
+						a[(x * 3 | 0) + color | 0][y] = (((b[(x * 3 | 0) + color | 0].charCodeAt(y + 1 | 0) + b[(x * 3 | 0) + color | 0].charCodeAt(y - 1 | 0) | 0) + b[(x - 1 | 0) * 3 | 0].charCodeAt(y) | 0) + b[(x + 1 | 0) * 3 | 0].charCodeAt(y) | 0) / 4 | 0;
 					}
 					}
 				}
 				}
 			}
 			}

+ 32 - 1
test/expected/len.js

@@ -17,6 +17,37 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
                 result[i] = this.makeArray.apply(this, forward);
         return result;
         return result;
     },
     },
+    makeCharArray: function (/*dimensions*/){
+        var forward = Array.prototype.slice.call(arguments);
+        var length = forward.pop();
+
+        if (!forward.length)
+            return makeCharArray(length);
+
+        function makeCharArray(length){
+            var result = new Uint16Array(length);
+            result.charCodeAt = function(i){return this[i];};
+            return result;
+        }
+
+        function makeArray(){
+            var forward = Array.prototype.slice.call(arguments);
+            var result = new Array(forward.shift());
+            var i;
+            if (forward.length == 1){
+                var init = forward[0];
+                for(i = 0; i < result.length; ++i)
+                    result[i] = init();
+            }
+            else
+                for(i = 0; i < result.length; ++i)
+                    result[i] = makeArray.apply(undefined, forward);
+            return result;
+        }
+
+        forward.push(makeCharArray.bind(undefined, length));
+        return makeArray.apply(undefined, forward);
+    },
     assert: function (condition, code){
     assert: function (condition, code){
         if (!condition)
         if (!condition)
             throw new Error("assertion failed"
             throw new Error("assertion failed"
@@ -27,7 +58,7 @@ var m = function (){
 var s1 = "\"";
 var s1 = "\"";
 var a1 = RTL$.makeArray(10, 0);
 var a1 = RTL$.makeArray(10, 0);
 var a2 = RTL$.makeArray(15, false);
 var a2 = RTL$.makeArray(15, false);
-var a3 = RTL$.makeArray(20, 0);
+var a3 = RTL$.makeCharArray(20);
 var i = 0;
 var i = 0;
 
 
 function p1(a/*ARRAY OF INTEGER*/){
 function p1(a/*ARRAY OF INTEGER*/){

+ 39 - 29
test/expected/string.js

@@ -1,21 +1,34 @@
 var RTL$ = {
 var RTL$ = {
-    makeArray: function (/*dimensions, initializer*/){
+    makeCharArray: function (/*dimensions*/){
         var forward = Array.prototype.slice.call(arguments);
         var forward = Array.prototype.slice.call(arguments);
-        var result = new Array(forward.shift());
-        var i;
-        if (forward.length == 1){
-            var init = forward[0];
-            if (typeof init == "function")
+        var length = forward.pop();
+
+        if (!forward.length)
+            return makeCharArray(length);
+
+        function makeCharArray(length){
+            var result = new Uint16Array(length);
+            result.charCodeAt = function(i){return this[i];};
+            return result;
+        }
+
+        function makeArray(){
+            var forward = Array.prototype.slice.call(arguments);
+            var result = new Array(forward.shift());
+            var i;
+            if (forward.length == 1){
+                var init = forward[0];
                 for(i = 0; i < result.length; ++i)
                 for(i = 0; i < result.length; ++i)
                     result[i] = init();
                     result[i] = init();
+            }
             else
             else
                 for(i = 0; i < result.length; ++i)
                 for(i = 0; i < result.length; ++i)
-                    result[i] = init;
+                    result[i] = makeArray.apply(undefined, forward);
+            return result;
         }
         }
-        else
-            for(i = 0; i < result.length; ++i)
-                result[i] = this.makeArray.apply(this, forward);
-        return result;
+
+        forward.push(makeCharArray.bind(undefined, length));
+        return makeArray.apply(undefined, forward);
     },
     },
     assignArrayFromString: function (a, s){
     assignArrayFromString: function (a, s){
         var i;
         var i;
@@ -24,12 +37,6 @@ var RTL$ = {
         for(i = s.length; i < a.length; ++i)
         for(i = s.length; i < a.length; ++i)
             a[i] = 0;
             a[i] = 0;
     },
     },
-    strToArray: function (s){
-        var result = new Array(s.length);
-        for(var i = 0; i < s.length; ++i)
-            result[i] = s.charCodeAt(i);
-        return result;
-    },
     assert: function (condition, code){
     assert: function (condition, code){
         if (!condition)
         if (!condition)
             throw new Error("assertion failed"
             throw new Error("assertion failed"
@@ -39,7 +46,7 @@ var RTL$ = {
         var cmp = 0;
         var cmp = 0;
         var i = 0;
         var i = 0;
         while (!cmp && i < s1.length && i < s2.length){
         while (!cmp && i < s1.length && i < s2.length){
-            cmp = s1[i] - s2[i];
+            cmp = s1.charCodeAt(i) - s2.charCodeAt(i);
             ++i;
             ++i;
         }
         }
         return cmp ? cmp : s1.length - s2.length;
         return cmp ? cmp : s1.length - s2.length;
@@ -56,8 +63,8 @@ var s7 = "\t";
 var s8 = "\f";
 var s8 = "\f";
 var s9 = "\\";
 var s9 = "\\";
 var ch1 = 0;
 var ch1 = 0;
-var a1 = RTL$.makeArray(15, 0);
-var a2 = RTL$.makeArray(3, 0);
+var a1 = RTL$.makeCharArray(15);
+var a2 = RTL$.makeCharArray(3);
 
 
 function p1(s/*ARRAY OF CHAR*/){
 function p1(s/*ARRAY OF CHAR*/){
 }
 }
@@ -68,19 +75,22 @@ ch1 = 34;
 RTL$.assignArrayFromString(a1, s1);
 RTL$.assignArrayFromString(a1, s1);
 RTL$.assignArrayFromString(a2, s2);
 RTL$.assignArrayFromString(a2, s2);
 RTL$.assignArrayFromString(a1, s2);
 RTL$.assignArrayFromString(a1, s2);
-p1(RTL$.strToArray(s1));
-p1(RTL$.strToArray(s2));
+p1(s1);
+p1(s2);
 p2(34);
 p2(34);
+p2(a1.charCodeAt(0));
 RTL$.assert(ch1 == 34);
 RTL$.assert(ch1 == 34);
 RTL$.assert(34 == ch1);
 RTL$.assert(34 == ch1);
-RTL$.assert(RTL$.strCmp(RTL$.strToArray("abc"), RTL$.strToArray("abc")) == 0);
+RTL$.assert(RTL$.strCmp("abc", "abc") == 0);
 RTL$.assert(RTL$.strCmp(a1, a2) == 0);
 RTL$.assert(RTL$.strCmp(a1, a2) == 0);
 RTL$.assert(RTL$.strCmp(a1, a2) != 0);
 RTL$.assert(RTL$.strCmp(a1, a2) != 0);
 RTL$.assert(RTL$.strCmp(a1, a2) > 0);
 RTL$.assert(RTL$.strCmp(a1, a2) > 0);
-RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) > 0);
-RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) >= 0);
-RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) != 0);
-RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) < 0);
-RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) <= 0);
-RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) != 0);
+RTL$.assert(RTL$.strCmp(a1, s1) > 0);
+RTL$.assert(RTL$.strCmp(a1, s1) >= 0);
+RTL$.assert(RTL$.strCmp(a1, s1) != 0);
+RTL$.assert(RTL$.strCmp(s1, a1) < 0);
+RTL$.assert(RTL$.strCmp(s1, a1) <= 0);
+RTL$.assert(RTL$.strCmp(s1, a1) != 0);
+a1[0] = 97;
+a1[1] = a1.charCodeAt(0);
 }();
 }();

+ 9 - 0
test/input/run/string.ob

@@ -3,6 +3,7 @@ MODULE m;
 VAR
 VAR
 	a3: ARRAY 3 OF CHAR;
 	a3: ARRAY 3 OF CHAR;
 	a4: ARRAY 4 OF CHAR;
 	a4: ARRAY 4 OF CHAR;
+    a34: ARRAY 3, 4 OF CHAR;
 
 
 BEGIN
 BEGIN
 	ASSERT("abc" = "abc");
 	ASSERT("abc" = "abc");
@@ -13,4 +14,12 @@ BEGIN
 	a4 := "abc";
 	a4 := "abc";
 	ASSERT(a3 < a4); (*a4 has extra 0*)
 	ASSERT(a3 < a4); (*a4 has extra 0*)
 
 
+    a34[0] := "abcd";
+    a34[1][0] := "c";
+    ASSERT(a34[0][0] = "a");
+    ASSERT(a34[0][1] = "b");
+    ASSERT(a34[0][2] = "c");
+    ASSERT(a34[0][3] = "d");
+    ASSERT(a34[1][0] = "c");
+
 END m.
 END m.

+ 3 - 0
test/input/string.ob

@@ -30,6 +30,7 @@ BEGIN
 	p1(s1);
 	p1(s1);
 	p1(s2);
 	p1(s2);
     p2(s1);
     p2(s1);
+	p2(a1[0]);
 
 
     ASSERT(ch1 = s1);
     ASSERT(ch1 = s1);
 	ASSERT(s1 = ch1);
 	ASSERT(s1 = ch1);
@@ -47,4 +48,6 @@ BEGIN
 	ASSERT(s1 <= a1);
 	ASSERT(s1 <= a1);
 	ASSERT(s1 # a1);
 	ASSERT(s1 # a1);
 
 
+	a1[0] := "a";
+	a1[1] := a1[0];
 END m.
 END m.

+ 3 - 0
test/recompile_oberon.js.cmd

@@ -0,0 +1,3 @@
+cd %~dp0../src/oberon
+call %~dp0/run_nodejs.cmd %~dp0../src/oc_nodejs.js %~dp0/_out %~dp0../src/oberon/Lexer.ob
+cd %~dp0

+ 5 - 1
test/test_compile.js

@@ -64,7 +64,11 @@ function expectError(src, dirs){
 }
 }
 
 
 function run(src, dirs){
 function run(src, dirs){
-    eval(compile(src));
+    var result = compile(src);
+    var resultName = path.basename(src).replace(".ob", ".js");
+    var resultPath = path.join(dirs.output, resultName);
+    fs.writeFileSync(resultPath, result);
+    require(resultPath);
 }
 }
 
 
 function makeTest(test, src, dirs){
 function makeTest(test, src, dirs){