Closed theodore-norvell closed 5 years ago
After the above is done, we can add the "Loc" operator to the language. This operator has one operand which is an LContext. If the loc itself is evaluated in an RContext it skips the dereference that is normal in an RContext. That is, the loc operator evaluates to the same value its operand evaluates to regardless of whether that value is a location and regardless of whether the loc operand occurs in an R-Context or an L-Context.
Example 0
Assign( Var[a], Loc(Var[b]) )
where a is bound to a location loc0 and b is bound to a location loc1. Var[a] evaluates to loc0. Var[b] evaluates to loc1. Loc(Var[b]]) evaluates to loc1. The whole expression evaluates to () with the side effect that the value of loc0 becomes loc1.
Example 1
Assign( Loc(Var[a]), Loc(Var[b]) )
where a is bound to a location loc0 and b is bound to a location loc1. Equivalent to example 0 except that Loc(Var[a]) evaluates to loc0. The use of Loc in an L-Context is superfluous.
Example 2
Assign( Tuple( Var[a], Var[b] ),
Tuple( Loc( Var[c] ), Loc( Var[d] ) ) )
where a is bound to a location loc0 and b is bound to a location loc1 and c to loc2 and d to loc3. Changes the value of loc0 to be loc2 and the value of loc1 to be loc3.
Example 3
Assign( Tuple( Var[a], Var[b] ),
Loc( Tuple( Var[c], Var[d] ) ) )
Equivalent to the above. SInce Tuples evaluate thei children in the same context as themselves, the Var[c] and Var[d] are in L-contexts. Thus the Tuple operator evaluates to a tuple of two locations. The Loc operator evaluates to the same tuple.
Example 4
Call( Var[f], Loc( Var[a] ) )
The argument is loc0.
Example 5
Lambda( Params(),
noType,
ExprSeq( VarDecl[loc]( Var[r], noType, noExp ),
Loc( Var[r] ) )
When this function is called it creates a new location and its result is that location. The body of a function an ExprSeq evaluated in an R-context. The ExprSeq itself passes on this context to its children (rather like a a tuple). The last child of the Expr sequence is thus evaluated in an R-Context. But since it's a Loc this has no effect. The value of the Loc expression is a location. The value of the ExprSeq is the same location and the result of the function is the same location.
Example 6
Assign( If( Var[a], ExprSeq( Var[b] ), ExprSeq( Var[c] ) ), numerLiteral[42] )
The first operand of an If is an R-context, but the other two operands inherit the context of the If, which in this case is L-Context. Thus no Loc operators are needed here. (Although it would be harmless to add them.)
This is all done.
We need to add a new type LocV for location values. For now LocV objects need only a single field. Call it "value". SInce the value will need to be updated and may be unitialized, the "value" field will b of type
TVar<Option<Value>>
.Processing VarDecls, including parameters, when the VarDecl is not a constant, we need to create a new location. Then the value of the field will be the location value.
We can then change the implementation of fields so that fields can be initialized once, but can not have their value changed. Also we can get rid of several fields in the Field class (type, isConstant, and isDeclared). The value field should be a TVar<Option>.
Drawing location values in the animatorHelpers. Just make a thick white box around the value. For now, locations that are referenced more than once, will just be drawn more than once. Later we might do something with arrows. For fields or locations that have not been initialized, the value can be drawn as nothing at all.
In the interpreter, we need to introduce automatic value coercions from locations to their values. Take for example an assignment
when Var[a] is stepped, its value will be the value of variable a. Let's suppose that value is a location value (an object of class LocV). When Var[b] is stepped, it's value might or might not be location value. If it's not, great. But if it a location value, we should fetch a value from the location. So we can see that there are two kinds of evaluation contexts. One kind (well call it L-context) where location values should be left alone. And another kind (we'll call it R-context) where they should be fetched from. In the case of assignment, the first operand is an L-context and the second operand is an R-context. We can notate this as
Most operands are R. Operands of function calls are always R. The first operand of If and While is R.
In some cases, the context is inherited. For example the Tuple operator is
meaning that in an R context, its operands are R and in an L context its operands are R. For example consider
Suppose a, b, x, and y are all fields with LocV objects as values and each of those LocV objects contains a NumberV object. Then the two operands of Assign should evaluate to a tuple of two locations and a tuple of two numbers.
Implementation
In the implementation, we give the selectors one more parameter and one more duty. The parameter is either LCONTEXT or RCONTEXT. The duty is that when a selector selects a node it also sends the context to the virtual machine state where it stored as part of the top evaluation object. (So we need another field class Evaluation.
When we finish the step by mapping the selected (pending) node to a value we check the context stored in the evaluation object. If it is RCONTEXT and the value is a LocV, we replace the value with the value fetched from the location. (We should only need to change code in the vms)
This implementation is a small change to the selectors and no change to the steppers.
Assignment
The assignment operator now becomes much simpler. Its first operand must evaluate to a locationV. Othewise it's an error. The right hand side must evaluate to a value. If there is no error, we update the contents of the location. This accomodates LHS that are dots or indexing operations or even functions that return locations.
We can also extend assignment to tuples and nested tuples as follows. We say that two values are compatible as follows:
Now if the LHS value and RHS value are not compatible, we have an error. If they are compatible we can do the assignment with the following recursive algorithm