Browse Source

more tests

Vladislav Folts 12 years ago
parent
commit
c6230a97d6
6 changed files with 105 additions and 66 deletions
  1. 5 2
      src/context.js
  2. 1 1
      src/module.js
  3. 5 2
      src/oc.js
  4. 5 4
      src/scope.js
  5. 4 2
      src/type.js
  6. 85 55
      test/test_unit.js

+ 5 - 2
src/context.js

@@ -19,7 +19,10 @@ var basicTypes = Type.basic;
 function getSymbolAndScope(context, id){
     var s = context.findSymbol(id);
     if (!s)
-        throw new Errors.Error("undeclared identifier: '" + id + "'");
+        throw new Errors.Error(
+            context instanceof Type.Module
+                ? "identifier '" + id + "' is not exported by module '" + context.name() + "'"
+                : "undeclared identifier: '" + id + "'");
     return s;
 }
 
@@ -1647,7 +1650,7 @@ exports.ModuleDeclaration = ChainedContext.extend({
                 var s = modules[i];
                 if (i)
                     gen.write(", ");
-                gen.write(s.info().id());
+                gen.write(s.info().name());
             }
             gen.write(");\n");
         }

+ 1 - 1
src/module.js

@@ -20,7 +20,7 @@ var JSModule = Type.Module.extend({
 	init: function Module$JSModule(){
 		Type.Module.prototype.init.call(this);
 	},
-	id: function(){return "this";},
+	name: function(){return "this";},
 	findSymbol: function(id){
 		return new Symbol.Found(new Symbol.Symbol(id, any));
 	}

+ 5 - 2
src/oc.js

@@ -44,7 +44,7 @@ function compileModule(stream, rtl, moduleResolver, handleErrors){
     return new CompiledModule(scope.module(), code.getResult(), scope.exports());
 }
 
-exports.compile = function(text, handleErrors){
+function compile(text, handleErrors){
     var stream = new Stream(text);
     var rtl = new RTL();
     var code = "";
@@ -62,4 +62,7 @@ exports.compile = function(text, handleErrors){
     } 
     while (!stream.eof());
     return rtl.generate() + code;
-};
+}
+
+exports.compileModule = compileModule;
+exports.compile = compile;

+ 5 - 4
src/scope.js

@@ -68,16 +68,17 @@ var ProcedureScope = Scope.extend({
 
 var CompiledModule = Type.Module.extend({
     init: function Scope$CompiledModule(id){
-        Type.Module.prototype.init.call(this);
-        this.__id = id;
+        Type.Module.prototype.init.call(this, id);
         this.__exports = undefined;
     },
-    id: function(){return this.__id;},
     defineExports: function(exports){
         this.__exports = exports;
     },  
     findSymbol: function(id){
-        return new Symbol.Found(this.__exports[id]);
+        var s = this.__exports[id];
+        if (!s)
+            return undefined;
+        return new Symbol.Found(s);
     }
 });
 

+ 4 - 2
src/type.js

@@ -172,9 +172,11 @@ exports.Procedure = BasicType.extend({
 });
 
 var Module = Id.extend({
-    init: function Type$Module(){
+    init: function Type$Module(name){
         Id.prototype.init.call(this);
-    }
+        this.__name = name;
+    },
+    name: function(){return this.__name;}
 });
 
 exports.Module = Module;

+ 85 - 55
test/test_unit.js

@@ -27,11 +27,9 @@ function makeContext(){
     return result;
 }
 
-function parseUsingGrammar(grammar, s, cxFactory, handlerError){
-    var baseContext = makeContext();
-    var context = cxFactory ? cxFactory(baseContext) : baseContext;
+function runAndHandleErrors(action, s, handlerError){
     try {
-        parseInContext(grammar, s, context);
+        action(s);
     }
     catch (x){
         if (!(x instanceof Errors.Error))
@@ -47,16 +45,18 @@ function parseUsingGrammar(grammar, s, cxFactory, handlerError){
     return true;
 }
 
-function setup(parser, contextFactory){
-    function parseImpl(s, handleError){
-        return parseUsingGrammar(parser, s, contextFactory, handleError);
-    }
+function parseUsingGrammar(grammar, s, cxFactory){
+    var baseContext = makeContext();
+    var context = cxFactory ? cxFactory(baseContext) : baseContext;
+    parseInContext(grammar, s, context);
+}
 
+function setup(run){
     return {
         parse: function(s){
             function handleError(e){throw new TestError(s + "\n\t" + e);}
 
-            if (!parseImpl(s, handleError))
+            if (!runAndHandleErrors(run, s, handleError))
                 throw new TestError(s + ": not parsed");
         },
         expectError: function(s, error){
@@ -66,12 +66,19 @@ function setup(parser, contextFactory){
                     throw new TestError(s + "\n\texpected error: " + error + "\n\tgot: " + sErr );
             }
 
-            if (parseImpl(s, handleError))
+            if (runAndHandleErrors(run, s, handleError))
                 throw new TestError(s + ": should not be parsed, expect error: " + error);
         }
     };
 }
 
+function setupParser(parser, contextFactory){
+    function parseImpl(s){
+        return parseUsingGrammar(parser, s, contextFactory);
+    }
+    return setup(parseImpl);
+}
+
 function setupWithContext(grammar, source){
     function innerMakeContext(){
         var context = makeContext();
@@ -86,7 +93,7 @@ function setupWithContext(grammar, source){
         return context;
     }
 
-    return setup(grammar, innerMakeContext);
+    return setupParser(grammar, innerMakeContext);
 }
 
 function context(grammar, source){
@@ -103,10 +110,12 @@ function testWithSetup(setup, pass, fail){
         var i;
         for(i = 0; i < pass.length; ++i)
             test.parse(pass[i]);
-        for(i = 0; i < fail.length; ++i){
-            var f = fail[i];
-            test.expectError(f[0], f[1]);
-        }
+    
+        if (fail)
+            for(i = 0; i < fail.length; ++i){
+                var f = fail[i];
+                test.expectError(f[0], f[1]);
+            }
     };
 }
 
@@ -119,14 +128,14 @@ function testWithContext(context, pass, fail){
 
 function testWithGrammar(grammar, pass, fail){
     return testWithSetup(
-        function(){return setup(grammar);},
+        function(){return setupParser(grammar);},
         pass,
         fail);
 }
 
 var testSuite = {
 comment: function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
     test.parse("(**)123");
     test.parse("(*abc*)123");
     test.parse("(*abc*)(*def*)123");
@@ -134,7 +143,7 @@ comment: function(){
     test.expectError("(*123", "comment was not closed");
 },
 "spaces are required to separate keywords and integers": function(){
-    var test = setup(Grammar.typeDeclaration);
+    var test = setupParser(Grammar.typeDeclaration);
 
     test.expectError("T = ARRAY10OFARRAY5OFINTEGER", "not parsed");
     test.expectError("T = ARRAY10 OF ARRAY 5 OF INTEGER", "not parsed");
@@ -169,7 +178,7 @@ expression: function(){
     test.expectError("noResult()", "procedure returning no result cannot be used in an expression");
 },
 "string expression": function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
 
     test.parse("\"\"");
     test.parse("\"a\"");
@@ -184,7 +193,7 @@ expression: function(){
     //assert(parse("\"a\" + \"b\""));
 },
 "parentheses": function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
 
     test.parse("(1)");
     test.parse("(1 + 2)");
@@ -200,25 +209,28 @@ identifier: function(){
         getResult: function() {return this.__ident;}
     });
     function makeContext() {return new IdentDeclarationContext();}
-    var parse = function(s) { return parseUsingGrammar(Grammar.ident, s, makeContext); };
-    assert(!parse(""));
-    assert(parse("i"));
-    assert(!parse("1"));
-    assert(parse("abc1"));
-},
-"variable declaration": function(){
-    var parse = function(s) { return parseUsingGrammar(Grammar.variableDeclaration, s); };
-    assert(parse("i: INTEGER"));
-    assert(parse("i, j: INTEGER"));
-    assert(!parse("i: T"));
-},
-"procedure VAR section": function(){
-    var parse = function(s) { return parseUsingGrammar(Grammar.declarationSequence, s); };
-    assert(parse("VAR"));
-    assert(parse("VAR i: INTEGER;"));
-    assert(parse("VAR i, j: INTEGER;"));
-    assert(parse("VAR i, j: INTEGER; b: BOOLEAN;"));
+
+    testWithSetup(
+        function(){return setupParser(Grammar.ident, makeContext);},
+        pass("i", "abc1"),
+        fail(["", "not parsed"],
+             ["1", "not parsed"]
+             )
+        )();
 },
+"variable declaration": testWithGrammar(
+    Grammar.variableDeclaration,
+    pass("i: INTEGER",
+         "i, j: INTEGER"),
+    fail(["i: T", "undeclared identifier: 'T'"])
+    ),
+"procedure VAR section": testWithGrammar(
+    Grammar.declarationSequence,
+    pass("VAR",
+         "VAR i: INTEGER;",
+         "VAR i, j: INTEGER;",
+         "VAR i, j: INTEGER; b: BOOLEAN;")
+    ),
 "const declaration": function(){
     var test = setupWithContext(
           Grammar.declarationSequence
@@ -285,13 +297,13 @@ identifier: function(){
     test.expectError("T = ARRAY ORD({0..5} >= {0..8}) OF INTEGER", "array size must be greater than 0, got 0");
 },
 "multi-dimensional array declaration": function(){
-    var test = setup(Grammar.typeDeclaration);
+    var test = setupParser(Grammar.typeDeclaration);
 
     test.parse("T = ARRAY 10 OF ARRAY 5 OF INTEGER");
     test.parse("T = ARRAY 10, 5 OF INTEGER");
 },
 "PROCEDURE type declaration": function(){
-    var test = setup(Grammar.typeDeclaration);
+    var test = setupParser(Grammar.typeDeclaration);
 
     test.parse("T = PROCEDURE");
     test.parse("T = PROCEDURE()");
@@ -300,7 +312,7 @@ identifier: function(){
     test.parse("T = PROCEDURE(): T");
 },
 "POINTER declaration": function(){
-    var test = setup(Grammar.typeDeclaration);
+    var test = setupParser(Grammar.typeDeclaration);
 
     test.parse("T = POINTER TO RECORD END");
     test.parse("T = RECORD p: POINTER TO T END");
@@ -420,7 +432,7 @@ identifier: function(){
                      "read-only variable cannot be used as VAR parameter");
 },
 "LEN": function(){
-    var test = setup(Grammar.procedureDeclaration);
+    var test = setupParser(Grammar.procedureDeclaration);
 
     test.parse("PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a) END p");
     test.parse("PROCEDURE p(VAR a: ARRAY OF BOOLEAN): INTEGER; RETURN LEN(a) END p");
@@ -600,7 +612,7 @@ identifier: function(){
     test.expectError("i := noResult()", "procedure returning no result cannot be used in an expression");
     },
 "array expression": function(){
-    var test = setup(Grammar.procedureBody);
+    var test = setupParser(Grammar.procedureBody);
     test.parse("VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1 END");
     test.parse("VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1; a[1] := a[0] END");
     test.expectError("VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := TRUE END"
@@ -621,13 +633,13 @@ identifier: function(){
                    , "index out of bounds: maximum possible index is 9, got 15");
 },
 "multi-dimensional array expression": function(){
-    var test = setup(Grammar.procedureBody);
+    var test = setupParser(Grammar.procedureBody);
     test.parse("VAR a: ARRAY 10 OF ARRAY 5 OF INTEGER; BEGIN a[0][0] := 1 END");
     test.parse("VAR a: ARRAY 10, 5 OF BOOLEAN; BEGIN a[0][0] := TRUE END");
     test.parse("VAR a: ARRAY 10, 5 OF BOOLEAN; BEGIN a[0, 0] := TRUE END");
 },
 "INTEGER number": function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
     test.parse("0");
     test.parse("123");
     test.parse("1H");
@@ -650,7 +662,7 @@ identifier: function(){
     //test.expectError("s := {32}", "0..31");
 },
 "REAL number": function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
     test.parse("1.2345");
     test.parse("1.");
     test.parse("1.2345E6");
@@ -662,7 +674,7 @@ identifier: function(){
     test.expectError("1.2345E-1 2", "not parsed");
 },
 "LONGREAL number": function(){
-    var test = setup(Grammar.expression);
+    var test = setupParser(Grammar.expression);
     test.parse("1.2345D6");
     test.parse("1.2345D+6");
     test.parse("1.2345D-6");
@@ -848,7 +860,7 @@ identifier: function(){
         )
 ),
 "procedure body": function(){
-    var test = setup(Grammar.procedureBody);
+    var test = setupParser(Grammar.procedureBody);
     test.parse("END");
     test.parse("VAR END");
     test.parse("VAR i: INTEGER; END");
@@ -874,7 +886,7 @@ identifier: function(){
 },
 "procedure heading": function(){
     function innerMakeContext(cx){return new Context.ProcDecl(makeContext());}
-    var test = setup(Grammar.procedureHeading, innerMakeContext);
+    var test = setupParser(Grammar.procedureHeading, innerMakeContext);
 
     test.parse("PROCEDURE p");
     test.parse("PROCEDURE p(a1: INTEGER)");
@@ -1104,7 +1116,7 @@ procedure: function(){
     test.expectError("r1 := b1", "type mismatch: 'r1' is 'T1' and cannot be assigned to 'Base1' expression");
 },
 "open Array assignment fails": function(){
-    var test = setup(Grammar.procedureDeclaration);
+    var test = setupParser(Grammar.procedureDeclaration);
     test.expectError("PROCEDURE p(s1, s2: ARRAY OF CHAR); BEGIN s1 := s2 END p"
                    , "cannot assign to read-only variable");
     test.expectError("PROCEDURE p(VAR s1, s2: ARRAY OF CHAR); BEGIN s1 := s2 END p"
@@ -1113,7 +1125,7 @@ procedure: function(){
                    , "'s2' cannot be assigned to open 'ARRAY OF CHAR'");
 },
 "string assignment to open array fails": function(){
-    var test = setup(Grammar.procedureDeclaration);
+    var test = setupParser(Grammar.procedureDeclaration);
     test.expectError("PROCEDURE p(s: ARRAY OF CHAR); BEGIN s := \"abc\" END p", "cannot assign to read-only variable");
     test.expectError("PROCEDURE p(VAR s: ARRAY OF CHAR); BEGIN s := \"abc\" END p", "string cannot be assigned to open ARRAY OF CHAR");
 },
@@ -1131,18 +1143,18 @@ procedure: function(){
     test.expectError("p4(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'ARRAY OF INTEGER'");
 },
 "scope": function(){
-    var test = setup(Grammar.declarationSequence);
+    var test = setupParser(Grammar.declarationSequence);
     test.parse("PROCEDURE p1(a1: INTEGER); END p1; PROCEDURE p2(a1: BOOLEAN); END p2;");
 },
 module: function(){
-    var test = setup(Grammar.module);
+    var test = setupParser(Grammar.module);
     test.parse("MODULE m; END m.");
     test.expectError("MODULE m; END undeclared.",
                      "original module name 'm' expected, got 'undeclared'");
     test.expectError("MODULE m; BEGIN - END m.", "END expected (MODULE)");
 },
 assert: function(){
-    var test = setup(Grammar.statement);
+    var test = setupParser(Grammar.statement);
     test.parse("ASSERT(TRUE)");
     test.parse("ASSERT(TRUE, 123)");
     test.expectError("ASSERT()", "at least 1 argument expected, got 0");
@@ -1186,7 +1198,25 @@ assert: function(){
          ["MODULE m; IMPORT a1 := u1, a1 := u2; END m.", "duplicated alias: 'a1'"],
          ["MODULE m; IMPORT J := JS; BEGIN JS.alert(\"test\") END m.", "undeclared identifier: 'JS'"]
          )
-    )
+    ),
+//"imported variables are read-only": function(){
+"imported identifiers": function(){
+    var imported = oc.compileModule(new Stream("MODULE test; END test."));
+    function run(s){
+        oc.compileModule(new Stream(s),
+                         undefined,
+                         function(){return imported.symbol().info();});
+    }
+    testWithSetup(
+        function(){return setup(run);},
+        pass(),
+        fail(["MODULE m; IMPORT test; BEGIN test.p(); END m.",
+              "identifier 'p' is not exported by module 'test'"],
+              ["MODULE m; IMPORT t := test; BEGIN t.p(); END m.",
+               "identifier 'p' is not exported by module 'test'"]
+              ))
+    ();
+    }
 };
 
 Test.run(testSuite);