Program A()
{
x, y, z: integer;
procedure B()
{
y: integer;
y=0;
x=z+1;
z=y+2;
}
procedure C()
{
z:
The crux is that the lexical graph looks like this:
B <- A -> C -> D
whereas the call graph looks like this:
A -> C -> D -> B
The only difference is what lineage B has. In the lexical picture, B is defined directly in the scope of A (the global scope). In the dynamic picture, the stack at B already has D on top of C and then A.
This difference amounts to how the keywords x
and z
are resolved in B. Lexically, they are identified with A.x
and A.z
, but dynamically, they are identified with D.x
and (since no D.z
exists) with C.z
.
def B:
B.y = 0
x = z + 1
z = y + 2
def C:
def D:
D.x = z + 1
y = D.x + 1
call B
C.z = 5
call D
A.x, A.y, A.z = 10, 11, 12
call C
print A.x, A.y, A.z
Above I've tried to represent your code more clearly. Note that D mutates A.y
according to both methods of name resolution, whereas B only mutates A.x
and A.z
if lexical rather than dynamic scoping is chosen.
Note that while a function is only ever defined once*, it is common to call it from multiple places (and it may even call itself recursively). Thus, while it is fairly trivial to perform lexical scoping using the static code, the dynamic scoping is more complicated because the same keyword (in the same function) may resolve to different variables (from different name spaces) during different calls to that function (requiring you to step through the program and track how the call stack changes during execution).
*(Except in templating languages..)