Skip to content

Commit

Permalink
Fix typeguard for value variables (Issue #16).
Browse files Browse the repository at this point in the history
  • Loading branch information
vladfolts committed Sep 5, 2013
1 parent 8882ebb commit b486363
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/code.js
Expand Up @@ -59,7 +59,7 @@ var Expression = Class.extend({
if (this.__type instanceof Type.Array || this.__type instanceof Type.Record)
return this;
var info = this.__designator.info();
if (!(info instanceof Type.Variable && info.isVar()))
if (!(info instanceof Type.VariableRef))
return this;
return new Expression(this.__code + ".get()", this.__type);
},
Expand All @@ -68,7 +68,7 @@ var Expression = Class.extend({
return this;

var info = this.__designator.info();
if (info instanceof Type.Variable && info.isVar())
if (info instanceof Type.VariableRef)
return this;

return new Expression(this.__designator.refCode(), this.__type);
Expand Down
28 changes: 20 additions & 8 deletions src/context.js
Expand Up @@ -261,7 +261,7 @@ exports.Identdef = ChainedContext.extend({
});

exports.Designator = ChainedContext.extend({
init: function DesignatorContext(context){
init: function Context$Designator(context){
ChainedContext.prototype.init.call(this, context);
this.__currentType = undefined;
this.__info = undefined;
Expand All @@ -284,14 +284,19 @@ exports.Designator = ChainedContext.extend({
},
setIdent: function(id){
var t = this.__currentType;
if (t instanceof Type.Pointer)
var isReadOnly = this.__info instanceof Type.Variable
&& this.__info.isReadOnly();
if (t instanceof Type.Pointer){
this.__handleDeref();
isReadOnly = false;
}
else if (!(t instanceof Type.Record
|| t instanceof Type.Module
|| t instanceof Module.AnyType))
throw new Errors.Error("cannot designate '" + t.description() + "'");

this.__denote(id);
this.__info = new Type.Variable(this.__currentType, isReadOnly);
this.__scope = undefined;
},
codeGenerator: function(){return this.__code;},
Expand All @@ -310,8 +315,9 @@ exports.Designator = ChainedContext.extend({
throw new Errors.Error("index out of bounds: maximum possible index is "
+ (type.length() - 1)
+ ", got " + value );

this.__currentType = type.elementsType();
this.__info = new Type.Variable(this.__currentType, false, this.__info.isReadOnly());
this.__info = new Type.Variable(this.__currentType, this.__info.isReadOnly());
},
handleLiteral: function(s){
if (s == "]" || s == ","){
Expand All @@ -324,18 +330,22 @@ exports.Designator = ChainedContext.extend({
this.__derefCode = this.__code.result();
this.__code = new Code.SimpleGenerator();
}
else if (s == "^")
else if (s == "^"){
this.__handleDeref();
this.__info = new Type.VariableRef(this.__currentType);
}
},
__handleDeref: function(){
if (!(this.__currentType instanceof Type.Pointer))
throw new Errors.Error("POINTER TO type expected, got '"
+ this.__currentType.description() + "'");
this.__currentType = this.__currentType.baseType();
this.__info = new Type.Variable(this.__currentType, false, false);
},
handleTypeCast: function(type){
if (this.__currentType instanceof Type.Record){
if (!(this.__info instanceof Type.VariableRef))
throw new Errors.Error(
"invalid type cast: a value variable and cannot be used in typeguard");
if (!(type instanceof Type.Record))
throw new Errors.Error(
"invalid type cast: RECORD type expected as an argument of RECORD type guard, got '"
Expand Down Expand Up @@ -378,7 +388,7 @@ exports.Designator = ChainedContext.extend({
return this.rtl().makeRef(this.__derefCode, this.__propCode);
if (!(this.__currentType instanceof Type.Array)
&& !(this.__currentType instanceof Type.Record)
&& this.__info instanceof Type.Variable && !this.__info.isVar())
&& !(this.__info instanceof Type.VariableRef))
return "{set: function($v){" + code + " = $v;}, get: function(){return " + code + ";}}";
return code;
}
Expand Down Expand Up @@ -486,7 +496,9 @@ exports.ProcDecl = ChainedContext.extend({
if (name == this.__id.id())
throw new Errors.Error("argument '" + name + "' has the same name as procedure");
var readOnly = !arg.isVar && (arg.type instanceof Type.Array);
var s = new Symbol.Symbol(name, new Type.Variable(arg.type, arg.isVar, readOnly));
var v = arg.isVar ? new Type.VariableRef(arg.type)
: new Type.Variable(arg.type, readOnly);
var s = new Symbol.Symbol(name, v);
this.currentScope().addSymbol(s);

var code = this.codeGenerator();
Expand Down Expand Up @@ -812,7 +824,7 @@ exports.MulOperator = ChainedContext.extend({

function writeDerefDesignatorCode(designator, code){
var info = designator.info();
if (info instanceof Type.Variable && info.isVar())
if (info instanceof Type.VariableRef)
code.write(".get()");
}

Expand Down
14 changes: 8 additions & 6 deletions src/operator.js
Expand Up @@ -92,34 +92,36 @@ function assign(left, right, context){
+ "' is '" + leftType.description()
+ "' and cannot be assigned to '" + rightType.description() + "' expression");

if (isArray && rightType instanceof Type.Array)
if (isArray && rightType instanceof Type.Array){
if (leftType.length() === undefined)
throw new Errors.Error("'" + leftCode
+ "' is open '" + leftType.description()
+ "' and cannot be assigned");
else if (rightType.length() === undefined)
if (rightType.length() === undefined)
throw new Errors.Error("'" + leftCode
+ "' cannot be assigned to open '"
+ rightType.description() + "'");
else if (leftType.length() != rightType.length())
if (leftType.length() != rightType.length())
throw new Errors.Error("array size mismatch: '" + leftCode
+ "' has size " + leftType.length()
+ " and cannot be copied to the array with size "
+ rightType.length());
}

if (isArray || rightType instanceof Type.Record)
return context.rtl().copy(rightCode, leftCode);

var castCode = castOperation(context, right.deref()).code();
rightCode = castCode ? castCode : rightCode;
return leftCode + (info.isVar() ? ".set(" + rightCode + ")"
: " = " + rightCode);
return leftCode + (info instanceof Type.VariableRef
? ".set(" + rightCode + ")"
: " = " + rightCode);
}

function makeInplace(code, altOp){
return function(left, right){
var info = left.designator().info();
if (info.isVar())
if (info instanceof Type.VariableRef)
return assign(left, altOp(left, right));
return left.code() + code + right.deref().code();
};
Expand Down
13 changes: 9 additions & 4 deletions src/type.js
Expand Up @@ -153,21 +153,25 @@ exports.Const = Id.extend({
});

var Variable = Id.extend({
init: function Variable(type, isVar, isReadOnly){
init: function Variable(type, isReadOnly){
Id.prototype.init.call(this);
this.__type = type;
this.__isVar = isVar;
this.__isReadOnly = isReadOnly;
},
idType: function(){return this.__isReadOnly ? "read-only variable" : "variable";},
type: function(){return this.__type;},
isVar: function(){return this.__isVar;},
isReadOnly: function(){return this.__isReadOnly;}
});

var VariableRef = Variable.extend({
init: function Type$VariableRef(type){
Variable.prototype.init.call(this, type, false);
}
});

var ExportedVariable = Variable.extend({
init: function ExportedVariable(variable){
Variable.prototype.init.call(this, variable.type(), variable.isVar(), true);
Variable.prototype.init.call(this, variable.type(), true);
},
idType: function(){return "imported variable";}
});
Expand All @@ -188,6 +192,7 @@ var Module = Id.extend({
});

exports.Variable = Variable;
exports.VariableRef = VariableRef;
exports.ExportedVariable = ExportedVariable;
exports.Module = Module;
exports.Type = Type;
Expand Down
2 changes: 1 addition & 1 deletion test/expected/cast.js
Expand Up @@ -47,7 +47,7 @@ var pd1 = null;
var pd2 = null;
var pad = null;

function p(b/*Base*/, d1/*Derived1*/){
function p(b/*VAR Base*/, d1/*VAR Derived1*/){
RTL$.typeGuard(b, Derived1).field1 = 0;
RTL$.typeGuard(b, Derived2).field2 = 1;
RTL$.typeGuard(d1, Derived2).field2 = 2;
Expand Down
2 changes: 1 addition & 1 deletion test/input/cast.ob
Expand Up @@ -15,7 +15,7 @@ VAR
pd2: POINTER TO Derived2;
pad: PAnonymousDerived;

PROCEDURE p(b: Base; d1: Derived1);
PROCEDURE p(VAR b: Base; VAR d1: Derived1);
BEGIN
b(Derived1).field1 := 0;
b(Derived2).field2 := 1;
Expand Down
26 changes: 22 additions & 4 deletions test/test_unit.js
Expand Up @@ -37,7 +37,8 @@ function runAndHandleErrors(action, s, handlerError){
}
catch (x){
if (!(x instanceof Errors.Error))
throw new Error("'" + s + '":\n' + x.stack);
throw new Error("'" + s + "': " + x + "\n"
+ (x.stack ? x.stack : "(no stack)"));

if (handlerError)
handlerError(x);
Expand Down Expand Up @@ -370,10 +371,11 @@ var testSuite = {
"type mismatch: 'pDerived' is 'POINTER TO Derived' and cannot be assigned to 'POINTER TO Base' expression"],
["NIL := p1", "not parsed"])
),
"POINTER cast": testWithContext(
"typeguard": testWithContext(
context(Grammar.expression,
"TYPE Base = RECORD END; PBase = POINTER TO Base; Derived = RECORD (Base) END; PDerived = POINTER TO Derived;"
+ "VAR p1, p2: POINTER TO RECORD END; pBase: POINTER TO Base; pDerived: POINTER TO Derived; i: INTEGER;"),
+ "VAR p1, p2: POINTER TO RECORD END; pBase: POINTER TO Base; pDerived: POINTER TO Derived;"
+ "vb: Base; i: INTEGER;"),
pass("pBase(PDerived)",
"pBase^(Derived)"),
fail(["pDerived(PDerived)",
Expand All @@ -383,7 +385,23 @@ var testSuite = {
["p1(INTEGER)",
"invalid type cast: POINTER type expected as an argument of POINTER type guard, got 'INTEGER'"],
["i(Derived)",
"invalid type cast: 'Derived' is not an extension of 'INTEGER'"])
"invalid type cast: 'Derived' is not an extension of 'INTEGER'"],
["vb(Derived)",
"invalid type cast: a value variable and cannot be used in typeguard"],
["vb(PDerived)",
"invalid type cast: a value variable and cannot be used in typeguard"])
),
"typeguard for VAR argument": testWithContext(
context(Grammar.procedureDeclaration,
"TYPE Base = RECORD END; Derived = RECORD (Base) i: INTEGER END;"
+ "T = RECORD END; TD = RECORD(T) b: Base END;"),
pass("PROCEDURE proc(VAR p: Base); BEGIN p(Derived).i := 1; END proc"),
fail(["PROCEDURE proc(p: Base); BEGIN p(Derived).i := 1; END proc",
"invalid type cast: a value variable and cannot be used in typeguard"],
["PROCEDURE proc(p: TD); BEGIN p.b(Derived).i := 1; END proc",
"invalid type cast: a value variable and cannot be used in typeguard"],
["PROCEDURE proc(VAR p: T); BEGIN p(TD).b(Derived).i := 1; END proc",
"invalid type cast: a value variable and cannot be used in typeguard"])
),
"POINTER relations": testWithContext(
context(Grammar.expression,
Expand Down

0 comments on commit b486363

Please sign in to comment.