浏览代码

Merge pull request #27 from vladfolts/rel_v1.0

fixes from rel v1.0
vladfolts 12 年之前
父节点
当前提交
1f41b4cd7b
共有 5 个文件被更改,包括 87 次插入18 次删除
  1. 19 9
      src/context.js
  2. 2 1
      src/grammar.js
  3. 2 0
      test/compile.cmd
  4. 37 0
      test/compile.js
  5. 27 8
      test/test_unit.js

+ 19 - 9
src/context.js

@@ -309,11 +309,11 @@ exports.Designator = ChainedContext.extend({
         var e = this.__indexExpression;
         var expType = e.type();
         if (expType != basicTypes.integer)
-            throw new Errors.Error("'INTEGER' expression expected, got '" + expType.name() + "'");
+            throw new Errors.Error("'INTEGER' expression expected, got '" + expType.description() + "'");
 
         var type = this.__currentType;
         if (!(type instanceof Type.Array))
-            throw new Errors.Error("ARRAY expected, got '" + type.name() + "'");
+            throw new Errors.Error("ARRAY expected, got '" + type.description() + "'");
         var value = e.constValue();
         if (value !== undefined && value >= type.length())
             throw new Errors.Error("index out of bounds: maximum possible index is "
@@ -682,7 +682,7 @@ exports.ArrayDimensions = ChainedContext.extend({
     handleExpression: function(e){
         var type = e.type();
         if (type !== basicTypes.integer)
-            throw new Errors.Error("'INTEGER' constant expression expected, got '" + type.name() + "'");
+            throw new Errors.Error("'INTEGER' constant expression expected, got '" + type.description() + "'");
         var value = e.constValue();
         if (value === undefined)
             throw new Errors.Error("constant expression expected as ARRAY size");
@@ -815,7 +815,7 @@ exports.AddOperator = ChainedContext.extend({
         else if (s == "OR"){
             if (type != basicTypes.bool)
                 throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
-                                     + type.name() + "'");
+                                     + type.description() + "'");
             o = op.or;
         }
         if (o)
@@ -1106,7 +1106,7 @@ exports.Expression = ChainedContext.extend({
 function handleIfExpression(e){
     var type = e.type();
     if (type !== basicTypes.bool)
-        throw new Errors.Error("'BOOLEAN' expression expected, got '" + type.name() + "'");
+        throw new Errors.Error("'BOOLEAN' expression expected, got '" + type.description() + "'");
 }
 
 var IfContextBase = ChainedContext.extend({
@@ -1334,10 +1334,10 @@ exports.For = ChainedContext.extend({
             throw new Errors.Error(
                 !this.__initExprParsed
                     ? "'INTEGER' expression expected to assign '" + this.__var
-                      + "', got '" + type.name() + "'"
+                      + "', got '" + type.description() + "'"
                     : !this.__toParsed
-                    ? "'INTEGER' expression expected as 'TO' parameter, got '" + type.name() + "'"
-                    : "'INTEGER' expression expected as 'BY' parameter, got '" + type.name() + "'"
+                    ? "'INTEGER' expression expected as 'TO' parameter, got '" + type.description() + "'"
+                    : "'INTEGER' expression expected as 'BY' parameter, got '" + type.description() + "'"
                     );
         if (!this.__initExprParsed)
             this.__initExprParsed = true;
@@ -1375,6 +1375,16 @@ exports.For = ChainedContext.extend({
 
 exports.emitForBegin = function(context){context.handleBegin();};
 
+exports.CheckAssignment = ChainedContext.extend({
+    init: function Context$CheckAssignment(context){
+        ChainedContext.prototype.init.call(this, context);
+    },
+    handleLiteral: function(s){
+        if (s == "=")
+            throw new Errors.Error("did you mean ':=' (statement expected, got expression)?");
+    }
+});
+
 exports.Assignment = ChainedContext.extend({
     init: function AssignmentContext(context){
         ChainedContext.prototype.init.call(this, context);
@@ -1480,7 +1490,7 @@ exports.FieldListDeclaration = HandleSymbolAsType.extend({
 
 function assertProcType(type){
     if (!(type instanceof Type.Procedure) && !(type instanceof Module.AnyType))
-        throw new Errors.Error("PROCEDURE expected, got '" + type.name() + "'");
+        throw new Errors.Error("PROCEDURE expected, got '" + type.description() + "'");
 }
 
 exports.ActualParameters = ChainedContext.extend({

+ 2 - 1
src/grammar.js

@@ -87,7 +87,8 @@ var actualParameters = and("(", context(optional(expList), Context.ActualParamet
 var procedureCall = context(and(designator, optional(actualParameters))
                           , Context.StatementProcedureCall);
 
-var assignment = context(and(designator, ":=", required(expression, "expression expected"))
+var assignment = context(and(designator, context(or(":=", "="), Context.CheckAssignment)
+                       , required(expression, "expression expected"))
                        , Context.Assignment);
 
 var statement = optional(or(

+ 2 - 0
test/compile.cmd

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

+ 37 - 0
test/compile.js

@@ -0,0 +1,37 @@
+"use strict";
+
+var oc = require("oc");
+var fs = require("fs");
+var path = require("path");
+
+function compile(src){
+    var text = fs.readFileSync(src, "utf8");
+    var errors = "";
+    var result = oc.compile(text, function(e){errors += "File \"" + src + "\", " + e;});
+    if (errors){
+        console.info(errors);
+        return;
+    }
+
+    var fileName = path.basename(src);
+    if (path.extname(fileName) != ".ob"){
+        console.info(result);
+        return;
+    }
+
+    fileName = fileName.substr(0, fileName.length - ".ob".length) + ".js";
+    fs.writeFileSync(fileName, result);
+    console.info("compiled to '" + fileName + "' - OK!");
+
+}
+
+function main(){
+    if (process.argv.length < 2){
+        console.info("compile.js <oberon source file path>");
+        return;
+    }
+
+    compile(process.argv[2]);
+}
+
+main();

+ 27 - 8
test/test_unit.js

@@ -293,7 +293,7 @@ var testSuite = {
     ),
 "array declaration": testWithContext(
     context(Grammar.typeDeclaration,
-            "CONST c1 = 5; VAR v1: INTEGER;"),
+            "CONST c1 = 5; VAR v1: INTEGER; p: POINTER TO RECORD END;"),
     pass("T = ARRAY 10 OF INTEGER",
          "T = ARRAY 10 OF BOOLEAN",
          "T = ARRAY 1 + 2 OF INTEGER",
@@ -307,6 +307,8 @@ var testSuite = {
           "'INTEGER' constant expression expected, got 'BOOLEAN'"],
          ["T = ARRAY v1 OF INTEGER",
           "constant expression expected as ARRAY size"],
+         ["T = ARRAY p OF INTEGER",
+          "'INTEGER' constant expression expected, got 'POINTER TO anonymous RECORD'"],
          ["T = ARRAY c1 - 10 OF INTEGER",
           "array size must be greater than 0, got -5"],
          ["T = ARRAY ORD({0..5} >= {0..8}) OF INTEGER",
@@ -625,7 +627,8 @@ var testSuite = {
          "proc1 := proc1",
          "proc2 := NIL",
          "a[1] := 2"),
-    fail(["i := b", "type mismatch: 'i' is 'INTEGER' and cannot be assigned to 'BOOLEAN' expression"],
+    fail(["i = 0", "did you mean ':=' (statement expected, got expression)?"],
+         ["i := b", "type mismatch: 'i' is 'INTEGER' and cannot be assigned to 'BOOLEAN' expression"],
          ["c := i", "cannot assign to constant"],
          ["ch := \"AB\"",
           "type mismatch: 'ch' is 'CHAR' and cannot be assigned to 'multi-character string' expression"],
@@ -644,8 +647,12 @@ var testSuite = {
           "type mismatch: 'a[0]' is 'INTEGER' and cannot be assigned to 'BOOLEAN' expression"],
          ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[TRUE] := 1 END",
           "'INTEGER' expression expected, got 'BOOLEAN'"],
+         ["VAR a: ARRAY 10 OF INTEGER; p: POINTER TO RECORD END; BEGIN a[p] := 1 END",
+          "'INTEGER' expression expected, got 'POINTER TO anonymous RECORD'"],
          ["VAR i: INTEGER; BEGIN i[0] := 1 END",
           "ARRAY expected, got 'INTEGER'"],
+         ["VAR p: POINTER TO RECORD END; BEGIN p[0] := 1 END",
+          "ARRAY expected, got 'POINTER TO anonymous RECORD'"],
          ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[0][0] := 1 END",
           "ARRAY expected, got 'INTEGER'"],
          ["VAR a: ARRAY 10 OF BOOLEAN; BEGIN a[0,0] := TRUE END",
@@ -706,17 +713,19 @@ var testSuite = {
     ),
 "IF statement": testWithContext(
     context(Grammar.statement,
-            "VAR b1: BOOLEAN; i1: INTEGER;"),
+            "VAR b1: BOOLEAN; i1: INTEGER; p: POINTER TO RECORD END;"),
     pass("IF b1 THEN i1 := 0 END",
          "IF FALSE THEN i1 := 0 ELSE i1 := 1 END",
          "IF TRUE THEN i1 := 0 ELSIF FALSE THEN i1 := 1 ELSE i1 := 2 END"),
     fail(["IF i1 THEN i1 := 0 END", "'BOOLEAN' expression expected, got 'INTEGER'"],
          ["IF b1 THEN i1 := 0 ELSIF i1 THEN i1 := 2 END",
-          "'BOOLEAN' expression expected, got 'INTEGER'"])
+          "'BOOLEAN' expression expected, got 'INTEGER'"],
+         ["IF p THEN i1 := 0 END",
+          "'BOOLEAN' expression expected, got 'POINTER TO anonymous RECORD'"])
     ),
 "CASE statement": testWithContext(
     context(Grammar.statement,
-            "CONST ci = 15; cc = \"A\";   VAR c1: CHAR; b1: BOOLEAN; i1, i2: INTEGER;"),
+            "CONST ci = 15; cc = \"A\";   VAR c1: CHAR; b1: BOOLEAN; i1, i2: INTEGER; p: POINTER TO RECORD END;"),
     pass("CASE i1 OF END",
          "CASE i1 OF 0: b1 := TRUE END",
          "CASE c1 OF \"A\": b1 := TRUE END",
@@ -735,6 +744,8 @@ var testSuite = {
          ["CASE b1 OF END", "'INTEGER' or 'CHAR' expected as CASE expression"],
          ["CASE i1 OF \"A\": b1 := TRUE END",
           "label must be 'INTEGER' (the same as case expression), got 'CHAR'"],
+         ["CASE i1 OF p: b1 := TRUE END",
+          "'p' is not a constant"],
          ["CASE c1 OF \"A\", 1: b1 := TRUE END",
           "label must be 'CHAR' (the same as case expression), got 'INTEGER'"],
          ["CASE c1 OF \"A\"..1: b1 := TRUE END",
@@ -757,7 +768,7 @@ var testSuite = {
     ),
 "FOR statement": testWithContext(
     context(Grammar.statement,
-            "CONST c = 15; VAR b: BOOLEAN; i, n: INTEGER;"),
+            "CONST c = 15; VAR b: BOOLEAN; i, n: INTEGER; p: POINTER TO RECORD END;"),
     pass("FOR i := 0 TO 10 DO n := 1 END",
          "FOR i := 0 TO 10 BY 5 DO b := TRUE END",
          "FOR i := 0 TO n DO b := TRUE END",
@@ -769,22 +780,29 @@ var testSuite = {
          ["FOR c := 0 TO 10 DO END", "'c' is not a variable"],
          ["FOR i := TRUE TO 10 DO n := 1 END",
           "'INTEGER' expression expected to assign 'i', got 'BOOLEAN'"],
+         ["FOR i := p TO 10 DO n := 1 END",
+          "'INTEGER' expression expected to assign 'i', got 'POINTER TO anonymous RECORD'"],
+         ["FOR i := 0 TO p DO n := 1 END",
+          "'INTEGER' expression expected as 'TO' parameter, got 'POINTER TO anonymous RECORD'"],
          ["FOR i := 0 TO TRUE DO END",
           "'INTEGER' expression expected as 'TO' parameter, got 'BOOLEAN'"],
          ["FOR i := 0 TO 10 BY n DO END",
           "constant expression expected as 'BY' parameter"],
+         ["FOR i := 0 TO 10 BY p DO END",
+          "'INTEGER' expression expected as 'BY' parameter, got 'POINTER TO anonymous RECORD'"],
          ["FOR i := 0 TO 10 BY TRUE DO END",
           "'INTEGER' expression expected as 'BY' parameter, got 'BOOLEAN'"],
          ["FOR i := 0 TO 10 DO - END",
           "END expected (FOR)"])
     ),
 "logical operators": testWithContext(
-    context(Grammar.statement, "VAR b1, b2: BOOLEAN; i1: INTEGER;"),
+    context(Grammar.statement, "VAR b1, b2: BOOLEAN; i1: INTEGER; p: POINTER TO RECORD END;"),
     pass("b1 := b1 OR b2",
          "b1 := b1 & b2",
          "b1 := ~b2"),
     fail(["b1 := i1 OR b2", "BOOLEAN expected as operand of 'OR', got 'INTEGER'"],
          ["b1 := b1 OR i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"],
+         ["b1 := p OR b1", "BOOLEAN expected as operand of 'OR', got 'POINTER TO anonymous RECORD'"],
          ["b1 := i1 & b2", "BOOLEAN expected as operand of '&', got 'INTEGER'"],
          ["b1 := b1 & i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"],
          ["b1 := ~i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"])
@@ -1004,7 +1022,7 @@ var testSuite = {
 "procedure call": testWithContext(
     context(Grammar.statement,
             "TYPE ProcType = PROCEDURE;" +
-            "VAR notProcedure: INTEGER;" +
+            "VAR notProcedure: INTEGER; ptr: POINTER TO RECORD END;" +
             "PROCEDURE p; END p;" +
             "PROCEDURE p1(i: INTEGER); END p1;" +
             "PROCEDURE p2(i: INTEGER; b: BOOLEAN); END p2;" +
@@ -1015,6 +1033,7 @@ var testSuite = {
          "p1(1 + 2)",
          "p2(1, TRUE)"),
     fail(["notProcedure", "PROCEDURE expected, got 'INTEGER'"],
+         ["ptr()", "PROCEDURE expected, got 'POINTER TO anonymous RECORD'"],
          ["p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
          ["p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'"],
          ["p()()", "not parsed"],