Block structured languages
6.5 Block structured languages
This section presents a more complex example of the use of tuples in combination with compositionality. The example deals with the scope of variables in a block structured language. A variable from a global scope is visible in a local scope only if it is not hidden by a variable with the same name in the local scope.
6.5.1 Blocks
A block is a list of statements. A statement is a variable declaration, a variable usage or a nested block. The concrete representation of an example block of our block structured language looks as follows (dcl stands for declaration, use stands for usage and x, y and z are variables).
use x ; dcl x ; (use z ; use y ; dcl x ; dcl z ; use x) ; dcl y ; use y
Statements are separated by semicolons. Nested blocks are surrounded by paren- theses. The usage of z refers to the local declaration (the only declaration of z). The usage of y refers to the global declaration (the only declaration of y). The local usage of x refers to the local declaration and the global usage of x refers to the global declaration. Note that it is allowed to use variables before they are declared. Here are some mutually recursive (data)types, which describe the abstract syntax of blocks, corresponding to the grammar that describes the concrete syntax of blocks which is used above. We use meaningful names for data constructors and we use built-in lists instead of user-defined lists for the blockalgebra. As usual, the algebra type BlockAlgebra, which consists of two tuples of functions, and the fold function foldBlock, which uses two mutually recursive local functions, can be generated from the two mutually recursive (data)types.
type Block
= [Statement]
data Statement = Dcl Idf | Use Idf | Blk Block type Idf = String
type BlockAlgebra b s = ((s -> b -> b,b)
,(Idf -> s,Idf -> s,b -> s) )
Compositionality
foldBlock :: BlockAlgebra b s -> Block -> b foldBlock ((cons,empty),(dcl,use,blk)) = fold where
fold (s:b)
= cons (foldS s) (fold b)
fold []
= empty
foldS (Dcl x) = dcl x foldS (Use x) = use x foldS (Blk b) = blk (fold b)
6.5.2 Generating code
The goal of this section is to generate code from a block. The code consists of a sequence of instructions. There are three types of instructions.
• Enter (l,c): enters the l’th nested block in which c local variables are de-
clared. • Leave (l,c): leaves the l’th nested block in which c local variables were
declared. • Access (l,c): accesses the c’th variable of the l’th nested block.
The code generated for the above example looks as follows.
[Enter (0,2),Access (0,0) ,Enter (1,2),Access (1,1),Access (0,1),Access (1,0),Leave (1,2) ,Access (0,1),Leave (0,2) ]
Note that we start numbering levels (l) and counts (c) (which are sometimes called displacements) from 0. The abstract syntax of the code to be generated is described by the following datatype.
type Count = Int type Level = Int
type Variable
= (Level,Count)
type BlockInfo = (Level,Count) data Instruction = Enter BlockInfo
| Leave BlockInfo | Access Variable
type Code = [Instruction] The function ab2ac, which generates abstract code (a value of type Code) from an
abstract block (a value of type Block), uses a compositional function block2Code. For all syntactic constructs of Statement and Block we define appropriate semantic actions on an algebra of computations. Here is a, somewhat simplified, description of these semantic actions.
6.5 Block structured languages
• Dcl: Every time we declare a local variable x we have to update the local environment le of the block we are in by associating with x the current level– local-count pair (l,lc). Moreover we have to increment the local variable count lc to lc+1. Note that we do not generate any code for a declaration statement. Instead we perform some computations which make it possible to generate appropriate code for other statements.
dcl x (le,l,lc) = (le’,lc’) where
le’ = le ‘update‘ (x,(l,lc)) lc’ = lc+1
where function update is defined in the AssociationList module. • Use: Every time we use a local variable x we have to generate code cd’ for it.
This code is of the form [Access (l,lc)]. The level–local-count pair (l,lc) of the variable is looked up in the global environment e.
use x e = cd’ where
cd’ = [Access (l,c)] (l,c) = e ? x
• Blk: Every time we enter a nested block we increment the global level l to l+1, start with a fresh local variable count 0 and set the local environment of the nested block we enter to the current global environment e. The computation for the nested block results in a local variable count lcB and a local envi- ronment leB. Furthermore we need to make sure that the global environment (the one in which we look up variables) which is used by the computation for the nested block is equal to leB. The code which is generated for the block is surrounded by an appropriate [Enter lcB]-[Leave lcB] pair.
blk fB (e,l) = cd’ where
l’ = l+1 (leB,lcB,cdB) = fB (leB,l’,e,0) cd’ = [Enter (l’,lcB)]++cdB++[Leave (l’,lcB)]
• []: No action need to be performed for an empty block. • (:): For every nonempty block we perform the computation of the first state-
ment of the block which, given a local environment le and local variable count lc, results in a local environment leS and local variable count lcS. This environment-count pair is then used by the computation of the rest of the block to result in a local environment le’ and local variable count lc’. The code cd’ which is generated is the concatenation cdS++cdB of the code cdS which is generated for the first statement and the code cdB which is generated for the rest of the block.
cons fS fB (le,lc) = (le’,lc’,cd’) where
(leS,lcS,cdS) = fS (le,lc) (le’,lc’,cdB) = fB (leS,lcS) cd’ = cdS++cdB
Compositionality
What does our actual computation type look like? For dcl we need three inherited attributes: a global level, a local block environment and a local variable count. Two of them: the local block environment and the local variable count are also synthesised attributes. For use we need one inherited attribute: a global block environment, and we compute one synthesised attribute: the generated code. For blk we need two inherited attributes: a global block environment and a global level, and we compute two synthesised attributes: the local variable count and the generated code. Moreover there is one extra attribute: a local block environment which is both inherited and synthesised. When processing the statements of a nested block we already make use of the global block environment which we are synthesising (when looking up variables). For cons we compute three synthesised attributes: the local block environment, the local variable count and the generated code. Two of them, the local block environment and the local variable count are also needed as inherited attributes. It is clear from the considerations above that the following types fulfill our needs.
type BlockEnv
= [(Idf,Variable)]
type GlobalEnv = (BlockEnv,Level) type LocalEnv
= (BlockEnv,Count)
The implementation of block2Code is now a straightforward translation of the ac- tions described above. Attributes which are not mentioned in those actions are added as extra components which do not contribute to the functionality.
block2Code :: Block -> GlobalEnv -> LocalEnv -> (LocalEnv,Code) block2Code = foldBlock ((cons,empty),(dcl,use,blk)) where
cons fS fB (e,l) (le,lc) = ((le’,lc’),cd’) where
((leS,lcS),cdS) = fS (e,l) (le,lc) ((le’,lc’),cdB) = fB (e,l) (leS,lcS) cd’ = cdS++cdB
empty (e,l) (le,lc) = ((le,lc),[]) dcl x (e,l) (le,lc) = ((le’,lc’),[]) where
le’ = (x,(l,lc)):le lc’ = lc+1
use x (e,l) (le,lc) = ((le,lc),cd’) where
cd’ = [Access (l,c)] (l,c) = e ? x
blk fB (e,l) (le,lc) = ((le,lc),cd’) where
((leB,lcB),cdB) = fB (leB,l’) (e,0) l’ = l+1 cd’ = [Enter (l’,lcB)] ++ cdB ++ [Leave (l’,lcB)]
The code generator starts with an empty local environment, a fresh level and a fresh local variable count. The code is a synthesised attribute. The global environment is an attribute which is both inherited and synthesised. When processing a block we already use the global environment which we are synthesising (when looking up variables).
ab2ac :: Block -> Code ab2ac b = [Enter (0,c)] ++ cd ++ [Leave (0,c)] where
6.6 Exercises
((e,c),cd) = block2Code b (e,0) ([],0) aBlock
= [Use "x",Dcl "x"
,Blk [Use "z",Use "y",Dcl "x",Dcl "z",Use "x"] ,Dcl "y",Use "y"]
? ab2ac aBlock [Enter (0,2),Access (0,0) ,Enter (1,2),Access (1,1),Access (0,1),Access (1,0),Leave (1,2) ,Access (0,1),Leave (0,2)]