Sfoglia il codice sorgente

Rewrite stream.js on oberon.

Vladislav Folts 11 anni fa
parent
commit
163417886e

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 _out/
+snapshot/
 test/output/
 *.sublime-project
 *.sublime-workspace

+ 2 - 1
browser/linkjs.py

@@ -31,7 +31,7 @@ def process(path, out, resolved, resolving, dirs):
 	if module_name in resolving:
 		raise Exception('cyclic import detected: "%s"' % module_name)
 	result = 'imports["%s"] = {};\n' % path
-	result += '(function %s(exports){\n' % module_name
+	result += '(function module$%s(exports){\n' % module_name
 	src_path = resolve_path(path, dirs)
 	with open(src_path) as f:
 		for l in f:
@@ -58,6 +58,7 @@ def link(input_path, output_path, dirs, version = None):
 		prolog = ""
 		if not version is None:
 			prolog += 'var buildVersion = %s;\n' % encode_to_js_string(version)
+		prolog += "var GLOBAL = this;\n"
 		prolog += "var imports = {};\n"
 		prolog += 'function require(module){return imports[module];}\n'
 		out.write(prolog)

+ 0 - 4
browser/oberonjs.html

@@ -50,10 +50,6 @@ END test.
 <p><a href="https://github.com/vladfolts/oberonjs">Development</a></p>
 <p id="version"></p>
 
-<script>
-    function require(){}
-</script>
-
 <script src="oc.js"></script>
 
 <script>

+ 1 - 1
build.py

@@ -74,7 +74,7 @@ def build(out, use_git):
     if not os.path.exists(out):
         os.mkdir(out)
 
-    link('oc.js', os.path.join(out, 'oc.js'), ['src'], version)
+    link('oc.js', os.path.join(out, 'oc.js'), ['src', 'src/oberon.js'], version)
     copy('browser/oberonjs.html', out)
     for d in ['codemirror', 'jslibs']:
         copytree(os.path.join('browser', d), os.path.join(out, d))

+ 34 - 23
src/lexer.js

@@ -1,11 +1,15 @@
 "use strict";
 
 var Errors = require("errors.js");
