# Interpretation of Assignment Expressions
The initial way to assign a binding a value in Egg is via def
ine.
This construct acts as a way both to define new bindings and to give existing ones a new value.
As a consequence, when you try to give a nonlocal binding a new value, you will end up defining a local one with the same name instead.
The problem here is to add a special interpreter for set(x, value)
, similar to def(x, value)
, which
- Does not create a new binding in the current scope
- Updates the entry in the nearest scope (local, parent, grand-parent, etc.) in which a binding with name
x
exist - If no binding with name
x
exists, throws aReferenceError
.
This example illustrates the kind of behavior we want:
➜ eloquentjsegg git:(private2122) cat examples/scope.egg
do(
def(x,9), /* def crea una nueva variable local */
def(f, fun( /* fun creates a new scope */
do(
def(x, 4),
print(x) # 4
)
)),
def(g, fun(set(x, 8))), /* set no crea una nueva variable local */
f(),
print(x), # 9
g(),
print(x) # 8
)
➜ eloquentjsegg git:(private2122) bin/egg.js examples/scope.egg
4
9
8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# l-values and r-values
Some languages use the idea of l-values and r-values, deriving from the typical mode of evaluation on the left and right-hand side of an assignment statement.
- An l-value refers to an object that persists beyond a single expression.
- An r-value is a temporary value that does not persist beyond the expression that uses it.
In many languages, notably the C family, l-values have storage addresses that are programmatically accessible to the running program (e.g., via some address-of operator like "&
" in C/C++), meaning that they are variables or de-referenced references to a certain memory location.
Form the perspective of interpretation and translation, the same sub-expresion x
has different meanings depending on the side of the assignment in which it is:
x = x
On the left side x
is a reference or a binding, on the right side is a value: the value stored on that reference.
# leftEvaluate
Our evaluate
methods do not work here, since they interpret the expressions in a r-value context.
The proposal is to introduce leftEvaluate
methods in the AST node classes that evaluate the expressions in a l-value context. Something like the following code for specialForms['=']
:
specialForms['='] = specialForms['set'] = function(args, env) {
if (args.length !== 2 || args[0].type === 'value') {
throw new SyntaxError('Bad use of set');
}
let valueTree = args[args.length-1];
let value = valueTree.evaluate(env);
let leftSide = args[0];
let [scope, name] = leftSide.leftEvaluate(env);
scope[name] = value;
return value;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Exercise: leftEvaluate for WORD Nodes
Try to write the leftEvaluate
method(s) for Egg. Allow only words on the left side of any assignment
# Exercise: Accesing elements on arrays and objects
More difficult!
If you only provide a leftEvaluate
for WORD
nodes, then you can't modify en element of an array or a property of an object or an entry of a dictionary/map. 😟 👎
But may be adding an appropriate leftEvaluate
to APPLY
nodes you can alleviate the problem (🧐 not the final solution, we will solve this problem in incoming labs, but the idea behind this exercise helps).
The goal is to achieve that a program like this may work:
➜ eloquentjsegg git:(private2122) ✗ cat examples/leftvalue-pair.egg
do(
def (x, array(array(1,2),array(3,4))),
=(array(x, 0),9),
print(x) # [9,[3,4]]
)
2
3
4
5
6
and also this:
➜ eloquentjsegg git:(private2122) ✗ cat examples/leftvalue-object.egg
do {
def (x, object(a: 1, b:3)),
=(array(x, "a"),9),
print(x) # { "a":9, "b":3}
}
2
3
4
5
6
➜ eloquentjsegg git:(private2122) ✗ bin/egg.js examples/leftvalue-object.egg
{"a":9,"b":3}
2
# Challenge: Multiple indices
More difficult!
If you solved the previous exercise you can consider supporting multiple indices.
How to make something like this work?
➜ eloquentjsegg git:(private2122) ✗ cat examples/leftvalue-array-multiple-indices-2.egg
do (
def (x, array(array(1,2),array(3,4))),
=(array(x, 0, 0),9),
print(x) # [[9,2], [3,4]]
)
2
3
4
5
6
➜ eloquentjsegg git:(private2122) ✗ bin/egg.js examples/leftvalue-array-multiple-indices-2.egg
[[9,2],[3,4]]
2
# Set and Negative indices
If you decide to give support to negative indices in your arrays, you not only must take care of the element
function
but also inside the set
for programs like this one:
➜ eloquentjsegg git:(private2122) ✗ cat examples/leftvalue-array-negative-indices.egg
do (
def (x, array(array(1,2),array(3,4))),
=(array(x, -1, -1),9),
print(x) # [[1,2],[3,9]]
)
➜ eloquentjsegg git:(private2122) ✗ bin/egg.js examples/leftvalue-array-negative-indices.egg
[[1,2],[3,9]]
2
3
4
5
6
7
8
Here is another example:
➜ eloquentjsegg git:(private2122) ✗ cat examples/leftvalue-object-multiple.egg
do {
def (x, object(a: array(1, 2), b:3)),
=(array(x, "a", -1), 9),
print(x) # {"a":[1,9],"b":3}
}
➜ eloquentjsegg git:(private2122) ✗ bin/egg.js examples/leftvalue-object-multiple.egg
{"a":[1,9],"b":3}
2
3
4
5
6
7
8
9
# Future Work on Assignments
Future Work
Although by now we will restrict to allow only words on the left side of any assignment, we aim to increase expressiveness and to allow assignments that can contains expressions like the =(y["y"][1], 9)
or =(self.c, a)
in the following example (examples/set-lefteval.egg
):
do (
def (x, [[1,2], [3,4]]),
=(x[0], 9), # [9, [3,4]]
print(x), # [ 9, [ 3, 4 ] ]
def(y, map(x:4, y: array(0,7))),
=(y["y"][1], 9), # map(x:4, y: [0,9])
print(y["y", 1]), # 9
print(y), # { x: 4, y: [ 0, 9 ] }
def(z, { c:4, g: fun(a, =(self.c, a))}),
print(z.c), # 4
print(z.g(8)), # 8
print(z.c) # 8
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
That we will compile and execute like here:
➜ egg-oop-parser-solution git:(master) ✗ bin/eggc.js examples/set-lefteval.egg
➜ egg-oop-parser-solution git:(master) ✗ bin/evm examples/set-lefteval.json
[9,[3,4]]
[0,9]
{"x":4,"y":[0,9]}
4
8
8
2
3
4
5
6
7
8
# See also
- See also section Fixing Scope (opens new window) in the EloquentJS book
- Puede encontrar una solución al problema en la rama
inicial
de este repo ULL-ESIT-PL-1617/egg/ (opens new window). La ramainicial
como su nombre indica contiene además del código descrito en el capítulo de EloquentJS las soluciones a los ejercicios propuestos en el capítulo del libro.