Browse Source

implement FLOOR/FLT/LONG/SHORT/INC/DEC

Vladislav Folts 12 years ago
parent
commit
907372e9b0

+ 4 - 3
src/cast.js

@@ -3,6 +3,7 @@ var Code = require("code.js");
 var Type = require("type.js");
 var ArrayType = Type.Array;
 var PointerType = Type.Pointer;
+var ProcedureType = Type.Procedure;
 
 function doNoting(context, e){return e;}
 
@@ -19,7 +20,7 @@ function findPointerBaseType(pBase, pType){
 }
 
 function matchesToNIL(t){
-    return t instanceof PointerType || t.isProcedure();
+    return t instanceof PointerType || t instanceof ProcedureType;
 }
 
 function areTypesMatch(t1, t2){
@@ -27,7 +28,7 @@ function areTypesMatch(t1, t2){
         return true;
     if (t1 instanceof PointerType && t2 instanceof PointerType)
         return areTypesMatch(t1.baseType(), t2.baseType());
-    if (t1.isProcedure() && t2.isProcedure())
+    if (t1 instanceof ProcedureType && t2 instanceof ProcedureType)
         return areProceduresMatch(t1, t2);
     if (t1 == Type.nil && matchesToNIL(t2)
         || t2 == Type.nil && matchesToNIL(t1))
@@ -85,7 +86,7 @@ function implicitCast(from, to){
     }
     else if (from == Type.nil && matchesToNIL(to))
         return doNoting;
-    else if (from.isProcedure() && to.isProcedure()){
+    else if (from instanceof ProcedureType && to instanceof ProcedureType){
         if (areProceduresMatch(from, to))
             return doNoting;
     }

+ 5 - 5
src/context.js

@@ -205,9 +205,9 @@ exports.Designator = ChainedContext.extend({
         if ( t === undefined){
             var s = getSymbol(this.parent(), id);
             var info = s.info();
-            if (s.isType())
+            if (s.isType() || s.isProcedure())
                 this.__currentType = info;
-            else if (s.isVariable() || s.isConst() || s.isProcedure()){
+            else if (s.isVariable() || s.isConst()){
                 this.__currentType = info.type();
             }
             else
@@ -414,7 +414,7 @@ exports.ProcDecl = ChainedContext.extend({
     },
     typeName: function(){return undefined;},
     setType: function(type){
-        var procSymbol = new Symbol(this.__name, new Type.Procedure(type));
+        var procSymbol = new Symbol(this.__name, type);
         this.__outerScope.addSymbol(procSymbol);
         this.__type = type;
     },
@@ -600,7 +600,7 @@ var equalOpTypeCheck = {
         return [basicTypes.int, basicTypes.real, basicTypes.set, basicTypes.bool, basicTypes.char].indexOf(t) != -1
             || (t instanceof Type.Array && t.elementsType() == basicTypes.char)
             || t instanceof Type.Pointer
-            || t.isProcedure()
+            || t instanceof Type.Procedure
             || t == Type.nil;
     }
 };
@@ -1403,7 +1403,7 @@ exports.FieldListDeclaration = ChainedContext.extend({
 });
 
 function assertProcType(type){
-    if (!(type instanceof Procedure.Type) && !(type instanceof Module.AnyType))
+    if (!(type instanceof Type.Procedure) && !(type instanceof Module.AnyType))
         throw new Errors.Error("PROCEDURE expected, got '" + type.name() + "'");
 }
 

+ 176 - 37
src/procedure.js

@@ -35,9 +35,8 @@ var ProcCallGenerator = Class.extend({
         this.__code = new Code.SimpleGenerator();
         this.writeCode(this.prolog());
     },
-    id: function(){return this.__id;},
+    //id: function(){return this.__id;},
     context: function(){return this.__context;},
-    codeGenerator: function(){return this.__code;},
     handleArgument: function(e){
         var pos = this.__argumentsCount++;
         var isVarArg = false;
@@ -67,7 +66,7 @@ var ProcCallGenerator = Class.extend({
     },
     callExpression: function(){
         this.writeCode(this.epilog());
-        return new Code.Expression(this.codeGenerator().result(),
+        return new Code.Expression(this.__code.result(),
                                    this.resultType());
     },
     resultType: function(){return this.__type ? this.__type.result() : undefined;},
@@ -102,21 +101,15 @@ var ProcCallGenerator = Class.extend({
         return new CheckArgumentResult(arg.type, arg.isVar, castOperation);
     },
     epilog: function(){return ")";},
-    writeCode: function(s){this.codeGenerator().write(s);}
+    writeCode: function(s){this.__code.write(s);}
 });
 
-var ProcType = Type.Basic.extend({
-    init: function ProcType(name, args, result, callGeneratorFactory){
-        Type.Basic.prototype.init.bind(this)(name, "null");
-        this.__arguments = args;
-        this.__result = result;
-        this.__callGeneratorFactory = callGeneratorFactory
-            ? callGeneratorFactory
-            : function(context, id, type){
-                return new ProcCallGenerator(context, id, type);
-            };
+var DefinedProc = Type.Procedure.extend({
+    init: function DefinedProc(name){
+        Type.Procedure.prototype.init.call(this, name);
+        this.__arguments = undefined;
+        this.__result = undefined;
     },
-    isProcedure: function(){return true;},
     define: function(args, result){
         this.__arguments = args;
         this.__result = result;
@@ -131,7 +124,7 @@ var ProcType = Type.Basic.extend({
             + (this.__result ? ": " + this.__result.name() : "");
         },
     callGenerator: function(context, id){
-        return this.__callGeneratorFactory(context, id, this);
+        return new ProcCallGenerator(context, id, this);
     },
     __dumpProcArgs: function(){
         if (!this.__arguments.length)
@@ -148,6 +141,20 @@ var ProcType = Type.Basic.extend({
     }
 });
 
+var ProcType = Type.Procedure.extend({
+    init: function ProcType(name, args, result, callGeneratorFactory){
+        Type.Procedure.prototype.init.call(this, name);
+        this.__arguments = args;
+        this.__result = result;
+        this.__callGeneratorFactory = callGeneratorFactory;
+    },
+    arguments: function(){return this.__arguments;},
+    result: function(){return this.__result;},
+    callGenerator: function(context, id){
+        return this.__callGeneratorFactory(context, id, this);
+    }
+});
+
 var TwoArgToOperatorProcCallGenerator = ProcCallGenerator.extend({
     init: function TwoArgToOperatorProcCallGenerator(context, id, type, operator){
         ProcCallGenerator.prototype.init.call(this, context, id, type);
@@ -193,8 +200,62 @@ function setBitImpl(name, op){
             return new TwoArgToOperatorProcCallGenerator(
                 context, id, type, operator);
             });
-    var type = new Type.Procedure(proc);
-    var symbol = new Type.Symbol(name, type);
+    var symbol = new Type.Symbol(name, proc);
+    return symbol;
+}
+
+function incImpl(name, unary, op){
+    var args = [new Arg(Type.basic.int, true), new Arg(Type.basic.int, false)];
+    function operator(x, y){
+        if (!y){
+            code = unary + x.code();
+            return new Code.Expression(
+                code, undefined, undefined, undefined, precedence.prefix);
+        }
+        else {
+            var value = y.constValue();
+            if (value === undefined)
+                throw new Errors.Error("constant expected as second argument of " + name);
+            var comment = y.isTerm() ? "" : "/*" + y.code() + "*/";
+            var valueCode = value + comment;
+            var code = op(x.code(), valueCode);
+            return new Code.Expression(
+                code, undefined, undefined, undefined, precedence.assignment);
+        }
+    }
+    var CallGenerator = ProcCallGenerator.extend({
+        init: function IncProcCallGenerator(context, id, type, operator){
+            ProcCallGenerator.prototype.init.call(this, context, id, type);
+            this.__operator = operator;
+            this.__firstArgument = undefined;
+            this.__secondArgument = undefined;
+        },
+        prolog: function(id){return "";},
+        handleArgument: function(e){
+            if (!this.__firstArgument)
+                this.__firstArgument = e;
+            else
+                this.__secondArgument = e;
+            ProcCallGenerator.prototype.handleArgument.call(this, e);
+        },
+        writeArgumentCode: function(){},
+        checkArgumentsCount: function(count){
+            checkVariableArgumentsCount(1, 2, count);
+        },
+        epilog: function(type){return "";},
+        callExpression: function(){
+            return operator(this.__firstArgument, this.__secondArgument);
+        }
+    });
+    var proc = new ProcType(
+        "predefined procedure " + name,
+        args,
+        undefined,
+        function(context, id, type){
+            return new CallGenerator(
+                context, id, type, operator);
+            });
+    var symbol = new Type.Symbol(name, proc);
     return symbol;
 }
 
@@ -224,11 +285,44 @@ function bitShiftImpl(name, op){
         function(context, id, type){
             return new CallGenerator(context, id, type);
         });
-    var type = new Type.Procedure(proc);
-    var symbol = new Type.Symbol(name, type);
+    var symbol = new Type.Symbol(name, proc);
     return symbol;
 }
 
+function longShort(name){
+    var CallGenerator = ProcCallGenerator.extend({
+        init: function FloorProcCallGenerator(context, id, type){
+            ProcCallGenerator.prototype.init.call(this, context, id, type);
+            this.__callExpression = undefined;
+        },
+        prolog: function(){return "";},
+        epilog: function(){return "";},
+        checkArgument: function(pos, e){
+            var result = ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+            this.__callExpression = e;
+            return result;
+        },
+        callExpression: function(){return this.__callExpression;}
+    });
+    var args = [new Arg(Type.basic.real, false)];
+    var proc = new ProcType(
+        "predefined procedure " + name,
+        args,
+        Type.basic.real,
+        function(context, id, type){
+            return new CallGenerator(context, id, type);
+        });
+    var symbol = new Type.Symbol(name, proc);
+    return symbol;
+}
+
+function checkVariableArgumentsCount(min, max, actual){
+    if (actual < min)
+        throw new Errors.Error("at least " + min + " argument expected, got " + actual);
+    if (actual > max )
+        throw new Errors.Error("at most " + max + " arguments expected, got " + actual);
+}
+
 exports.predefined = [
     function(){
         var NewProcCallGenerator = ProcCallGenerator.extend({
@@ -252,13 +346,13 @@ exports.predefined = [
 
         var name = "NEW";
         var args = [new Arg(undefined, true)];
-        var type = new Type.Procedure(new ProcType(
+        var type = new ProcType(
             "predefined procedure NEW",
             args,
             undefined,
             function(context, id, type){
                 return new NewProcCallGenerator(context, id, type);
-            }));
+            });
         var symbol = new Type.Symbol(name, type);
         return symbol;
     }(),
@@ -281,13 +375,13 @@ exports.predefined = [
 
         var name = "LEN";
         var args = [new Arg(new Type.Array("ARRAY OF any type"), false)];
-        var type = new Type.Procedure(new ProcType(
+        var type = new ProcType(
             "predefined procedure LEN",
             args,
             Type.basic.int,
             function(context, id, type){
                 return new LenProcCallGenerator(context, id, type);
-            }));
+            });
         var symbol = new Type.Symbol(name, type);
         return symbol;
     }(),
@@ -301,13 +395,13 @@ exports.predefined = [
         });
         var name = "ODD";
         var args = [new Arg(Type.basic.int, false)];
-        var type = new Type.Procedure(new ProcType(
+        var type = new ProcType(
             "predefined procedure ODD",
             args,
             Type.basic.bool,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
-            }));
+            });
         var symbol = new Type.Symbol(name, type);
         return symbol;
     }(),
@@ -318,8 +412,7 @@ exports.predefined = [
             },
             prolog: function(){return this.context().rtl().assertId() + "(";},
             checkArgumentsCount: function(count){
-                if (count != 1 && count != 2 )
-                    throw new Errors.Error("1 or 2 arguments expected, got " + this.__argumentsCount);
+                checkVariableArgumentsCount(1, 2, count);
             }
         });
 
@@ -331,12 +424,13 @@ exports.predefined = [
             function(context, id, type){
                 return new AssertProcCallGenerator(context, id, type);
             });
-        var type = new Type.Procedure(proc);
-        var symbol = new Type.Symbol("ASSERT", type);
+        var symbol = new Type.Symbol("ASSERT", proc);
         return symbol;
     }(),
     setBitImpl("INCL", function(x, y){return x + " |= " + y;}),
     setBitImpl("EXCL", function(x, y){return x + " &= ~(" + y + ")";}),
+    incImpl("INC", "++", function(x, y){return x + " += " + y;}),
+    incImpl("DEC", "--", function(x, y){return x + " -= " + y;}),
     function(){
         var CallGenerator = ProcCallGenerator.extend({
             init: function AbsProcCallGenerator(context, id, type){
@@ -362,10 +456,55 @@ exports.predefined = [
             function(context, id, type){
                 return new CallGenerator(context, id, type);
             });
-        var type = new Type.Procedure(proc);
-        var symbol = new Type.Symbol("ABS", type);
+        var symbol = new Type.Symbol("ABS", proc);
+        return symbol;
+    }(),
+    function(){
+        var CallGenerator = ProcCallGenerator.extend({
+            init: function FloorProcCallGenerator(context, id, type){
+                ProcCallGenerator.prototype.init.call(this, context, id, type);
+            },
+            prolog: function(){return "Math.floor(";},
+        });
+        var args = [new Arg(Type.basic.real, false)];
+        var proc = new ProcType(
+            "predefined procedure FLOOR",
+            args,
+            Type.basic.int,
+            function(context, id, type){
+                return new CallGenerator(context, id, type);
+            });
+        var symbol = new Type.Symbol("FLOOR", proc);
         return symbol;
     }(),
+    function(){
+        var CallGenerator = ProcCallGenerator.extend({
+            init: function FloorProcCallGenerator(context, id, type){
+                ProcCallGenerator.prototype.init.call(this, context, id, type);
+                this.__callExpression = undefined;
+            },
+            prolog: function(){return "";},
+            epilog: function(){return "";},
+            checkArgument: function(pos, e){
+                var result = ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+                this.__callExpression = new Code.Expression(e.code(), Type.basic.real, undefined, e.constValue());
+                return result;
+            },
+            callExpression: function(){return this.__callExpression;}
+        });
+        var args = [new Arg(Type.basic.int, false)];
+        var proc = new ProcType(
+            "predefined procedure FLT",
+            args,
+            Type.basic.real,
+            function(context, id, type){
+                return new CallGenerator(context, id, type);
+            });
+        var symbol = new Type.Symbol("FLT", proc);
+        return symbol;
+    }(),
+    longShort("LONG"),
+    longShort("SHORT"),
     bitShiftImpl("LSL", op.lsl),
     bitShiftImpl("ASR", op.asr),
     bitShiftImpl("ROR", op.ror),
@@ -405,13 +544,13 @@ exports.predefined = [
         var name = "ORD";
         var argType = new Type.Basic("CHAR or BOOLEAN or SET");
         var args = [new Arg(argType, false)];
-        var type = new Type.Procedure(new ProcType(
+        var type = new ProcType(
             "predefined procedure " + name,
             args,
             Type.basic.int,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
-            }));
+            });
         var symbol = new Type.Symbol(name, type);
         return symbol;
     }(),
@@ -430,17 +569,17 @@ exports.predefined = [
             callExpression: function(){return this.__callExpression;}
         });
         var name = "CHR";
-        var type = new Type.Procedure(new ProcType(
+        var type = new ProcType(
             "predefined procedure " + name,
             [new Arg(Type.basic.int, false)],
             Type.basic.char,
             function(context, id, type){
                 return new CallGenerator(context, id, type);
-            }));
+            });
         var symbol = new Type.Symbol(name, type);
         return symbol;
     }()
     ];
 
 exports.CallGenerator = ProcCallGenerator;
-exports.Type = ProcType;
+exports.Type = DefinedProc;

+ 7 - 10
src/type.js

@@ -4,15 +4,14 @@ var Class = require("rtl.js").Class;
 var Errors = require("errors.js");
 
 var Id = Class.extend({
-	init: function Id(){},
+	init: function Id(){}
 });
 
 var Type = Id.extend({
 	init: function Type(){
-		Id.prototype.init.bind(this)();
+		Id.prototype.init.call(this);
 	},
 	idType: function(){return "type";},
-	isProcedure: function(){return false;},
 });
 
 exports.Type = Type;
@@ -31,7 +30,7 @@ exports.String = Type.extend({
 
 var BasicType = Type.extend({
 	init: function BasicType(name, initValue){
-		Type.prototype.init.bind(this)();
+		Type.prototype.init.call(this);
 		this.__name = name;
 		this.__initValue = initValue;
 	},
@@ -152,13 +151,11 @@ exports.Variable = Id.extend({
 	isReadOnly: function(){return this.__isReadOnly;}
 });
 
-exports.Procedure = Id.extend({
-	init: function Procedure(type){
-		Id.prototype.init.bind(this)();
-		this.__type = type;
+exports.Procedure = BasicType.extend({
+	init: function Procedure(name){
+		BasicType.prototype.init.call(this, name, "null");
 	},
-	idType: function(){return "procedure";},
-	type: function(){return this.__type;}
+	idType: function(){return "procedure";}
 });
 
 var Symbol = Class.extend({

+ 4 - 0
test/expected/floor.js

@@ -0,0 +1,4 @@
+var m = function (){
+var i = 0;
+i = Math.floor(1.23);
+}();

+ 4 - 0
test/expected/flt.js

@@ -0,0 +1,4 @@
+var m = function (){
+var r = 0;
+r = 123;
+}();

+ 12 - 0
test/expected/inc_dec.js

@@ -0,0 +1,12 @@
+var m = function (){
+var ic = 10;
+var i = 0;
+++i;
+i += 2;
+i += 15/*3 * 5*/;
+i += 10/*ic*/;
+--i;
+i -= 2;
+i -= 15/*3 * 5*/;
+i -= 10/*ic*/;
+}();

+ 8 - 0
test/expected/long_short.js

@@ -0,0 +1,8 @@
+var m = function (){
+var r = 0;
+var lr = 0;
+r = lr;
+lr = r;
+r = 1.23;
+lr = 1.23;
+}();

+ 7 - 0
test/input/floor.ob

@@ -0,0 +1,7 @@
+MODULE m;
+
+VAR i: INTEGER;
+
+BEGIN
+    i := FLOOR(1.23);
+END m.

+ 7 - 0
test/input/flt.ob

@@ -0,0 +1,7 @@
+MODULE m;
+
+VAR r: REAL;
+
+BEGIN
+    r := FLT(123);
+END m.

+ 16 - 0
test/input/inc_dec.ob

@@ -0,0 +1,16 @@
+MODULE m;
+
+CONST ic = 10;
+VAR i: INTEGER;
+
+BEGIN
+	INC(i);
+	INC(i, 2);
+	INC(i, 3 * 5);
+	INC(i, ic);
+
+	DEC(i);
+	DEC(i, 2);
+	DEC(i, 3 * 5);
+	DEC(i, ic);
+END m.

+ 10 - 0
test/input/long_short.ob

@@ -0,0 +1,10 @@
+MODULE m;
+
+VAR r: REAL; lr: LONGREAL;
+
+BEGIN
+    r := SHORT(lr);
+    lr := LONG(r);
+    r := SHORT(1.23);
+    lr := LONG(1.23);
+END m.

+ 49 - 1
test/test_unit.js

@@ -395,6 +395,34 @@ identifier: function(){
          ["i := ABS(i, i)", "1 argument(s) expected, got 2"]
          )
     ),
+"FLOOR": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER; r: REAL;"),
+    pass("i := FLOOR(r)"),
+    fail(["i := FLOOR(i)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'REAL'"],
+         ["i := FLOOR(r, r)", "1 argument(s) expected, got 2"]
+         )
+    ),
+"FLT": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER; r: REAL;"),
+    pass("r := FLT(i)"),
+    fail(["r := FLT(r)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
+         ["i := FLT(i, i)", "1 argument(s) expected, got 2"]
+         )
+    ),
+"LONG": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER; r: REAL; lr: LONGREAL;"),
+    pass("lr := LONG(r)"),
+    fail(["lr := LONG(i)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'REAL'"],
+         ["lr := LONG(r, r)", "1 argument(s) expected, got 2"]
+         )
+    ),
+"SHORT": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER; r: REAL; lr: LONGREAL;"),
+    pass("r := SHORT(lr)"),
+    fail(["r := SHORT(i)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'REAL'"],
+         ["r := SHORT(lr, lr)", "1 argument(s) expected, got 2"]
+         )
+    ),
 "LSL": testWithContext(
     context(Grammar.statement,
             "VAR i: INTEGER; r: REAL; c: CHAR;"),
@@ -450,6 +478,26 @@ identifier: function(){
     pass("ch := CHR(i)"),
     fail(["ch := CHR(ch)", "type mismatch for argument 1: 'CHAR' cannot be converted to 'INTEGER'"])
 ),
+"INC": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER;"),
+    pass("INC(i)",
+         "INC(i, 3)"),
+    fail(["INC(i + i)", "expression cannot be used as VAR parameter"],
+         ["INC(i, i)", "constant expected as second argument of INC"],
+         ["INC()", "at least 1 argument expected, got 0"],
+         ["INC(i, 1, 2)", "at most 2 arguments expected, got 3"]
+         )
+),
+"DEC": testWithContext(
+    context(Grammar.statement, "VAR i: INTEGER;"),
+    pass("DEC(i)",
+         "DEC(i, 3)"),
+    fail(["DEC(i + i)", "expression cannot be used as VAR parameter"],
+         ["DEC(i, i)", "constant expected as second argument of DEC"],
+         ["DEC()", "at least 1 argument expected, got 0"],
+         ["DEC(i, 1, 2)", "at most 2 arguments expected, got 3"]
+         )
+),
 "assignment statement": function(){
     var test = setupWithContext(
           Grammar.statement
@@ -1009,7 +1057,7 @@ assert: function(){
     var test = setup(Grammar.statement);
     test.parse("ASSERT(TRUE)");
     test.parse("ASSERT(TRUE, 123)");
-    test.expectError("ASSERT()", "1 or 2 arguments expected, got 0");
+    test.expectError("ASSERT()", "at least 1 argument expected, got 0");
     test.expectError("ASSERT(123, TRUE)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'BOOLEAN'");
 },
 IMPORT: function(){