+var Stream = require("oberon.js/Stream.js");
 
 function isDigit(c) {return c >= '0' && c <= '9';}
 
 exports.digit = function(stream, context){
-    var c = stream.getChar();
+    if (Stream.eof(stream))
+        return false;
+
+    var c = Stream.getChar(stream);
     if (!isDigit(c))
         return false;
     context.handleChar(c);
@@ -13,7 +17,7 @@ exports.digit = function(stream, context){
 };
 
 exports.hexDigit = function(stream, context){
-    var c = stream.getChar();
+    var c = Stream.getChar(stream);
     if (!isDigit(c) && (c < 'A' || c > 'F'))
         return false;
     context.handleChar(c);
@@ -21,8 +25,9 @@ exports.hexDigit = function(stream, context){
 };
 
 exports.point = function(stream, context){
-    if (stream.getChar() != '.'
-        || stream.peekChar() == '.') // not a diapason ".."
+    if (Stream.eof(stream) 
+        || Stream.getChar(stream) != '.'
+        || (!Stream.eof(stream) && Stream.peekChar(stream) == '.')) // not a diapason ".."
         return false;
     context.handleLiteral(".");
     return true;
@@ -37,13 +42,16 @@ exports.character = function(stream, context){
 };
 
 function string(stream, context){
-    var c = stream.getChar();
+    if (Stream.eof(stream))
+        return false;
+
+    var c = Stream.getChar(stream);
     if (c != '"')
         return false;
 
     var result = "";
     var parsed = false;
-    stream.read(function(c){
+    Stream.read(stream, function(c){
         if (c == '"'){
             parsed = true;
             return false;
@@ -54,7 +62,7 @@ function string(stream, context){
     if (!parsed)
         throw new Errors.Error("unexpected end of string");
 
-    stream.next(1);
+    Stream.next(stream, 1);
     context.handleString(result);
     return true;
 }
@@ -76,12 +84,12 @@ var jsReservedWords
        ];
 
 exports.ident = function(stream, context){
-    if (!isLetter(stream.peekChar()))
+    if (Stream.eof(stream) || !isLetter(Stream.peekChar(stream)))
         return false;
     
-    var savePos = stream.pos();
+    var savePos = Stream.pos(stream);
     var result = "";
-    stream.read(function(c){
+    Stream.read(stream, function(c){
         if (!isLetter(c) && !isDigit(c) /*&& c != '_'*/)
             return false;
         result += c;
@@ -89,7 +97,7 @@ exports.ident = function(stream, context){
     });
 
     if (reservedWords.indexOf(result) != -1){
-        stream.setPos(savePos);
+        Stream.setPos(stream, savePos);
         return false;
     }
     if (jsReservedWords.indexOf(result) != -1)
@@ -100,17 +108,17 @@ exports.ident = function(stream, context){
 };
 
 function skipComment(stream){
-    if (stream.peekStr(2) != "(*")
+    if (Stream.peekStr(stream, 2) != "(*")
         return false;
 
-    stream.next(2);
-    while (stream.peekStr(2) != "*)"){
-        if (stream.eof())
+    Stream.next(stream, 2);
+    while (Stream.peekStr(stream, 2) != "*)"){
+        if (Stream.eof(stream))
             throw new Errors.Error("comment was not closed");
         if (!skipComment(stream))
-            stream.next(1);
+            Stream.next(stream, 1);
         }
-    stream.next(2);
+    Stream.next(stream, 2);
     return true;
 }
 
@@ -121,23 +129,26 @@ exports.skipSpaces = function(stream, context){
         return;
 
     do {
-        stream.read(readSpaces);
+        Stream.read(stream, readSpaces);
     }
     while (skipComment(stream));
 };
 
 exports.separator = function(stream, context){
-    return !isLetter(stream.peekChar());
+    return Stream.eof(stream) || !isLetter(Stream.peekChar(stream));
 };
 
 exports.literal = function(s){
     return function(stream, context){
-        if (stream.str(s.length) != s)
+        if (Stream.peekStr(stream, s.length) != s)
             return false;
-        stream.next(s.length);
+        Stream.next(stream, s.length);
         
-        if ((!context.isLexem || !context.isLexem()) && isLetter(s[s.length - 1])){
-            var next = stream.peekChar();
+        if ((!context.isLexem || !context.isLexem())
+            && isLetter(s[s.length - 1])
+            && !Stream.eof(stream)
+           ){
+            var next = Stream.peekChar(stream);
             if (isLetter(next) || isDigit(next))
                 return false;
         }

+ 7 - 2
src/nodejs.js

@@ -18,7 +18,9 @@ var ModuleGenerator = Rtl.Class.extend({
         var modules = this.__imports;
         for(var name in modules){
             var alias = modules[name];
-            result += "var " + alias + " = require(\"" + name + ".js\");\n";
+            result += "var " + alias + " = " + (name == "this" 
+                ? "GLOBAL"
+                : "require(\"" + name + ".js\")") + ";\n";
         }
         return result;
     },
@@ -58,12 +60,14 @@ function compile(sources, handleErrors, outDir){
     var rtl = new Rtl.RTL(rtlCodeWatcher);
     var moduleCode = function(name, imports){return new ModuleGenerator(name, imports);};
 
+    var compiledFilesStack = [];
     oc.compileModules(
             sources,
             function(name){
                 var fileName = name;
                 if (!path.extname(fileName).length)
                     fileName += ".ob";
+                compiledFilesStack.push(fileName);
                 return fs.readFileSync(fileName, "utf8");
             },
             function(moduleResolver){return new Context.Context(
@@ -71,7 +75,7 @@ function compile(sources, handleErrors, outDir){
                 moduleCode,
                 rtl,
                 moduleResolver);},
-            handleErrors,
+            function(e){handleErrors("File \"" + compiledFilesStack[compiledFilesStack.length - 1] + "\", " + e);},
             function(name, code){
                 if (rtlCodeWatcher.used()){
                     code = "var " + rtl.name() + " = require(\"" + rtl.name() 
@@ -79,6 +83,7 @@ function compile(sources, handleErrors, outDir){
                     rtlCodeWatcher.reset();
                 }
                 writeCompiledModule(name, code, outDir);
+                compiledFilesStack.pop();
             });
 
     var rtlCode = rtl.generate();

+ 42 - 0
src/oberon.js/JsString.js

@@ -0,0 +1,42 @@
+var RTL$ = require("RTL$.js").RTL$;
+var JS = GLOBAL;
+var Type = RTL$.extend({
+	init: function Type(){
+	}
+});
+
+function len(self/*Type*/){
+	var result = 0;
+	result = self.length;
+	return result;
+}
+
+function at(self/*Type*/, pos/*INTEGER*/){
+	var result = 0;
+	result = self[pos];
+	return result;
+}
+
+function indexOf(self/*Type*/, c/*CHAR*/){
+	var result = 0;
+	result = self.indexOf(JS.String.fromCharCode(c));
+	return result;
+}
+
+function indexOfFrom(self/*Type*/, c/*CHAR*/, pos/*INTEGER*/){
+	var result = 0;
+	result = self.indexOf(JS.String.fromCharCode(c), pos);
+	return result;
+}
+
+function substr(self/*Type*/, pos/*INTEGER*/, len/*INTEGER*/){
+	var result = null;
+	result = self.substr(pos, len);
+	return result;
+}
+exports.Type = Type;
+exports.len = len;
+exports.at = at;
+exports.indexOf = indexOf;
+exports.indexOfFrom = indexOfFrom;
+exports.substr = substr;

+ 23 - 0
src/oberon.js/RTL$.js

@@ -0,0 +1,23 @@
+var RTL$ = {
+    extend: function extend(methods){
+        function Type(){
+            for(var m in methods)
+                this[m] = methods[m];
+        }
+        Type.prototype = this.prototype;
+
+        var result = methods.init;
+        result.prototype = new Type(); // inherit this.prototype
+        result.prototype.constructor = result; // to see constructor name in diagnostic
+        
+        result.extend = extend;
+        return result;
+    },
+    assert: function (condition, code){
+        if (!condition)
+            throw new Error("assertion failed"
+                          + ((code !== undefined) ? " with code " + code : ""));
+    }
+};
+
+exports.RTL$ = RTL$;

+ 87 - 0
src/oberon.js/Stream.js

@@ -0,0 +1,87 @@
+var RTL$ = require("RTL$.js").RTL$;
+var JsString = require("JsString.js");
+var Type = RTL$.extend({
+	init: function Type(){
+		this.s = null;
+		this.pos = 0;
+	}
+});
+
+function make(text/*Type*/){
+	var result = null;
+	result = new Type();
+	result.s = text;
+	return result;
+}
+
+function eof(self/*Type*/){
+	return self.pos == JsString.len(self.s);
+}
+
+function pos(self/*Type*/){
+	return self.pos;
+}
+
+function setPos(self/*Type*/, pos/*INTEGER*/){
+	RTL$.assert(pos <= JsString.len(self.s));
+	self.pos = pos;
+}
+
+function next(self/*Type*/, n/*INTEGER*/){
+	RTL$.assert((self.pos + n | 0) <= JsString.len(self.s));
+	self.pos = self.pos + n | 0;
+}
+
+function peekChar(self/*Type*/){
+	RTL$.assert(!eof(self));
+	return JsString.at(self.s, self.pos);
+}
+
+function getChar(self/*Type*/){
+	var result = 0;
+	RTL$.assert(!eof(self));
+	result = JsString.at(self.s, self.pos);
+	++self.pos;
+	return result;
+}
+
+function peekStr(self/*Type*/, len/*INTEGER*/){
+	var max = 0;
+	max = JsString.len(self.s) - self.pos | 0;
+	if (len > max){
+		len = max;
+	}
+	return JsString.substr(self.s, self.pos, len);
+}
+
+function read(self/*Type*/, f/*ReaderProc*/){
+	while (true){
+		if (!eof(self) && f(peekChar(self))){
+			next(self, 1);
+		} else break;
+	}
+	return !eof(self);
+}
+
+function lineNumber(self/*Type*/){
+	var line = 0;
+	var lastPos = 0;
+	lastPos = JsString.indexOf(self.s, 13);
+	while (true){
+		if (lastPos != -1 && lastPos < self.pos){
+			++line;
+			lastPos = JsString.indexOfFrom(self.s, 13, lastPos + 1 | 0);
+		} else break;
+	}
+	return line + 1 | 0;
+}
+exports.make = make;
+exports.eof = eof;
+exports.pos = pos;
+exports.setPos = setPos;
+exports.next = next;
+exports.peekChar = peekChar;
+exports.getChar = getChar;
+exports.peekStr = peekStr;
+exports.read = read;
+exports.lineNumber = lineNumber;

+ 42 - 0
src/oberon/JsString.ob

@@ -0,0 +1,42 @@
+MODULE JsString;
+IMPORT JS;
+
+TYPE
+    Type* = POINTER TO RECORD END;
+
+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[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;
+
+END JsString.

+ 86 - 0
src/oberon/Stream.ob

@@ -0,0 +1,86 @@
+MODULE Stream;
+IMPORT JsString;
+
+TYPE
+    Type = POINTER TO RECORD
+        s: JsString.Type;
+        pos: INTEGER
+    END;
+
+    ReaderProc = PROCEDURE(c: CHAR): BOOLEAN;
+
+PROCEDURE make*(text: JsString.Type): Type;
+VAR result: Type;
+BEGIN
+    NEW(result);
+    result.s := text;
+    RETURN result
+END make;
+
+PROCEDURE eof*(self: Type): BOOLEAN;
+    RETURN self.pos = JsString.len(self.s)
+END eof;
+
+PROCEDURE pos*(self: Type): INTEGER;
+    RETURN self.pos
+END pos;
+
+PROCEDURE setPos*(self: Type; pos: INTEGER);
+BEGIN
+    ASSERT(pos <= JsString.len(self.s));
+    self.pos := pos
+END setPos;
+
+PROCEDURE next*(self: Type; n: INTEGER);
+BEGIN
+    ASSERT(self.pos + n <= JsString.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)
+END peekChar;
+
+PROCEDURE getChar*(self: Type): CHAR;
+VAR result: CHAR;
+BEGIN
+    ASSERT(~eof(self));
+    result := JsString.at(self.s, self.pos);
+    INC(self.pos);
+    RETURN result
+END getChar;
+
+PROCEDURE peekStr*(self: Type; len: INTEGER): JsString.Type;
+VAR max: INTEGER;
+BEGIN
+    max := JsString.len(self.s) - self.pos;
+    IF len > max THEN
+        len := max;
+    END
+    RETURN JsString.substr(self.s, self.pos, len)
+END peekStr;
+
+PROCEDURE read*(self: Type; f: ReaderProc): BOOLEAN;
+BEGIN
+    WHILE ~eof(self) & f(peekChar(self)) DO
+        next(self, 1);
+    END
+    RETURN ~eof(self)
+END read;
+
+PROCEDURE lineNumber*(self: Type): INTEGER;
+VAR 
+    line: INTEGER;
+    lastPos: INTEGER;
+BEGIN
+    lastPos := JsString.indexOf(self.s, 0DX);
+    WHILE (lastPos # -1) & (lastPos < self.pos) DO
+        INC(line);
+        lastPos := JsString.indexOfFrom(self.s, 0DX, lastPos + 1);
+    END;
+    RETURN line + 1
+END lineNumber;
+
+END Stream.

+ 4 - 4
src/oc.js

@@ -6,7 +6,7 @@ var Errors = require("errors.js");
 var Grammar = require("grammar.js");
 var Lexer = require("lexer.js");
 var ImportRTL = require("rtl.js");
-var Stream = require("stream.js").Stream;
+var Stream = require("oberon.js/Stream.js");
 
 var RTL = ImportRTL.RTL;
 var Class = ImportRTL.Class;
@@ -32,7 +32,7 @@ function compileModule(stream, context, handleErrors){
         if (x instanceof Errors.Error) {
             //console.log(context.getResult());
             if (handleErrors){
-                handleErrors(stream.describePosition() + ": " + x);
+                handleErrors("line " + Stream.lineNumber(stream) + ": " + x);
                 return undefined;
             }
         }
@@ -51,7 +51,7 @@ function compileModulesFromText(
         resolveModule,
         handleCompiledModule,
         handleErrors){
-    var stream = new Stream(text);
+    var stream = Stream.make(text);
     do {
         var context = contextFactory(resolveModule);
         var module = compileModule(stream, context, handleErrors);
@@ -60,7 +60,7 @@ function compileModulesFromText(
         handleCompiledModule(module);
         Lexer.skipSpaces(stream);
     }
-    while (!stream.eof());
+    while (!Stream.eof(stream));
 }
 
 var ModuleResolver = Class.extend({

+ 9 - 8
src/parser.js

@@ -3,6 +3,7 @@
 var assert = require("assert.js").ok;
 var Errors = require("errors.js");
 var Lexer = require("lexer.js");
+var Stream = require("oberon.js/Stream.js");
 
 function implicitParser(p){
 	return typeof p === "string" ? Lexer.literal(p) : p;
@@ -39,10 +40,10 @@ exports.or = function(/*...*/){
 	return function(stream, context){
 		for(var i = 0; i < parsers.length; ++i){
 			var p = parsers[i];
-			var savePos = stream.pos();
+			var savePos = Stream.pos(stream);
 			if (p(stream, context))
 				return true;
-			stream.setPos(savePos);
+			Stream.setPos(stream, savePos);
 		}
 		return false;
 	};
@@ -50,12 +51,12 @@ exports.or = function(/*...*/){
 
 exports.repeat = function(p){
 	return function(stream, context){
-			var savePos = stream.pos();
-			while (!stream.eof() && p(stream, context)){
+			var savePos = Stream.pos(stream);
+			while (!Stream.eof(stream) && p(stream, context)){
 				Lexer.skipSpaces(stream, context);
-				savePos = stream.pos();
+				savePos = Stream.pos(stream);
 			}
-			stream.setPos(savePos);
+			Stream.setPos(stream, savePos);
 			return true;
 		};
 };
@@ -65,9 +66,9 @@ exports.optional = function(parser){
 	var p = implicitParser(parser);
 
 	return function(stream, context){
-		var savePos = stream.pos();
+		var savePos = Stream.pos(stream);
 		if ( !p(stream, context))
-			stream.setPos(savePos);
+			Stream.setPos(stream, savePos);
 		return true;
 		};
 };

+ 0 - 47
src/stream.js

@@ -1,47 +0,0 @@
-"use strict";
-
-var assert = require("assert.js").ok;
-var Class = require("rtl.js").Class;
-
-exports.Stream = Class.extend({
-	init: function Stream(s){
-		this.__s = s;
-		this.__pos = 0;
-	},
-	str: function(n){return this.__s.substr(this.__pos, n);},
-	getChar: function(){
-		if (this.eof())
-			return undefined;
-		return this.__s.charAt(this.__pos++);
-	},
-	read: function(f){
-		while (!this.eof() && f(this.peekChar()))
-			this.next(1);
-		return !this.eof();
-	},
-	peekChar: function(){
-		if (this.eof())
-			return undefined;
-		return this.__s.charAt(this.__pos);
-	},
-	peekStr: function(size){
-		var max = this.__s.length - this.__pos;
-		if (size > max)
-			size = max;
-		return this.__s.substr(this.__pos, size);
-	},
-	next: function(n){
-		assert(this.__pos + n <= this.__s.length);
-		this.__pos += n;
-	},
-	pos: function(){return this.__pos;},
-	setPos: function(pos){
-		assert(pos <= this.__s.length);
-		return this.__pos = pos;
-	},
-	eof: function(){return this.__pos == this.__s.length;},
-	describePosition: function(){
-		var line = this.__s.substr(0, this.__pos).split("\n").length;
-		return "line " + line;
-	}
-});

+ 5 - 0
test/compile_oberon_source.cmd

@@ -0,0 +1,5 @@
+@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

+ 7 - 0
test/examples/Lexer.ob

@@ -0,0 +1,7 @@
+MODULE Lexer;
+
+PROCEDURE isDigit*(c: CHAR): BOOLEAN;
+    RETURN (c >= "0") OR (c <= "9")
+END isDigit;
+
+END Lexer.

+ 5 - 0
test/make_snapshot.cmd

@@ -0,0 +1,5 @@
+del /S /Q ..\snapshot
+mkdir ..\snapshot
+mkdir ..\snapshot\oberon.js
+copy ..\src\*.js ..\snapshot
+copy ..\src\oberon.js\*.js ..\snapshot\oberon.js

+ 2 - 2
test/run_nodejs.cmd

@@ -1,2 +1,2 @@
-SET NODE_PATH=.;%~dp1
-"C:\Program Files\nodejs\node.exe" %*
+@SET NODE_PATH=.;%~dp1;%NODE_PATH%
+@"C:\Program Files\nodejs\node.exe" %*

+ 1 - 1
test/test_compile.cmd

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

+ 1 - 1
test/test_unit.cmd

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

+ 5 - 5
test/test_unit.js

@@ -8,7 +8,7 @@ var Grammar = require("grammar.js");
 var oc = require("oc.js");
 var ImportRTL = require("rtl.js");
 var Scope = require("scope.js");
-var Stream = require("stream.js").Stream;
+var Stream = require("Stream.js");
 var Test = require("test.js");
 
 var TestError = Test.TestError;
@@ -16,8 +16,8 @@ var RTL = ImportRTL.RTL;
 var Class = ImportRTL.Class;
 
 function parseInContext(grammar, s, context){
-    var stream = new Stream(s);
-    if (!grammar(stream, context) || !stream.eof())
+    var stream = Stream.make(s);
+    if (!grammar(stream, context) || !Stream.eof(stream))
         throw new Errors.Error("not parsed");
 }
 
@@ -159,10 +159,10 @@ var TestContextWithModule = TestContext.extend({
 function testWithModule(src, pass, fail){
     return testWithSetup(
         function(){
-            var imported = oc.compileModule(new Stream(src), makeContext());
+            var imported = oc.compileModule(Stream.make(src), makeContext());
             var module = imported.symbol().info();
             return setup(function(s){
-                oc.compileModule(new Stream(s),
+                oc.compileModule(Stream.make(s),
                                  new TestContextWithModule(module));
             });},
         pass,