attributes and attribute grammars

62

Upload: nadeena1987

Post on 06-Apr-2018

243 views

Category:

Documents


0 download

TRANSCRIPT

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 1/62

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 2/62

Attributes and Attribute Grammars

Attributeso variables (type,offset,first or last occurrence,...)o constants (type, value, scope, ...)

Attribute grammar(Syntax-directed definition)o A generalization of CFG in which each grammar symbol has an associated set of

attributeso Semantics rules for computing attribute values

Synthesized Attributes

Synthesized Attributeso The value is computed from the values of attributes of the childreno S-attributed grammar - synthesized attributes onlyo Bottom-up propagation

Exampleo values of expressionso types of expressions

Attributes are additional values associated with something of central interest. In the case of ASTs, you can think of them as pairs (attribute_name, attribute_value) assosicated with eachAST node, where the attribute name corresponds to some interesting fact-type, and theattribute value corresponds to the actual state of that fact (e.g.,"(constants_in_subtree_count,12)").

Inherited and synthesized is vocabulary for describing how these attribute are computed for each AST node, usually associaed with teh grammar rule that produces the AST node usingits children.

Synthesized attributes are those that have been computed for children nodes, and are beingpassed up the tree. Often, values of synthesized attributes are combined to produce anattribute for the parent node. If an AST node has two children, each of which have thier ownattributes (constants_in_subtree_count,5) and (constants_in_subtree_count,7), then bypassing those attributes up, the parent can compute his corresponding attribute(constants_in_subtree,12).

Syntax-directed translation refers to a method of compiler implementation where thesource language translation is completely driven by the parser. In other words, the

parsing process and parse trees are used to direct semantic analysis and the translationof the source program. This can be a separate phase of a compiler or we can augmentour conventional grammar with information to control the semantic analysis andtranslation. Such grammars are called attribute grammars.

We augment a grammar by associating attributes with each grammar symbol that

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 3/62

describes its properties. An attribute has a name and an associated value: a string, anumber, a type, a memory location, an assigned register—whatever information weneed. For example, variables may have an attribute "type" (which records the declaredtype of a variable, useful later in type-checking) or an integer constant may have anattribute "value" (which we will later need to generate code).

With each production in a grammar, we give semantic rules or actions, which describehow to compute the attribute values associated with each grammar symbol in a

production. The attribute value for a parse node may depend on information from itschildren nodes below or its siblings and parent node above.Consider this production, augmented with a set of actions that use the "value" attributefor a digit node to store the appropriate numeric value. Below, we use the syntax X.a torefer to the attribute a associated with symbol X.digit –> 0 {digit.value = 0}

| 1 {digit.value = 1}| 2 {digit.value = 2}

...| 9 {digit.value = 9}Attributes may be passed up a parse tree to be used by other productions:int1 –> digit {int1.value = digit.value}

| int2 digit {int1.value = int2.value*10 + digit.value}(We are using subscripts in this example to clarify which attribute we are referring to,so int1 and int2 are different instances of the same non-terminal symbol.)There are two types of attributes we might encounter: synthesized or inherited.

Synthesized attributes are those attributes that are passed up a parse tree, i.e., the left- 2side attribute is computed from the right-side attributes. The lexical analyzer usuallysupplies the attributes of terminals and the synthesized ones are built up for thenonterminals and passed up the tree.X –> Y1Y2...YnX.a = f(Y1.a, Y2.a, ...Yn.a)Inherited attributes are those that are passed down a parse tree, i.e., the right-sideattributes are derived from the left-side attributes (or other right-side attributes). Theseattributes are used for passing information about the context to nodes further down thetree.X –> Y1Y2...YnYk.a = f(X.a, Y1.a, Y2.a, ..., Yk-1.a, Yk+1.a, ..., Yn.a)

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 4/62

Inherited Attributes

An inherited attribute is one whose value at a node in a parse tree is defined interms of attributes at the parent and/or siblings of that node. Inherited attributesare convenient for expressing the dependence of a programming languageconstruct on the context in which it appears. For example, we can use aninherited attribute to keep track of whether an identifier appears on the left or the right side of an assignment in order to decide whether the address or thevalue of the identifier is needed. Although it is always possible to rewrite asyntax directed definitions with inherited attributes. EXAMPLE:A declaration generated by the nonterminal D in the syntaxdirected definition in figure below, consists of the keyword int or real, followed

by a list of identifiers. The nonterminal T has the synthesized attribute type,

whose value is determined by the keyword in the declaration. The semantic ruleL. in :=T. type , associated with production D->TL, sets inherited attribute L.in tothe type in the declaration. The rules then pass this type down the parse treeusing the inherited attribute L. in. Rules associated with the productions for Lcall procedure addtype to add the type of each identifier to its entry in thesymbol table(pointed to by attribute entry ).

Syntax Directed definition with inherited attribute L. in

The figure shows an annotated parse tree for the sentence real id1, id2, id3. Thevalue of L. in at the three L nodes gives the type of identifiers id1, id2, andid3. These values are determined by computing the value of the attributeT. type at the left child of the root and then evaluate L. in top-down at thethree L nodes in the right subtree of the root. At each L node we also callthe procedure addtype to insert into the symbol table the fact that theidentifier at the right child of this node has type real.

Parse tree with inherited attributed in at each node labeled L

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 5/62

Imperative programmingFrom Wikipedia, the free encyclopedia

This article needs additional citations for verification . Please help improve this article byadding citations to reliable sources . Unsourced material may be challenged and removed . (October 2011)

Programming paradigms

Agent-oriented

Automata-based

Component-based

Flow-based

Pipelined

Concatenative

Concurrent computing

Relativistic programming

Data-driven

Declarative (contrast: Imperative )

Constraint

Dataflow

Cell-oriented (spreadsheets )

Reactive

Logic

Abductive logic

Answer set

Constraint logic

Functional logic

Inductive logic

Event-driven

Service-oriented

Time-driven

Expression-oriented

Feature-oriented

Function-level (contrast: Value-level )

Functional

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 6/62

Generic

Imperative (contrast: Declarative )

Procedural

Language-oriented

Discipline-specific

Domain-specific

Grammar-oriented

Dialecting

Intentional

Metaprogramming

Automatic

Reflective

Attribute-oriented

Template

Policy-based

Non-structured (contrast: Structured )

Array

Nondeterministic

Parallel computing

Process-oriented

Programming in the large / small

Semantic

Structured (contrast: Non-structured )

Modular (contrast: Monolithic)

Object-oriented

By separation of concerns :

Aspect-oriented

Role-oriented

Subject-oriented

Class-based

Prototype-based

Recursive

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 7/62

Value-level (contrast: Function-level )

v

d

e

In computer science , imperative programming is a programming paradigm that describes

computation in terms of statements that change a program state . In much the same way

that imperative mood in natural languages expresses commands to take action, imperative programs

define sequences of commands for the computer to perform.

The term is used in opposition to declarative programming , which expresses what the program shouldaccomplish without prescribing how to do it in terms of sequences of actions to be

taken. Functional and logical programming are examples of a more declarative approach.

Contents

[hide ]

• 1 Imperative, procedural, and declarative programming

• 2 Overview

• 3 Block structure

• 4 History

• 5 Notes

• 6 References

• 7 See also

[edit ]Imperative, procedural, and declarative programming

Procedural programming is imperative programming in which the program is built from one or more

procedures (also known as subroutines or functions). The terms are often used as synonyms, but the

use of procedures has a dramatic effect on how imperative programs appear and how they are

constructed. Heavily procedural programming, in which state changes are localized to procedures or

restricted to explicit arguments and returns from procedures, is known as structured programming .

From the 1960s onwards, structured programming and modular programming in general, have been

promoted as techniques to improve the maintainability and overall quality of imperative

programs. Object-oriented programming extends this approach. [citation needed ]

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 8/62

Procedural programming could be considered as a step towards declarative programming. A

programmer can often tell, simply by looking at the names, arguments and return types of procedures

(and related comments), what a particular procedure is supposed to do - without necessarily looking at

the detail of how the procedure achieves its result. At the same time, a complete program is still

imperative since it 'fixes' the statements to be executed and their order of execution to a large extent.

Declarative programming is a non-imperative style of programming in which programs describe the

desired results of the program, without explicitly listing command or steps that need to be carried out

to achieve the results. Functional and logical programming languages are characterized by a

declarative programming style.

In a pure functional language , such as Haskell , all functions are without side effects , and state

changes are only represented as functions that transform the state. Although pure functional

languages are non-imperative, they often provide a facility for describing the effect of a function as a

series of steps. Other functional languages, such as Lisp, OCaml and Erlang , support a mixture of

procedural and functional programming.

In logical programming languages , programs consist of logical statements, and the program executes

by searching for proofs of the statements. As in functional programming languages, some logical

programming languages such as Prolog , and database query languages such as SQL, while

declarative in principle, also support a procedural style of programming.

Many imperative programming languages (such as Fortran , BASIC and C)

are abstractions of assembly language .

[edit ]Overview

The hardware implementation of almost all computers is imperative; [note 1] nearly all computer hardware

is designed to execute machine code , which is native to the computer, written in the imperative style.

From this low-level perspective, the program state is defined by the contents of memory, and the

statements are instructions in the native machine language of the computer. Higher-level imperative

languages use variables and more complex statements, but still follow the same

paradigm. Recipes and process checklists , while not computer programs , are also familiar concepts

that are similar in style to imperative programming; each step is an instruction, and the physical worldholds the state. Since the basic ideas of imperative programming are both conceptually familiar and

directly embodied in the hardware, most computer languages are in the imperative style.

Assignment statements , in imperative paradigm, perform an operation on information located in

memory and store the results in memory for later use. High-level imperative languages, in addition,

permit the evaluation of complex expressions , which may consist of a combination o f arithmetic

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 9/62

operations and function evaluations, and the assignment of the resulting value to memory. Looping

statements (such as in while loops , do while loops and for loops ) allow a sequence of statements to be

executed multiple times. Loops can either execute the statements they contain a predefined number of

times, or they can execute them repeatedly until some condition

changes. Conditional branching statements allow a sequence of statements to be executed only if

some condition is met. Otherwise, the statements are skipped and the execution sequence continues

from the statement following them. Unconditional branching statements allow the execution sequence

to be transferred to some other part of the program. These include the jump (called " goto " in many

languages), switch and the subprogram, or procedure , call (which usually returns to the next statement

after the call)..

[edit ]Block structure

Main article: block (programming)

Early in the development of high level languages , the introduction of the block enabled the

construction of programs in which a group of statements and declarations could be treated as if they

were a single statement. This, alongside the introduction of subroutines , enabled complex structures to

be expressed by hierarchical decomposition into simpler procedural structures.

[edit ]History

The earliest imperative languages were the machine languages of the original computers. In these

languages, instructions were very simple, which made hardware implementation easier, but hindered

the creation of complex programs. FORTRAN , developed by John Backus at IBMstarting in 1954, was

the first major programming language to remove the obstacles presented by machine code in the

creation of complex programs. FORTRAN was a compiled language that allowed named variables,

complex expressions, subprograms, and many other features now common in imperative languages.

The next two decades saw the development of a number of other major high-level imperative

programming languages. In the late 1950s and 1960s, ALGOL was developed in order to allow

mathematical algorithms to be more easily expressed, and even served as the operating system 's

target language for some computers. MUMPS (1966) carried the Imperative paradigm to a logical

extreme, by not having any statements at all, relying purely on commands, even to the extent of having the IF and ELSE commands be independent of each other, and only connected by a intrinsic

variable named $TEST. COBOL (1960) and BASIC (1964) were both attempts to make programming

syntax look more like English. In the 1970s, Pascal was developed by Niklaus Wirth , and C was

created by Dennis Ritchie while he was working at Bell Laboratories . Wirth went on to design Modula-

2, and Oberon . For the needs of the United States Department of Defense , Jean Ichbiah and a team

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 10/62

at Honeywell began designing Ada in 1978, after a 4-year project to define the requirements for the

language. The specification was first published in 1983, with revisions in 1995 and 2005/6.

The 1980s saw a rapid growth in interest in object-oriented programming . These languages were

imperative in style, but added features to support objects . The last two decades of the 20th century

saw the development of a considerable number of such programming languages. Smalltalk-80 ,

originally conceived by Alan Kay in 1969, was released in 1980 by the Xerox Palo Alto Research

Center . Drawing from concepts in another object-oriented language— Simula (which is considered to

be the world's first object-oriented programming language , developed in the late 1960s)— Bjarne

Stroustrup designed C++, an object-oriented language based on C. C++ was first implemented in

1985. In the late 1980s and 1990s, the notable imperative languages drawing on object-oriented

concepts were Perl , released by Larry Wall in 1987; Python , released by Guido van Rossum in

1990; PHP , released by Rasmus Lerdorf in 1994; Java , first released by Sun Microsystems in 1994

and Ruby , released in 1995 by Yukihiro “matz” Matsumoto. Microsoft's .NET platform (2002) is at itscore imperative as are its primary target languages, VB.NET and C#.

Category:Procedural programming languages lists additional imperative programming languages.

Functional Programming vs.Imperative Programming

Visual Studio 2010Other Versions

This topic compares and contrasts functional programming with more traditionalimperative (procedural) programming.

Functional Programming vs. Imperative

ProgrammingThe functional programming paradigm was explicitly created to support a purefunctional approach to problem solving. Functional programming is a formof declarative programming . In contrast, most mainstream languages, includingobject-oriented programming (OOP) languages such as C#, Visual Basic, C++, andJava –, were designed to primarily support imperative (procedural) programming.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 11/62

With an imperative approach, a developer writes code that describes in exactingdetail the steps that the computer must take to accomplish the goal. This issometimes referred to as algorithmic programming. In contrast, a functionalapproach involves composing the problem as a set of functions to be executed. Youdefine carefully the input to each function, and what each function returns. Thefollowing table describes some of the general differences between these two

approaches.

Characteristic Imperative approach Functional approach

Programmer focus How to perform tasks (algorithms) andhow to track changes in state.

What information is desired and whattransformations are required.

State changes Important. Non-existent.

Order of execution Important. Low importance.

Primary flow control Loops, conditionals, and function(method) calls.

Function calls, including recursion.

Primary manipulationunit

Instances of structures or classes. Functions as first-class objects and datacollections.

Although most languages were designed to support a specific programmingparadigm, many general languages are flexible enough to support multipleparadigms. For example, most languages that contain function pointers can be usedto credibly support functional programming. Furthermore, in C# 3.0 and Visual Basic9.0, explicit language extensions have been added to support functional

programming, including lambda expressions and type inference. LINQ technology is aform of declarative, functional programming.

The Imperative Programming Paradigm Imperative programming is characterized by programming with a state and

commands which modify the state. Imperative:

a command or order Procedure:

a. the act, method or manner of proceeding in some process or course of action

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 12/62

b. a particular course of action or way of doing something.

When imperative programming is combined with subprograms it iscalled procedural programming. In either case the implication is clear.

Programs are directions or orders for performing an action.Keywords and phrases: Assignment, goto, structured programming, command, statement, procedure, control-flow, imperative language, assertions, axiomatic semantics.state, variables, instructions, control structures.

The imperative programming paradigm is an abstraction of real computerswhich in turn are based on the Turing machine and the Von Neumann machinewith its registers and store (memory). At the heart of these machines is theconcept of a modifiable store. Variables and assignments are the programminglanguage analog of the modifiable store. The store is the object that ismanipulated by the program. Imperative programming languages provide avariety of commands to provide structure to code and to manipulate the store.Each imperative programming language defines a particular view of hardware.These views are so distinct that it is common to speak of a Pascal machine, Cmachine or a Java machine. A compiler implements the virtual machine defined

by the programming language in the language supported by the actual hardwareand operating system.

In imperative programming, a name may be assigned to a value and later reassigned to another value. The collection of names and the associated valuesand the location of control in the program constitute the state . The state is alogical model of storage which is an association between memory locations andvalues. A program in execution generates a sequence of states(See Figure N.1).The transition from one state to the next is determined by assignmentoperations and sequencing commands.

Figure N.1: State Sequence

S0 -- O 0 -> S 1 - ... -> S n-1 -- O n-1-> S n

Unless carefully written, an imperative program can only be understood interms of its execution behavior. The reason is that during the execution of thecode, any variable may be referenced, control may be transferred to anyarbitrary point, and any variable binding may be changed. Thus, the whole

program may need to be examined in order to understand even a small portionof code.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 13/62

Since the syntax of C, C++ and Java are similar, in what follows, commentsmade about C apply also to C++ and Java.

Variables and Assignment

Imperative programs are characterized by sequences of bindings (state changes)in which a name may be bound to a value at one point in the program and later

bound to a different value. Since the order of the bindings affects the value of expressions, an important issue is the proper sequencing of bindings.

Terminology. When speaking of hardware we use terms like bit pattern , storage cell, and storage address. Somewhat analogous terms in programming languages are value, variable, and name . Since variablesare usually bound to a name and to a value, the word variable is oftenused to mean the name of a value.

Aside. Most descriptions of imperative programming languages are tied tohardware and implementation considerations where a name is bound to anaddress, a variable to a storage cell, and a value to a bit pattern. Thus, a name istied to two bindings, a binding to a location and to a value. The location iscalled the l-value and the value is called the r-value . The necessity for thisdistinction follows from the implementation of the assignment. For example,

X := X+2

the X on the left of the assignment denotes a location while the X on the r ighthand side denotes the value. Assignment changes the value at a location.

A variable may be bound to a hardware location at various times. It may be bound at compile time (rarely), at load time (for languages with staticallocation) or at run time (for languages with dynamic allocation). From theimplementation point of view, variable declarations are used to determine theamount of storage required by the program.

The following examples illustrate the general form for variable declarations inimperative programming languages.

Pascal style declaration:var name : Type;C style declaration: Type name;

A variable and a value are bound by an assignment. A variety of notations isused to indicate the binding of a variable V and the value of an expression E .

Pascal V := EC V = EAPL V <-- EScheme (setq V E)

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 14/62

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 15/62

From the point of view of axiomatic semantics, the assignment is a predicatetransformer. It is a function from predicates to predicates. From the point of view of denotational semantics, the assignment is a function from states tostates. From the point of view of operational semantics, the assignment changesthe state of an abstract machine.

Unstructured Commands

Given the importance of sequence control, it is not surprising that considerableeffort has been given to finding appropriate control structures. Figure N.Mgives a minimal set of basic control structures.

Figure N.M: A set of unstructured commandscommand ::= identifier := expression

| command ; command | label : command | GOTO label | IF boo_exp THEN GOTO label

The unstructured commands contain the assignment command, sequentialcomposition of commands, a provision to identify a command with a label, andunconditional and conditional GOTO commands. The unstructured commandshave the advantage, they have direct hardware support and are completely

general purpose. However, the programs are flat without hierarchical structurewith the result that the code may be difficult to read and understand. The set of unstructured commands contains one of the most powerful commands, theGOTO. It is also the most criticized. The GOTO can make it difficult tounderstand a program by producing `spaghetti' like code. So named because thecontrol seems to wander around in the code like strands of spaghetti.

The GOTO commands are explicit transfer of control from one point in a program to another program point. These jump commads come inunconditional and conditional forms:

goto label if conditional expression goto label

At the machine level alternation and iteration may be implementedusing labels and goto commands. Goto commands often take two forms:

1. Unconditional goto . The unconditional goto command has the form:

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 16/62

goto LABEL i

The sequence of instructions next executed begin with the commandlabeled with LABELi .

2. Conditional goto . The conditional goto command has the form:

if conditional expression then goto LABEL i

If the conditional expression is true then execution transfers to thesequence of commands headed by the command labeledwith LABELi otherwise it continues with the command following theconditional goto.

Structured Programming

The term structured programming was coined to describe a style of programming that emphasizes hierarchical program structures in which eachcommand has one entry point and one exit point. The goal of structured

programming is to provide control structures that make it easier to reason aboutimperative programs. Figure M.N gives a minimal set of structured commands.

Figure N.M: A set of structured commandscommand ::= SKIP

| identifier := expression | IF guarded_command [ [] guarded_command ]+ FI | DO guarded_command [ [] guarded_command ]+ OD| command ; command

guarded_command ::= guard --> command guard ::= boolean expression

The IF and DO commands which are defined in terms of guarded commandsrequire some explanation. The IF command allows for a choice between

alternatives while the DO command provides for iteration. In their simplestforms, an IF statement corresponds to an If condition then command and a DOstatement corresponds to a While condition Do command.

IF guard --> command FI= if guard then command DO guard --> command OD= while guard do command

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 17/62

A command proceded by a guard can only be executed if the guard is true. Inthe general case, the semantics of the IF - FI and DO - OD commands requiresthat only one command corresponding to a guard that is true be selected for execution. The selection is nondeterministic..

Control structures are syntactic structures that define the order in whichassignments are performed. Imperative programming languages provide a richassortment of sequence control mechanisms. Three control structures are foundin traditional imperative langauges: sequential composition , alternation ,and iteration .

Aside. Imperative programming languages often call assignments andcontrol structures commands , statements or instructions . In ordinaryEnglish, a statement is an expression of some fact or idea and thus is aninappropriate designation. Commands and instructions refer to an actionto be performed by a computer. Lacking a more neutral term we will usecommand to refer to assignment, skip, and control structures.

Sequential Composition. Sequential composition specifies a linear ordering for the execution of commands. It is usually indicated by placing commands intextual sequence and either line separation or a special symbol (such as thesemicolon) is used to indicate termination point of a command. In C thesemicolon is used as a terminator, in Pascal it is a command separator. At amore abstract level, composition of commands is indicated by using acomposition operator such as the semicolon (C 0;C 1).

Selection: Selection permits the specification of a sequence of commands bycases. The selection of a particular sequence is based on the value of anexpression. The if and case commands are the most common representatives of alternation.

Iteration: Iteration specifies that a sequence of commands may be executedzero or more times. At run time the sequence is repeatedly composed withitself. There is an expression whose value at run time determines the number of compositions. The while , repeat and for commands are the most commonrepresentatives of iteration.

Abstraction: A sequence of commands may be named and the name used toinvoke the sequence of commands. Subprograms, procedures, and functionsare the most common representatives of abstration.

SkipsThe simplest kind of command is the skip command. It has no effect.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 18/62

CompositionThe most common sequence is the sequential composition of two or (more)commands (often written `S 0;S1'). Sequential composition is available in everyimperative programming language.

AlternationAn alternative command may contain a number of alternative sequences of commands, from which exactly one is chosen to be executed. Thenondeterministic IF-FI command is unusual. Traditional programminglanguages usually have one or more if commands and a case command.

-- Adaif condition then

commands{ elsif condition then commands }[ else commands ]endifcase expression iswhen choice | choice =>commandswhen choice | choice =>commands[when others => commands ]end case;

Iteration

An iterative command has a body which is to be executed repeatedly and hasan expression which determines when the execution will cease. The threecommon forms are the while-do, the repeat-until, and the for-do.

while-do while condition do body

repeat-until repeatbody

until conditionfor-do for index := lowerBound, upperBound, step do

bodyThe while-do command semantics require the testing of the condition beforethe body is executed. The semantics of the repeat-until command require thetesting of the condition after the body is executed. The for-do commandsemantics require testing of the condition before the body is executed.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 19/62

The iterative commands are often used to traverse the elements of a datastructure - search for a item etc. This insight leads to the concept of generatorsand iterators.

Definition: A generator is an expression which generates a sequence of values contained in a data structure.

The generator concept appears in functional programming languagesas functionals .Definition: An iterator is a generalized looping structure whose iterations aredetermined by a generator.An iterator is used with the an extended form of the for loop where the iterator replaces the initial and final values of the loop index. For example, given a

binary search tree and a generator which performs inorder tree traversal, aniterator would iterate for each item in the tree following the inorder treetraversal.

FOR Item in Tree DO S;

Sequential Expressions

Imperative programming languages with their emphasis on the sequentialevaluation of commands often fail to provide a similar sequentiality to theevaluation of expressions. The following code illustrates a common

programming situation where there are two or more conditions which must

remain true for iteration to occur.i := 0;while (i < length) and (list[i] <> value) do

i := i+1The code implements a sequential search for a value in a table and terminateswhen either the entire table has been searched or the value is found. Assumingthat the subscript range for list is 0 to length it seems reasonable that thetermination of the loop should occur either when the index is out of bounds or when the value is found. That is, the arguments to the and should be evaluatedsequentually and if the first argument is false the remaining argument need not

be evaluated since the value of the expression cannot be true. Such anevaluation scheme is call short-circuit evaluation. In languages without short-circuit evaluation, if the value is not in the list, the program aborts with asubscript out of range error.

The Ada language provides the special operators and then and or else so thatthe programmer can specify short-circuit evaluation.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 20/62

Subprograms, procedures, and functions

A procedure is an abstraction of a sequence of commands. The procedurecall is a reference to the abstraction. The syntax of procedure definition and

invocation (call) is simple.Procedure definition: name ( parameter list ) { body }

Procedure invocation: name ( argument list )

The semantics of the procedure call is determined by the semantics of the procedure body. For many languages with non-recursive procedures, thesemantics may be viewed as simple textual substitution.

Terminology: Parameters are often called formal parameters andarugments are often called actual parameters.

Parameters and arguments have a simple syntax

Parameter list: t 0 name 1, ..., t n-1 name n-1

Argument list: expression 1, ..., expression n-1

An in parameter designates that the body of the procedure may not modify thevalue of the argument (often implemented as a copy of the argument).An out parameter designates that value of the argument is undefined on entry tothe procedure and when the procedure terminates, the argument is assigned to avalue (often copied to the argument). An in-out parameter designates that thevalue of the parameter may be defined on entry to the procedure and may bemodified when the procedure terminates.

Parameter ArgumentPascal in:

in-out:name : type var name : type

name expression

Ada inoutin-out

name : in typename : out typename : in out type

expressionnamename

C in:in-out:

type name type *name (internal reference to the in-out

parameter must be * name )

expression &name

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 21/62

Other Control Structures

Other control structures are possible. In simulations, it is often easier tostructure a program as a set cooperating tasks. When the task activities are

interdependent, they can be structured as a collection of c oroutines. Unlikesubroutines where control is passed back to the calling routine from thesubroutine when the subroutine is finished, control is passed back and forth

between the subroutines with control resuming where it left off (right after theresume commands in the following).

Coroutine C1...resume C2...

Coroutine C2...resume C3...

Coroutine C3...resume C1...

There is a single thread of control that moves from coroutine to coroutine. Themultiple calls to a coroutine do not necessarily require multiple activationrecords.

In addition to coroutines there are concurrent or parallel processes

[|| Process o, ... , Process n-1]with multiple threads of control which communicate either through sharedvariables or message passing. Concurrency and parallel programminglanguages are considered in a later chapter.

Reasoning about Imperative Programs

Imperative constructs jeopardize many of the fundamental techniques for reasoning about mathematical objects. For example, the assignment axiom of axiomatic semantics is valid only for languages without aliasing and sideeffects. Much of the work on the theory of programming languages is anattempt to explain the ``referentially opaque'' features of programminglanguages in terms of well-defined mathematical constructs. By providingdescriptions of programming language features in terms of standardmathematical concepts, programming language theory makes it possible tomanipulate programs and reason about them using precise and rigoroustechniques. Unfortunately, the resulting descriptions are complex and thenotational machinery is difficult to use in all but small examples. It is thiscomplexity that provides a strong motivativation to provide functional andlogic programming as alternatives to the imperative programming paradigm.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 22/62

SequencersThere are several common features of imperative programming languages thattend to make reasoning about the program difficult. The goto command\cite{Dijk68} breaks the sequential continuity of the program. When the use of the goto command is undisciplined, the breaks involve abrupt shifts of context.

In Ada, the exit sequencer terminates an enclosing loop. All enclosing loopsupto and including the named loop are exited and execution follows with thecommand following the named loop.

Ada uses the return sequencer to terminate the execution of the body of a procedure or function and in the case of a function, to return the result of thecomputation.

Exception handers are sequencers that take control when an exception is raised.

A sequencer is a construct that allows more general control flows to be programmed.

• Jumps• Exits• Exceptions -- propagation, raising, resumption, handler (implicit

invocation)• Coroutines

The machine language of a typical computer includes instructions which allowany instruction to be selected as the next instruction. A sequencer is a constructthat is provided to give high-level programming languages some of thisflexibility. We consider three sequencers, jumps, escapes, and exceptions. Themost powerful sequencer (the goto ) is also the most criticized. Sequencers canmake it difficult to understand a program by producing `spaghetti' like code. Sonamed because the control seems to wander around in the code like the strandsof spaghetti.

EscapeAn escape is a command which terminates the execution of a textuallyenclosing construct. An escape of the form:

return expr

is used in C to exit a function call and return the value computed by thefunction.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 23/62

An escape of the form:

exit(n)

is used to exit n enclosing constructs. The exit command can be used inconjunction with a general loop command to produce while and repeat as wellas more general looping constructs.

In C a break command sends control out of the enclosing loop to the commandfollowing the loop while the continue command transfers control to the

beginning of the enclosing loop.

ExceptionsThere are many ``exception'' conditions that can arise in program execution.Some exception conditions are normal for example, the end of an input filemarks the end of the input phase of a program. Other exception conditions aregenuine errors for example, division by zero. Exception handlers of variousforms can be found in PL/1, ML, CLU, Ada, Scheme and other languages.

There are two basic types of exceptions which arise during program execution.They are domain failure, and range failure.

Domain failureoccurs when the input parameters of an operation do not satisfy therequirements of the operation. For example, end of file on a readinstruction, division by zero.

Range failureoccurs when an operation is unable to produce a result for values whichare in the range. For example, division by numbers within an epsilon of zero.

Definition: An exception condition is a condition that prevents the completionof an operation. The recognition of the exception is called raising theexception.Once an exception is raised it must be handled. Handling exceptions isimportant for the construction of robust programs. A program is said to

be robust if it recovers from exceptional conditions.Definition: The action to resolve the exception is called handling theexception. The propagation of an exception is the passing of the exception tothe context where it can be handled.The simplest method of handling exceptions is to ignore it and continueexecution with the next instruction. This prevents programmer from learningabout the exception and may lead to erroneous results.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 24/62

The most common method of handling exceptions is to abort execution. This isnot exceptable for file I/O but may be acceptable for an array index being outof bounds or for division by zero.

The next level of error handling is to return a value outside the range of theoperation. This could be a global variable, a result parameter or a functionresult. This approach requires explicit checking by the programmer for the error values. For example, the eof boolean is set to true when the program has readthe last item in a file. The eof condition can then be checked before attemptingto read from a file. The disadvantage of this approach is that a program tends toget cluttered with code to test the results. A more serious consequence is that a

programmer may forget to include a test with the result that the exception isignored.

Responses to an ExceptionReturn a label and execute a goto -- Fortran

Issues

Resumption of Program Execution

Once an exception has been detected, control is passed to the handler thatdefines the action to be taken when the exception is raised. The questionremains, what happens after handling the exception?

One approach is to treat exception handlers as subroutines to which control is passed and after the execution of the handler control returns to the pointfollowing the call to the handler. This is the approach taken in PL/1. It impliesthat the handler ``fixed'' the state that raised the condition.

Another approach is that the exception handler's function is to provide a clean-up operation prior to termination. This is the approach taken in Ada. The unit inwhich the exception occurred terminates and control passes to the calling unit.Exceptions are propagated until an exception handler is found.

Suppression of the Exception

Some exceptions are inefficient to implement (for example, run time rangechecks on array bounds). The such exceptions are usually implemented insoftware and may require considerable implementation overhead. Somelanguages give the programmer control over whether such checks and theraising of the corresponding exception will be performed. This permits thechecks to be turned on during program development and testing and then turnedoff for normal execution.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 25/62

1. Handler Specification2. Default Handlers

Propagation of Exception

Side effectsSide effects are a feature of imperative programming languages that makereasoning about the program difficult. Side effects are used to providecommunication among program units. When undisciplined access to globalvariables is permitted, the program becomes difficult to understand. The entire

program must be scanned to determine which program units access and modifythe global variables since the call command does not reveal what variables may

be affected by the call.

At the root of differences between mathematical notations and imperative programs is the notion of referential transparency (substitutivity of equals for equals). Manipulation of formulas in algebra, arithmetic, and logic rely on the

principle of referential transparency. Imperative programming languagesviolate the principle. For example:

integer f(x:integer){

y := y+1;f := y + x

}This ``function'' in addition to computing a value also changes the value of theglobal variable y . This change to a global variable is called a side effect . Inaddition to modifying a global variable, it is difficult to reason with thefunction itself. For example, if at some point in the program it is known that y

= z = 0 then f(z) = 1 in the sense that after the call f(z) will return 1. But,should the following expression occur at that point in the program, it will befalse.

1 + f(z) = f(z) + f(z)

I/O functions of necessity involve side effects. The following expressionsinvolving the C function getint may return different values even thoughalgebraically they appear to have the same value.

2 * getint()getint() + getint()

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 26/62

The first multiplies the next integer read from the input file by two while thesecond expression denotes the sum of the next two successive integers readfrom the input file.

Aliasing and dangling referencesTwo names are aliases if they denote ( share ) the same data object during a unitactivation. Aliasing is another feature of imperative programming languagesthat makes programs harder to understand and harder to reason about.

One way aliases occur is when two or more arguments to a subprogram are thesame. When a data object is passed by ``reference'' it is referenced both by itsname in the calling environment and its parameter name in the calledenvironment. In the following subprogram, the parameters are in-out

parameters.

aliasingExample (m, n : in out integer);{

n := 1;n := m + n

}The two parameters are used as different names for the same object in thecall aliasingExample( i, i) . In this example, the result is that i is set to 2. Inthe call aliasingExample( a[i], a[j] ) the result depends on the valuesof i and j with aliasing occuring when they are equal. This second callillustrates that aliasing can occur at run time so the detection of aliasing may bedelayed until run time and so compilers cannot be relied on to detect aliasing.

Aliasing interferes with optimizing phase of a compiler. Optimizationsometimes requires the reordering of steps or the deletion of unnecessary steps.The following assignments which appear to be independent of each other illustrate an order depencency.

x := a + by := c + d

If x and c are aliases for the same object, the assignments are interdependentand the order of evaluation is important.

The purpose of the equivalence command in FORTRAN is the creation of aliases. It permits the efficient use of memory (historically a scarce commodity)and can be used as a crude form of a variant record. Another way in whichaliasing can occur is when a data object may be a component of several dataobjects (referenced through pointer linkages).

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 27/62

• Formal and actual parameters share the same data object.• Procedure calls have overlapping actual parameters.• A formal parameter and a global variable denote the same data object.

Pointers are intrinsically generators of aliasing.

var p, q : ^T ;...new( p );q := p

When a programming language requires programmers to manage memory for dynamically allocated objects and the language permits aliasing, an objectreturned to memory may still be accessible though an alias and the value may

be changed if the memory manager allocates the same storage area to another object. In the following code,

type pointer = ^Integer var p : Pointer;

procedure Dangling; var q : Pointer; begin;

new(q); q^ := 23; p := q; dispose(q)end;

beginnew(p); Dangling(p)

end;

the pointer p is left pointing to a non-existent value.

The problem of aliasing arises as soon as a language supports variables andassignment. If more than one assignment is permitted on the same variable x,the fact that x=a cannot be used at any other point in the program to infer a

property of x from a property of a . Aliasing and global variables only magnifythe issue.

Historical Perspectives and Further Reading

Imperative languages have a rich and varied history. The first imperative programming languages were machine languages. Machine instructions weresoon replaced with assembly languages which are essentially transliterations of machine code.

Early Imperative LanguagesFORTRAN (FORmula TRANslation) was the first high level language to gainwide acceptance. It was designed for scientific applications and featured an

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 28/62

algebraic notation, types, subprograms, and formatted input/output. It wasimplemented in 1956 by John Backus at IBM specifically for the IBM 704machine. Efficient execution was a major concern consequently, its structureand commands have much in common with assembly languages. FORTRANwon wide acceptance and continues to be in wide use in the scientificcomputing community.

COBOL (COmmon Business Oriented Language) was designed (by acommittee of representatives of computer manufactures and the Department of Defense) at the initiative of the U. S. Department of Defense in 1959 andimplemented in 1960 to meet the need for business data processingapplications. COBOL featured records, files and fixed decimal data. It also

provided a ``natural language'' like syntax so that programs would be able to beread and understood by non-programmers. COBOL won wide acceptance in the

business data processing community and continues to be in wide use.

ALGOL 60 (ALGorithmic Oriented Language) was designed in 1960 by aninternational committee for use in scientific problem solving. UnlikeFORTRAN it was designed independently of an implementation, a choicewhich lead to an elegant language. The description of ALGOL 60 introducedthe BNF notation for the definition of syntax and is a model of clarity andcompleteness. Although ALGOL 60 failed to win wide acceptance, itintroduced block structure, structured control statements and recursive

procedures into the imperative programming paradigm.

Evolutionary DevelopmentsPL/I (Programming Language I) was developed at IBM in the mid 1960s. Itwas designed as a general purpose language to replace the specific purposelanguages like FORTRAN, ALGOL 60, COBOL, LISP, and APL (APL andLISP were considered in the chapter on functional programming. PL/Iincorporated block structure, structured control statements, and recursion fromALGOL 60, subprograms and formatted input/output from FORTRAN, filemanipulation and the record structure from COBOL, dynamic storageallocation and linked structures from LISP, and some array operations from

APL. PL/I introduced exception handling and multitasking for concurrent programming. PL/I was complex, difficult to learn, and difficult to implement.For these and other reasons PL/I failed to win wide acceptance.

ALGOL 68 was designed to be a general purpose language which remediedPL/I's defects by using a small number of constructs and rules for combiningany of the constructs with predictable results--orthogonality. The description of

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 29/62

ALGOL 68 issued in 1969 was difficult to understand since it introduced anotation and terminology that was foreign to the computing community.ALGOL 68 introduced orthogonality and data extensibility as a way to producea compact but powerful language. The ``ALGOL 68 Report'' considered to beone of the most unreadable documents ever printed and implementationdifficulties prevented ALGOL 68's acceptance.

Pascal was developed by Nicklaus Wirth partly as a reaction to the problemsencountered with ALGOL 68 and as an attempt to provide a small and efficientimplementation of a language suitable for teaching good programming style. C,which was developed about the same time, was an attempt to provide anefficient language for systems programming.

Modula-2 and Ada extended Pascal with features to support module based program development and abstract data types. Ada was developed as the resultof a Department of Defense initiative while Modula-2 was developed by

Nicklaus Wirth. Like PL/1 and Algol-68, Ada represents an attempt to producea complete language representing the full range of programming tasks.

Simula 67 added coroutines and classes to ALGOL 60 to provide a languagemore suited to solving simulation problems. The concept of classes in object-oriented programming can be traced back to Simula's classes. Small-talk combined classes, inheritance, and ease of use to provide an integrated object-oriented development environment. C++ is an object-oriented programminglanguage derived from C. Java, a simplified C++, is an object-orientedlanguages designed to dynamically load modules at runtime and to reduce

programming errors.

Expression-oriented languagesExpression-oriented languages achieve simplicity and regularity by eliminatingthe distinction between expressions and commands. This permits asimplification in the syntax in that the language does not need both procedureand function abstractions nor conditional expressions and commands since theevaluation of an expression may both yield a value and have a side effect of

updating variables. Since the assignment expression V := E can be defined to both yield the value of the expression E and assign V to the value of E ,expressions of the form V 0 := ... := V n := E are possible giving multipleassignment for free. Algol-68, C, Scheme, and ML are examples of expressionoriented languages.

Exercises

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 30/62

1. [Time/Difficulty](section)Give all possible forms of assignment found inthe programming language C.

2. Give axiomatic, denotational and operational semantics for thesimultaneous assignment command.

3. Discuss the relationship between the assignment command and input andoutput commands.

4. Give axiomatic, denotational and operational semantics for the goto command.

5. Find an algorithm which transforms a program containing goto s into anequivalent program without goto s.

6. Give axiomatic, denotational and operational semantics for the skip command.

7. What is used to indicate sequential composition ina. the Pascal family of languages?

b. the C family of languages?8. Show how to implement the if-then-else command using unstructuredcommands.

9. Show how to implement a structured while-do and if-then-elsecommands using unstructured commands.

10.Show that case and if commands are equivalent by implementing a casecommand using if commands and an if command using a case command.

11.Compare and contrast the if and case/switch commands of Ada and C.12.Compare and contrast the looping commands of Ada and C.13.Show how to implement the repeat-until and for-do commands in terms

of a while-do command.14.Show that while and repeat until control structures are equivalent.15.Design a generalized looping command and give its axiomatic

semantics.16.Give axiomatic semantics for the IF-FI and DO-OD commands.17. Give axiomatic semantics for the C/C++/Java for command.18.Provide recursive definitions of the iterative control structures while,

repeat, and for.19.Alternative control structures20.What is the effect on the semantic descriptions if expressions are

permitted to have side effects?21.Axiomatic semantics22.Denotational semantics23.Operational semantics24.Classify the following common error/exception conditions as either

domain or range errors.a. overflow -- value exceeds representational capabilities

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 31/62

b. undefined value -- variable value is undefinedc. subscript error -- array subscript out of ranged. end of input -- attempt to read past end of input filee. data error -- data of the wrong type

LISP Tutorial Lecture 4: ImperativeProgramming

Imperative vs Functional Programming

The fragment of LISP we have learned so far is purelyfunctional. We program by defining mathematicalfunctions. Programming this way has the benefitof referential transparency , which means that anexperession has the same value and the same behavior,no matter where it is located, and how many times it isinvoked. For example, the following function alwaysreturns 4 when an input 2 is supplied:(defun double (x) (* 2 x))

Such programs are easy to develop and debug. The onlyeffect of calling a function is the returned value.Functions become a lot harder to understand anddebug when we allow them to produce side effects ,that is, affecting subsequent computation in waysother than returning a value. This style of

programming, in which side effect is not onlypermissible but is also the primary means by whichwe program, is called imperative programming . Thisis not something new, but is in fact the very kind of programming habits that you have acquired since

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 32/62

you learned your first programming language (e.g.C/C++, Pascal, Java). Because of this, weintentionally leave the topic of imperative

programming to the end of this series of tutorials.We do so since you already know so much aboutimperative programming, and since, in my taste,teaching it before teaching functional programmingwould only reinforce some of the bad habitsprogrammers usually have, and thereby luring youto write C code with LISP, which, of course, make itharder to finish your assignment on time.

Variables

Imperative programming is made possible by the notion of program state . The behavior of an expression depends on the exact state a program is in. In turn,the state of a program is defined by, guess what, variables .

Suppose we want to create a global counter to keep track of the times somecomputational events occur. Common LISP allows us to define global variablesas follows.

USER(7): (defparameter *counter* 0 "Counter to keep track of ....")*COUNTER*USER(8): (setf *counter* (1+ *counter*))1USER(9): *counter*1USER(10): (setf *counter* (1+ *counter*))2USER(11): *counter*200We use the defparameter special form to define a global variable *counter* ,

with zero as its initial value. We also decorate the declaration by adocumentation string. We then use the setf special form to assign new valuesto the variable. The setf special form returns the new value of the variable.Lastly, evaluating the variable gives its current value. Notice that *counter* isevaluated to two different values at different point of time. This is somethingyou will never see if you work with purely functional programs.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 33/62

Suppose we actually want this counter to reset to zero when it reaches a user defined threshold. We would encapsulate this service into the followingdefinitions:

(defparameter *counter* 0

"Counter to keep track of ....")(defparameter *threshold* 4

"Counter will be reset to zero when its value reaches thisthreshold.")

(defun counter-inc ()"Increment global counter by one."(if (>= (1+ *counter*) *threshold*)

(setf *counter* 0)(setf *counter* (1+ *counter*))))

(defun counter-value ()"Return current value of global counter."*counter*)

(defun counter-threshold ()"Return current threshold of global counter."*threshold*)

USER(24): (counter-value)0USER(25): (counter-inc)1USER(26): (counter-inc)2USER(27): (counter-inc)3

USER(28): (counter-threshold)4USER(29): (counter-inc)0

The function counter-inc can also be defined as follows:

(defun counter-inc ()"Increment global counter by one."(if (>= (1+ *counter*) *threshold*)

(setf *counter* 0)(incf *counter*)))

Sequencing

With the possibility of state alteration, we also need a new kind of programming construct --- sequencing . This allows us to perform multiplestate-alterating operations, one after another. For example, we might want to

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 34/62

introduce another function to our global counter abstraction: a function tomodify the value of *threshold* on the fly:

(defun counter-set-threshold (th)"Set counter threshold to TH."(setf *threshold* th)(if (>= *counter* *threshold*)

(setf *counter* 0))*threshold*)

The function sets the global value of *threshold* to a new value, and thenchecks if the current counter value has exceeded the new threshold. If the check succeeds, then the counter is reset to zero. Lastly, the function evaluates andreturns the last expression, which is simply the new value of *threshold* .

Notice that in this example the body of a function is no longer one expression, but a sequence of expressions. They are evaluated in order, and the value of thelast expression is returned as a value of the sequence. The effect is

demonstrated as in the following trace:USER(2): (counter-value)0USER(3): (counter-inc)1USER(4): (counter-inc)2USER(5): (counter-inc)3USER(6): (counter-inc)0USER(7): (counter-inc)1USER(8): (counter-inc)2USER(9): (counter-set-threshold 2)0USER(10): (counter-value)0

In fact, forms like let , let* , when and unless all admit sequences as their bodies. The cond form allows sequencing in the body of each alternative. Theexception is if , due to its syntax. However, you can always get around byintroducing a let form in the alternatives or by using other conditional forms.

Before we move on, notice that we can rewrite the counter-inc function asfollows:

(defun counter-inc ()"Increment global counter by one."(if (>= (1+ *counter*) *threshold*)

(setf *counter* 0)(incf *counter*)))

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 35/62

The (incf x) form is simply a shorthand for (setf x (+ x 1)) . In general, thefollowing are useful shorthands when developing imperative programs:

Shorthand Meaning(incf x delta) (setf x (+ x delta))(incf x) (incf x 1)(decf x delta) (setf x (- x delta))(decf x) (decf x 1)(push e x) (setf x (cons e x))(pop x) (let ((e (first x))) (setf x (rest x)) e)

Exercise: Create a global stack abstraction, with interface functions for performing pushing and poping.

Closures

The above method for defining an abstraction is not good enough. For one, theglobal variables are not encapsulated. There is nothing to forbid a careless user to change the value of *counter* in ways violating the invariants of the counter abstraction. To enforce this, we make use of local variables and closures .

As one would expect, the names introduced by a let form turns out not to be aname binding to some immutable values. The names refer to local variables.Such variables follow typical lexical scoping rules , so that LISP always looksup the innermost variable definition when a variable name is to be evaluated.

What is more interesting is that, due to the ability to return functions as values,the local variable has a life span longer than the expression defining it.Consider the following example:

USER(20): (setf inc (let ((counter 0))#'(lambda ()

(incf counter))))#USER(21): (funcall inc)1USER(22): (funcall inc)2

USER(23): (funcall inc)3We assign a value to the global variable inc . That value is obtained by firstdefining local variable counter using let , and then within the lexical scope of the local variable, a lambda expression is evaluated, thereby creating anannonymous function, which is returned as a value to be assigned to inc . Themost interesting part is in the body of that lambda expression --- it incrementsthe value of the local variable counter ! When the lambda expression is

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 36/62

returned, the local variable persists, and is accessible only through theannonymous function.

The thing to remember from this example is that, in other kinds of languageslike Pascal and C/C++ the lexical scope of a local variable somehow coincidewith its life span. After executation passes beyond the boundary of a lexicalscope, all the local variables defined within it cease to exist. This is not true inlanguages supporting higher-order functions, in particular, expressions thatreturn functions as values. However, that is not to say that lexical scoping doesnot hold in such languages. In the contrary, lexical scoping is enforced strictly,and therefore the only place from which you can alter the value of counter is,as every true believer of lexical scoping would agree, within the lexical scopeof the variable --- the lambda expression. As a result, the counter state iseffectively encapsulated. The only way to modify it is by going through theannonymous function stored in inc . The technical term to refer to this thing thatis stored in inc , this thing that at the same time captures both the definition of afunction and the variables referenced within the function body is calleda function closure .

What if we want to define multiple interface functions for the encapsulatedcounter? Simple, just return all of them:

USER(34): (setf list-of-funcs (let ((counter 0))(list #'(lambda ()

(incf counter))#'(lambda ()

(setf counter 0)))))(#

#)USER(35): (setf inc (first list-of-funcs))#USER(36): (setf reset (second list-of-funcs))#USER(37): (funcall inc)1USER(38): (funcall inc)2USER(39): (funcall inc)3

USER(40): (funcall reset)0USER(41): (funcall inc)1Exercise: Define an encapsulated global stack.

Poor-Man's Object

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 37/62

Having only one instance of this encapsulated counter abstraction is not goodenough. Imagine cases when we need multiple instances of such counters, eachhaving its own state. Well, the answer is simple, we just evaluate the function-returning let form everytime we need a new instance of counter. Since, a newlocal variable will be created everytime we evaluate the let form, the functionclosure that is returned each time will be associated with a different incarnationof the local variable counter . To automate this process, of course we definea constructor for such closures:

(defun make-counter ()"Create a new instance of a counter object."(let ((counter 0))

(list #'(lambda ()(incf counter))

#'(lambda ()(setf counter 0)))))

Notice how this allows us to maintain independently encapsulated states:USER(44): (setf c1 (make-counter))(#

#)USER(44): (setf c2 (make-counter))(#

#)USER(45): (funcall (first c1))1USER(46): (funcall (first c1))2USER(47): (funcall (second c1))0USER(48): (funcall (first c2))1USER(49): (funcall (first c2))2USER(50): (funcall (first c2))3USER(51): (funcall (first c1))1USER(52): (funcall (first c1))2USER(53): (funcall (second c2))0USER(54): (funcall (first c1))3

To make invoking counter interface functions easier, we can define thefollowing shorthands:

(defun counter-increment (counter)"Increment counter."(funcall (first counter)))

(defun counter-reset (counter)

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 38/62

"Reset counter."(funcall (second counter)))

And now we have an all-rounded counter abstraction.USER(56): (counter-increment c1)4USER(57): (counter-reset c1)

0

The moral of this store is that, function closures are encapsulated states . Theyare a poor-man's version of objects, which, after all, are nothing butencapsulated states. (Yes, I am aware that Common LISP has a Common LISPObject System (CLOS), and there is no point of using closures in this manner if all we want is simply object orientation. But No, I want you to understand whathigher-order functions buy you, and how they serve as a building block for other advanced programming language constructs. Lastly, I don't want you tospend time struggling to learn yet another object system.)

Exercise: Implement a constructor for your encapsulated stack abstraction.Define appropriate shorthands for convenient invocation of interface functions.

Iterative Constructs

To round off this tutorial, we discuss something that you know so much about--- iterations. We start with something very simple:

USER(1): (dotimes (i 10) (print i))

0123456789NILThe (dotimes ( i n ) body ) form executes body N times. In addition, it defines a

local variable i, which receives an integer value ranging from 0 to n-1 . The body of the form could be a sequence of expressions. The form returns NIL .The (print x ) form prints the LISP object x to the console.

A similar construct is defined for iterating through a list:

USER(2): (dolist (i '(a b c d e)) (print i))

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 39/62

ABCDENIL

The most general looping construct is the do form:

(defun fibonacci (n)"Compute the N'th Fibonacci number."(do ((f1 0 f2)

(f2 1 (+ f1 f2))(i 0 (1+ i)))

((= i n) f2); empty body))

The first list following the do keyword is a list of local variables and their

initializations and update forms. Each member of this list has theformat ( var init-formupdate-form ) . Within this loop are defined three localvariables f1 , f2 and i . The three initialization forms 0, 1 and 0 are evaluatedfirst, and then assigned to the three locals simultaneously. In each subsequentiteration, the three update forms f2 , (+ f1 f2) and (1+ i) will be evaluatedfirst, and then the values are assigned to the three local variablessimultaneously. The second list following the do keyword (i.e. ((= i n) f2) )has the general format of ( exit-condition return-form ) . The exit condition (=

i n) is checked after the initialization is done, or after the update is performedin subsequent iterations. If the test succeeds, then the return form is evaluated,

and its value is returned. Otherwise, the body of the do form, which in this caseis empty, will be executed with the updated value of the local variables.

Indefinite looping can be achieved using the (loop body ) form. One may exitfrom such loop using the return-from form:

(defun fib (n)"Compute the N'th Fibonacci number."(let ((f1 0)

(f2 1)(i 0))

(loop

(if (= i n)(return-from fib f2))

; empty body(psetf f1 f2

f2 (+ f1 f2)i (1+ i)))))

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 40/62

The fib function has the same meaning as the fibonacci function coded interms of do . The psetf is a variation of setf that implements "parallel"assignment.

Exercise 2.21, Programming Languages book by Ravi SethiThe following grammar generates numbers in binary notation:

C ::= C 0 | A 1 | 0A ::= B 0 | C 1 | 1B ::= A 0 | B 1

a. The generated numbers are all multiples of 3.We prove by induction on the length s of the generated string that, for n >= 0,nonterminal C generates numbers of the form 3n; A generates numbers of theform 3n+1; and B generates numbers of the form 3n+2.

The basis, s <= 2 is by enumeration. C generates 0, 00, and 11, therepresentations of 0, 0, and 3, respectively, all multiples of 3. A generates 1 and01, and 1 is of the form 3n+1. B generates 10, the representation of 2, which isof the form 3n+2.

For the inductive step, consider strings of length s.

Case . The first production is for nonterminal C. If the string ends in 0, it must be generated starting with production C ::= C 0. The suffix 0 corresponds tomultiplication by 2. Since the C on the right side generates multiples of 3 by theinductive hypothesis, then so must C on the left. If the string ends in 1, it must

be generated starting with production C ::= A 1. By the inductive hypothesis Agenerates a string representing 3n+1, so C represents 2(3n+1)+1, which equals6n+3, a multiple of 3.

The cases for the strings generated by B and C are similar.

b. All multiples of 3 are generated by the grammar

We prove by induction on n >= 0 that all numbers of the form 3n are generated by C, numbers of the form 3n+1 are generated by A, and numbers of the form3n+2 are generated by B.

The basis n <= 1 is by enumeration.

Inductive step. Suppose true for all smaller values, and consider n+1.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 41/62

If n+1 is even, let it be 2m. Note that the inductive hypothesis applies to m,since m < n for n >= 2.

Then, 3(n+1) = 3(2m) = 2(3m). By the inductive hypothesis, C generates 3m,so by C ::= C 0, it also generates 2(3m). And, 3(n+1)+1 = 3(2m)+1 = 2(3m)+1,so A generates this by A ::= C 1 and the inductive hypothesis. And, 3(n+1)+2 =3(2m)+2 = 2(3m)+2 = 2(3m+1), so B generates this by B ::= A 0.

If n+1 is odd, let it be 2m+1. Again, the inductive hypothesis applies to m sincem < n for n >= 2.

Now, 3(2m+1) = 2(3m)+3 = 2(3m+1)+1, which C generates by rule C ::= A 1.And, 3(2m+1)+1 = 3(2m)+4 = 2(3m+2), which A generates by rule A ::= B 0.And, 3(2m+1)+2 = 3(2m)+5 = 2(3m+2)+1, which B generates by rule B ::= B1.

Structured programmingFrom Wikipedia, the free encyclopedia

Programming paradigms

Agent-oriented

Automata-based

Component-based

Flow-based

Pipelined

Concatenative

Concurrent computing

Relativistic programming

Data-driven

Declarative (contrast: Imperative )

Constraint

Dataflow

Cell-oriented (spreadsheets )

Reactive

Logic

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 42/62

Abductive logic

Answer set

Constraint logic

Functional logic

Inductive logic

Event-driven

Service-oriented

Time-driven

Expression-oriented

Feature-oriented

Function-level (contrast: Value-level )

Functional

Generic

Imperative (contrast: Declarative )

Procedural

Language-oriented

Discipline-specific

Domain-specific

Grammar-oriented

Dialecting

Intentional

Metaprogramming

Automatic

Reflective

Attribute-oriented

Template

Policy-based

Non-structured (contrast: Structured )

Array

Nondeterministic

Parallel computing

Process-oriented

Programming in the large / small

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 43/62

Semantic

Structured (contrast: Non-structured )

Modular (contrast: Monolithic)

Object-oriented

By separation of concerns :

Aspect-oriented

Role-oriented

Subject-oriented

Class-based

Prototype-based

Recursive

Value-level (contrast: Function-level )

v

d

e

Structured programming is a programming paradigm aimed on improving the clarity, quality, and

development time of a computer program by making extensive use of subroutines , block

structures and for and while loops - in contrast to using simple tests and jumps such as

the goto statement which could lead to "spaghetti code " which is both difficult to follow and to maintain.

It emerged in the 1960s, particularly from work by Böhm and Jacopini, [1] and a famous letter, " Go To

Statement Considered Harmful " , from Edsger Dijkstra in 1968 [2]—and was bolstered theoretically by

the structured program theorem , and practically by the emergence of languages such as ALGOL with

suitably rich control structures.

Contents

[hide ]

• 1 Low-level structure Programming

• 2 Structured programming languages

• 3 History

o 3.1 Theoretical foundation

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 44/62

o 3.2 Debate

o 3.3 Outcome

• 4 Common deviations

o 4.1 Exception handling

o 4.2 State machines

• 5 See also

• 6 References

• 7 External links

[edit ]Low-level structure Programming

At a low level, structured programs are often composed of simple, hierarchical program flow structures.

These are sequence, selection, and repetition:

"Sequence" refers to an ordered execution of statements.

In "selection" one of a number of statements is executed depending on the state of the

program. This is usually expressed with keywords such

as if..then..else..endif ,switch , or case .

In "repetition" a statement is executed until the program reaches a certain state, or operations

have been applied to every element of a collection. This is usually expressed with keywords suchas while , repeat , for or do..until . Often it is recommended that each loop should only

have one entry point (and in the original structural programming, also only one exit point, and a

few languages enforce this).

A language is described as block-structured when it has a syntax for enclosing structures between

bracketed keywords, such as an if-statement bracketed by if..fi as in ALGOL 68 , or a code section

bracketed by BEGIN..END , as in PL/I - or the curly braces {...} of C and many later languages.

[edit ]Structured programming languages

It is possible to do structured programming in any programming language, though it is preferable to

use something like a procedural programming language . Some of the languages initially used for

structured programming languages include: ALGOL, Pascal , PL/I and Ada - but most new procedural

programming languages since that time have included features to encourage structured programming,

and sometimes deliberatly left out features [3] that would make unstructured programming easy.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 45/62

[edit ]History

[edit ]Theoretical foundation

The structured program theorem provides the theoretical basis of structured programming. It states

that three ways of combining programs—sequencing, selection, and iteration—are sufficient toexpress any computable function . This observation did not originate with the structured programming

movement; these structures are sufficient to describe the instruction cycle of a central processing unit ,

as well as the operation of a Turing machine . Therefore a processor is always executing a "structured

program" in this sense, even if the instructions it reads from memory are not part of a structured

program. However, authors usually credit the result to a 1966 paper by Böhm and Jacopini, possibly

because Dijkstra cited this paper himself. The structured program theorem does not address how to

write and analyze a usefully structured program. These issues were addressed during the late 1960s

and early 1970s, with major contributions by Dijkstra , Robert W. Floyd , Tony Hoare , and David Gries .

[edit ]Debate

P. J. Plauger , an early adopter of structured programming, described his reaction to the structured

program theorem:

Us converts waved this interesting bit of news under the noses of the unreconstructed

assembly-language programmers who kept trotting forth twisty bits of logic and saying, ' I

betcha can't structure this.' Neither the proof by Böhm and Jacopini nor our repeated

successes at writing structured code brought them around one day sooner than they were

ready to convince themselves. [citation needed ]

Donald Knuth accepted the principle that programs must be written with provability in mind, but he

disagreed (and still disagrees [citation needed ]) with abolishing the GOTO statement. In his 1974 paper,

"Structured Programming with Goto Statements", he gave examples where he believed that a

direct jump leads to clearer and more efficient code without sacrificing provability. Knuth proposed

a looser structural constraint: It should be possible to draw a program's flow chart with all forward

branches on the left, all backward branches on the right, and no branches crossing each other.

Many of those knowledgeable in compilers and graph theory have advocated allowing only

reducible flow graphs.[who? ]

Structured programming theorists gained a major ally in the 1970s after IBM researcher Harlan

Mills applied his interpretation of structured programming theory to the development of an

indexing system for the New York Times research file. The project was a great engineering

success, and managers at other companies cited it in support of adopting structured

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 46/62

programming, although Dijkstra criticized the ways that Mills's interpretation differed from the

published work.

As late as 1987 it was still possible to raise the question of structured programming in a computer

science journal. Frank Rubin did so in that year with a letter, "'GOTO considered harmful'

considered harmful." Numerous objections followed, including a response from Dijkstra that

sharply criticized both Rubin and the concessions other writers made when responding to him.

[edit ]Outcome

By the end of the 20th century nearly all computer scientists were convinced that it is useful to

learn and apply the concepts of structured programming. High-level programming languages that

originally lacked programming structures, such as FORTRAN , COBOL, and BASIC, now have

them.

[edit ]Common deviations[edit ]Exception handling

Although there is almost never a reason to have multiple points of entry to a subprogram, multiple

exits are often used to reflect that a subprogram may have no more work to do, or may have

encountered circumstances that prevent it from continuing.

A typical example of a simple procedure would be reading data from a file and processing it:

open file;

while (reading not finished) {

read some data;

if (error) {

stop the subprogram and inform rest of the program about the

error;

}

}

process read data;

finish the subprogram;

The "stop and inform" may be achieved by throwing an exception, second return from the

procedure, labelled loop break, or even a goto. As the procedure has 2 exit points, it breaks the

rules of Dijkstra's structured programming. Coding it in accordance with single point of exit rule

would be very cumbersome. If there were more possible error conditions, with different cleanup

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 47/62

rules, single exit point procedure would be extremely hard to read and understand, very likely

even more so than an unstructured one with control handled by goto statements.

Most languages have adopted the multiple points of exit form of structural programming. C allows

multiple paths to a structure's exit (such as "continue", "break", and "return"), newer languages

have also "labelled breaks" (similar to the former, but allowing breaking out of more than just the

innermost loop) and exceptions.

[edit ]State machines

Some programs, particularly parsers and communications protocols , have a number of states that

follow each other in a way that is not easily reduced to the basic structures. It is possible to

structure these systems by making each state-change a separate subprogram and using a

variable to indicate the active state (see trampoline ). However, some programmers (including

Knuth [citation needed ]) prefer to implement the state-changes with a jump to the new state.

[edit ]See also

Control flow (more detail of high-level control structures)

Minimal evaluation

Object-oriented programming

Nassi–Shneiderman diagram

Programming paradigms

Structured exception handlingStructure chart

Switch statement , a case of multiple GOTOs

[edit ]References

1. Edsger Dijkstra , Notes on Structured Programming , pg. 6

2. Böhm, C . and Jacopini, G.: Flow diagrams, Turing machines and languages with only

two formation rules , CACM 9(5), 1966.

3. Michael A. Jackson , Principles of Program Design , Academic Press, London, 1975.

4. O.-J. Dahl , E. W. Dijkstra , C. A. R. Hoare Structured Programming , Academic Press,

London, 1972 ISBN 0-12-200550-3

this volume includes an expanded version of the Notes on Structured

Programming , above, including an extended example of using the structured

approach to develop a backtracking algorithm to solve the 8 Queens problem .

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 48/62

a pdf version is in the ACM Classic Books Series

Note that the third chapter of this book, by Dahl, describes an approach

which is easily recognized as Object Oriented Programming. It can be seen as

another way to "usefully structure" a program to aid in showing that it is correct.

1. ^ Böhm, Jacopini. "Flow diagrams, turing machines and languages with only two

formation rules" Comm. ACM , 9(5):366-371, May 1966

2. ^ Edsger Dijkstra (March 1968). "Go To Statement Considered Harmful"

(PDF). Communications of the ACM 11 (3): 147–148. doi:10.1145/362929.362947 . "The

unbridled use of the go to statement has as an immediate consequence that it becomes

terribly hard to find a meaningful set of coordinates in which to describe the process progress.

... The go to statement as it stands is just too primitive, it is too much an invitation to make a

mess of one's program."

3. ^ GOTO for example

From structured programming to object-oriented programmingWe will assume that the reader of this material has some knowledge of imperative

programming, and that the reader already has been exposed to the ideas of structured programming. More specifically, we will assume that the reader has some background inC programming. In Chapter 6 (corresponding to the second lecture of the course) wesummarize the relationships between C and C#.

1.1 Structured Programming 1.4 Towards Object-oriented Programming1.2 A structured program: Hangman 1.5 Abstract Datatypes1.3 Observations about StructuredProgramming

1.1. Structured ProgrammingContents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index

We approach object-oriented programming by reviewing the dominating programmingapproach prior to object-oriented programming. It is called structured programming . A

brief background on structured programming, imperative programming, and - moregenerally - different schools of programming is provided in Focus box 1.1 . I will

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 49/62

recommend that you read the Wikipedia article about structured programming [wiki-str- pro] . It captures, very nicely, the essence of the ideas.

Structured programming relies on use of high-level control structures instead of low-level jumping

Structured programming is also loosely coupled with top-down programming and program development by stepwise refinement

Structured programming covers several, loosely coupled ideas. As summarized above,one of these is the use of control structures (such as if, switch/case, while and for) insteadof gotos.

Use of relatively small procedures is another idea. A well-structured program shoulddevote a single procedure to the solution of a single problem. The splitting of problems insubproblems should be reflected by breaking down a single procedure into a number of

procedures. The idea of program development by stepwise refinement [Wirth71 ]

advocates that this is done in a top-down fashion. The items below summarize the way itis done.

• Start by writing the main program

Use selective and iterative control structuresPostulate and call procedures P1, ...,Pn

• Implement P1, ... Pn, and in turn the procedures they make use of

• Eventually, the procedures become so simple that they can be implementedwithout introducing additional procedures

Only few programmers are radical with respect to top-down structured programming. Inthe practical world it is probably much more typical to start somewhere in the middle,and then both work towards the top and towards the bottom.

Imperative programming, Structured programming, andProgramming paradigms.

FOCUSBOX 1.1

Imperative programming is one of the four main programming paradigms . The others arefunctional programming, object-oriented programming, and logic programming.

Imperative programming is closely related to the way low-level machine languages work:Commands are used to change the values of locations in the memory of the computer. Inhigh-level languages, this is achieved by use of assignment statements , which is used tochange the values of variables. The assignment statement is therefore the archetypicalcommand in imperative programming. Control structures (sequence, selection, and

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 50/62

iteration) come on top of that together with procedural abstractions.

Programming done in the early years of the computing era (before the introduction of Algol) is often thought of as "unstructured programming". Unstructured programming is

largely characterized by use of "jumping around" by means of goto commands. Theintroduction of if and while control structures together with procedures eliminated the needfor gotos. This can be shown theoretically, but - more important - it also holds true in the

practical world of imperative programming. Armed with the common control structures(if and while , for instance) and procedural abstraction, very few programmers are temptedto use a goto statement in the programs they write. Such programming, without use of gotostatements, is often called structured programming .

1.2. A structured program: HangmanContents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index

In order to be concrete we will look at parts of a C program. The program implements asimple and rudimentary version of the well-known Hangman game. We will pretend thatthe program has been developed according to the structured programming ideas describedin Section 1.1 .

The main Hangman program, main , is shown in Program 1.1 . The fragments shownin purple are postulated (in the sense discussed in Section 1.1 ). I.e., they are called, butnot yet defined at the calling time. The postulated procedures are meant to be definedlater in the program development process. Some of them are shown below.

1234567891011

int main(void){char *playerName;

answer again;

playerName = getPlayerName (); initHangman ();

do{ playHangman (playerName);

again = askUser ("Do you want to play again");} while (again == yes);

}Program 1.1 The main function of the

Hangman program.

The function getPlayerName is intended to prompt the Hangman player for his or her name. As it appears in Program 1.2 this function only uses functions from the C standardlibrary. Therefore there are no emphasized parts in getPlayerName .

123

char *getPlayerName(){char *playerName = (char*)malloc(NAME_MAX);

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 51/62

45678

printf("What is your name? ");fgets(playerName, NAME_MAX, stdin);playerName[strlen(playerName)-1] = '\0';return playerName;

}Program 1.2 The function

getPlayerName of main.

The function initHangman calls an additional initialization function called initPuzzles ,which reads a puzzle from a text file. We will here assume that this function does notgive rise to additional refinement. We do not show the implementation of initPuzzles .

1234

void initHangman (void){srand(time(NULL));

initPuzzles ("puzzles.txt");}

Program 1.3 The functioninitHangman of main.

askUser is a general purpose function, which was called in main in Program 1.1 . Weshow it in Program 1.4 (only on web) and we see that it does not rely on additionalfunctions.

1234567

891011

answer askUser(char *question){char ch;do {

printf("%s [y/n]? ", question);while (isspace(ch = fgetc(stdin)));if (ch == 'y')

return yes;

else if (ch == 'n')return no;}while (ch != 'y' || ch != 'n');

}Program 1.4 The function askUser of main.

The function playHangman , seen in Program 1.5 , is called by main in the outer loopin Program 1.1 . playHangman contains an inner loop which is related to a single round of

playing. As it appears from Program 1.5 playHangman calls a lot of additional functions(all emphasized, but not all of them included here).

123456789

void playHangman (char playerName[]){int aPuzzleNumber, wonGame;

puzzle secretPuzzle; hangmanGameState gameState;

char playersGuess;

initGame (playerName, &gameState);aPuzzleNumber = rand() % numberOfPuzzles();secretPuzzle = getPuzzle (aPuzzleNumber);

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 52/62

10111213141516171819202122232425

while ((gameState.numberOfWrongGuesses < N) &&(gameState.numberOfCorrectGuesses <

secretPuzzle.numberOfCharsToGuess)){gameStatistics (gameState, secretPuzzle);

presentPuzzleOutline (secretPuzzle,gameState); printf("\n"); presentRemainingAlphabet (gameState); printf("\n");

if (CHEATING) presentSecretPuzzle (secretPuzzle);printf("\n");playersGuess = getUsersGuess ();clrconsole();

updateGameState (&gameState, secretPuzzle, playersGuess);}

gameStatistics (gameState, secretPuzzle);wonGame = wonOrLost (gameState,secretPuzzle);

handleHighscore (gameState, secretPuzzle, wonGame);}

Program 1.5 The function playHangman of main.

In Program 1.6 (only on web) and Program 1.7 (only on web), we show two additionalfunctions, initGame and getPuzzle , both of which are calledinplayHangman in Program 1.5 .

1234567891011121314

void initGame (char playerName[], hangmanGameState *state){strcpy(state->playerName, playerName);state->numberOfWrongGuesses = 0;state->numberOfCorrectGuesses = 0;int i;for (i=0; i < 128; i++){

if (isalpha(i))state->charGuessed[i] = 0;

elsestate->charGuessed[i] = 1;

} initHighscoreFacility ("highscorefile");

clrconsole();}

Program 1.6 The function InitGame of playHangman.

1234567891011121314

puzzle getPuzzle(int n){FILE *puzzleFile;puzzle result;int i, ch, num;char *puzzleLine, *catStr, *puzzleStr;char *puzzleLineQ1, *puzzleLineQ2, *puzzleLineQ3, *puzzleLineQ4;

puzzleLine = (char*)malloc(PUZZLEMAXCOUNT);catStr = (char*)calloc(PUZZLEMAXCOUNT,1);puzzleStr = (char*)calloc(PUZZLEMAXCOUNT,1);

puzzleFile = fopen(puzzleFileName, "r");

for(i = 0; i < n*4; i++) matchDoubleQuoteFile (puzzleFile); /* read

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 53/62

1516171819202122232425262728293031323334

3536

through n*4 double quotes */while (isspace(ch = getc(puzzleFile))); /* skip

white space */ungetc(ch,puzzleFile); /* put

double quote back */

fgets(puzzleLine, PUZZLEMAXCOUNT, puzzleFile); /* read aline from puzzle file */

puzzleLineQ1 = matchDoubleQuoteStr (puzzleLine); /* identifyquotes in string */

puzzleLineQ2 = matchDoubleQuoteStr (puzzleLineQ1+1);puzzleLineQ3 = matchDoubleQuoteStr (puzzleLineQ2+1);puzzleLineQ4 = matchDoubleQuoteStr (puzzleLineQ3+1);

strncpy(catStr, puzzleLineQ1+1, puzzleLineQ2 - puzzleLineQ1 - 1);strncpy(puzzleStr, puzzleLineQ3+1, puzzleLineQ4 - puzzleLineQ3 -

1);

num = 0;

for(i = 0; i < strlen(puzzleStr); i++) if (isalpha(puzzleStr[i]))num++;

result.category = catStr,result.phrase = puzzleStr;result.numberOfCharsToGuess = num;fclose(puzzleFile);return result;

}Program 1.7 The function getPuzzle of

playHangman.

As already brought up in Section 1.1 many programmers do not strictly adhere tostructured programming and top-down refinement when coding the hangman program.If you have programmed Hangman, or a similar game, it is an interesting exercise toreflect a little on the actual approach that was taken during your own development.In Section 4.1 we return to the Hangman example, restructured as an object-oriented

program.

Exercise 1.1. How did you program the Hangman game?

This is an exercise for students who have a practical experience with the development of the Hangman program, or a similar game.

Recall how you carried out the development of the program.

To which degree did you adhere to top-down development by stepwise refinement ?

If you did not use this development approach, then please try to characterize how youactually did it.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 54/62

There is no solution to this exercise

1.3. Observations about Structured ProgrammingContents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index

We will now attempt to summarize some of the weaknesses of structured programming.This will lead us towards object-oriented programming.

Structured programming is not the wrong way to write programs. Similarly, object-oriented programming is not necessarily the right way . Object-oriented programming(OOP) is an alternative program development technique that often tends to be better if wedeal with large programs and if we care about program reusability.

We make the following observations about structured programming:

• Structured programming is narrowly oriented towards solving one particular problem

It would be nice if our programming efforts could be oriented more broadly

• Structured programming is carried out by gradual decomposition of thefunctionality

The structures formed by functionality/actions/control are not themost stable parts of a programFocusing on data structures instead of control structure is an alternative

approach• Real systems have no single top - Real systems may have multiple tops

[Bertrand Meyer]

It may therefore be natural to consider alternatives to the top-downapproach

Let us briefly comment on each of the observations.

When we write a 'traditional' structured program it is most often the case that we have asingle application in mind. This may also be the case when we write an object-oriented

program. But with object-oriented programming it is more common - side by side withthe development of the application - also to focus on development of program pieces thatcan be used and reused in different contexts.

The next observation deals with 'stable structures'. What is most stable: the overallcontrol structure of the program, or the overall data structure of the program? The former relates to use of various control structures and to the flow procedure calls. The latter relates to data types and classes (in the sense to be discussed in Chapter 11 ). It is often

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 55/62

argued that the overall program data structure changes less frequently than the overall program control structure. Therefore, it is probably better to base the program structureon decomposition of data types than on procedural decomposition.

The last observation is due to Bertrand Meyer [ Meyer88 ]. He claims that "Real systems

have no top". Let us take the Hangman program as an example. Even if it is likely that wecan identify a single top of most hangman programs (in our program, main of Program1.1 ) the major parts of the program should be able to survive in similar games, for instance in "Wheel of Fortune". In addition, a high score facility of Hangman should beapplicable in a broad range of games. The high score part of the Hangman program mayeasily account for half of the total number of source lines in Hangman, and therefore it isattractive to reuse it in other similar games. The simple textual, line-oriented user interface could be replaceable by a more flexible user graphical user interface. In thatway, even the simple Hangman program can easily be seen as a program with no top, or a

program with multiple tops.

Readers interested in a good and extended discussion of 'the road to object-orientation'should read selected parts of Bertrand Meyers book 'Object-oriented SoftwareConstruction' [ Meyer88 ]. The book illustrates object-oriented programming using the

programming language Eiffel, and as such it is not directly applicable to the project of this course. The book is available in two versions. Either of them can be used. In myopinion 'Object-oriented Software Construction' is one of the best books about object-oriented programming.

1.4. Towards Object-oriented ProgrammingContents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index

We are now turning our interests towards 'the object-oriented way'. Below we list someof the most important ideas that we must care about when we make the transition fromstructured programming to object-oriented programming. This discussion is, in severalways, continued in Chapter 2 .

• The gap between the problem and the level of the machine:

Fill the gap bottom up• Use the data as the basic building blocks

Data, and relations between data, are more stable than the actions ondata

• Bundle data with their natural operationsBuild on the ideas of abstract datatypesConsolidate the programming constructs that encapsulate data(structs/records)

• Concentrate on the concepts and phenomena which should be handled by the program

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 56/62

Make use of existing theories of phenomena and conceptsForm new concepts from existing concepts

• Make use of a programming style that allows us to collapse the programming of objects

Our approach to object-oriented programming is continued in Chapter 2 . Before that, wewill clarify the concept of abstract data types.

1.5. Abstract DatatypesContents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index

A data type (or, for short, a type) is a set of values. All the values in a type share anumber of properties. An abstract data type is a data type where we focus on the possibleoperations on the values in the type, in contrast to the representation of these values. Thisleads to the following definitions.

A datatype is a set of values with common properties. A datatype is aclassification of data that reflects the intended use of the data in a program.

An abstract datatype is a data type together with a set of operations on thevalues of the type. The operations hide and protect the actual representation of the data.

In this material, boxes on a dark blue background with white letters are intended to give precise definitions of concepts.

To strengthen our understanding of abstract data types (ADTs) we will show afew specifications of well-known data types: Stacks, natural numbers, and booleans. Aspecification answers "what questions", not "how questions". The details are only shownin the web version of the material.

Program 1.8 specifies what a stack is in term of the classicoperations push , pop , top (and others as well). The specification is written in a (locallydeveloped and fairly old) language called GAS. The specification does not bring forwardany particular representation of a stack, and the specification does not tell how the stack operations are implemented. Stacks are represented as constructor terms, suchas push(5, push (6, new ())) . The equations in the specifications tells how to reduceother terms, such as pop(pop(push(5, push (6, new ())))) .

1234

Type stack [int]declare

constructorsnew () -> stack;

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 57/62

5678910111213141516171819202122

push (int, stack) -> stack;destructors

pop (stack) -> stack;selectors

top (stack) -> int;isnew (stack) -> bool;

for alli in int;s in stack;

letpop (new()) = error;pop (push (i,s)) = s;top (new()) = error;top (push (i,s)) = i;isnew (new()) = true;isnew (push(i,s)) = false;

endend stack.

Program 1.8 A specification of theabstract datatype stack.

The specification of natural numbers (0, 1, 2, ...) denotes a non-negative integer assuccessors of 0. Thus, the number 3 is denoted as succ(succ(succ(null())))) . Giventhis, the equations specify the arithmetic operations plus and minus , together withthe leq (less than or equal) operation.

12345678910111213141516171819

20212223

Type natnum []declare

constructorsnull () -> natnum;succ (natnum) -> natnum;

othersplus(natnum,natnum) -> natnum;minus(natnum, natnum) -> natnum;leq(natnum,natnum) -> bool;

for all i,j in natnum;let

plus(null(),j) = j;plus(succ(i),j) = succ(plus(i,j));

leq(null(),j) = true;leq(succ(i), null()) = false;leq(succ(i), succ(j)) = leq(i,j);

minus(i, null()) = i;

minus(null() , succ(j)) = error;minus(succ(i), succ(j)) = minus(i,j);

endend natnum.

Program 1.9 A specification of the abstract datatype natural numbers.

Finally, in Program 1.10 (only on web) we show the boolean type, where the constructorsare very simple. The equations for and , or , not , and implies are shown.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 58/62

1234567891011121314151617181920

2122

Type boolean []declare

constructorstrue() -> boolean;false() -> boolean;

othersnot (boolean) -> boolean;and (boolean, boolean) -> boolean;or (boolean, boolean) -> boolean;implies (boolean, boolean) -> boolean;

for allb, c in boolean;

letnot(true()) = false();not(false()) = true();and(true(),b) = b;and(false(),b) = false();or(true(),b) = true();or(false(),b) = b;implies(b,c) = or(not(b),c);

endend boolean.

Program 1.10 A specification of the abstract datatype boolean.

-------------------------------------------------------------------------------------------------------------Structured Programming 1

This is the beginning of our study of the imperative programming paradigm .(Sethi: chapters 3 through 5.) This paradigm is characterized by sequences of actions, which change the state of a computation. Actions can be variableassignments or procedure calls.

Unlike some of the other programming paradigms we will study later,imperative programming doesn't directly support reasoning about correctness.

This implies we need strong discipline in the way we express imperative programs, to have any hope!

There are basically three levels of discipline that are used:

• Structured programming• Programming with invariants• Axiomatic semantics

Axiomatic semantics is one kind of underpinning for the other levels. We willdiscuss that next time.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 59/62

Structured programming

The main idea is syntax-directed control flow (so we haven't entirelyabandoned the topic of syntax---we will keep using ideas about syntaxthroughout the course). I commend to your attention the grammar for Pascalcontrol statements in Sethi, Fig. 3-3 on p. 65.

Note the distinction between indefinite iteration (while and repeat in Pascal,for in C) and definite iteration (for in Pascal).

The Pascal for loop has two forms:

for I := E to E do S

for I := E downto E do S

Aside on EBNF: how can you write the grammar for the two kindsof for statements more succinctly, using EBNF rather than straightBNF? Answer .

Note the discussion in Sethi of implementation of the case selectionstatement---how a good compiler might use three different ways of implementing it, depending on the number of cases and whether they are denseor sparse in the range from the minimum to the maximum case label. What arethose three ways?

Programming with invariants

The structured control flow statements are single-entry / single-exit . (Exceptwhen break , return , or continue are used.)

Their behavior can be characterized by conditions at entry and exit of astatement S .

At entry: pre-condition Q (an assertion in first-order logic)

At exit: post-condition R (another assertion)

Meaning is that whenever the statement is entered with Q true of the state, andif the statement terminates, then R is true of the resulting state. This is usuallyexpressed concisely with the notation

{Q} S { R}

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 60/62

and is called ``partial-correctness of S with respect to Q and R.''

``Partial'' because it is stated in terms of ``if the statement terminates''---it saysnothing about the case in which S fails to terminate (has an infinite loop). (Analternative formulation of program correctness insists on proving terminationand is thus called ``total correctness.'')

To make proof of partial correctness possible, we usually need additionalassertions attached to one or more points in S . These are called invariant assertions , or simply invariants .

An invariant assertion is, by definition, true whenever control reaches thecontrol point to which it is attached.

The main kind of invariant is a loop invariant ---one that is attached to, say the beginning of a while loop. It must be true initially and every time around theloop Note the example in Sethi of a linear search procedure and how it can be correctly

programmed by thinking in terms of invariant assertions. What is the loop invarexample?

Pointers enable us to effectively represent complex data structures, to changevalues as arguments to functions, to work with memory which has beendynamically allocated, and to store data in complex ways.

A pointer provides an indirect means of accessing the value of a particular dataitem. Lets see how pointers actually work with a simple example,

program pointers1( output );type int_pointer = integer;

var iptr : int_pointer;begin

new( iptr );iptr^ := 10;writeln('the value is ', iptr^);dispose( iptr )

end.

The line

type int_pointer = integer;

declares a new type of variable called int_pointer , which is a pointer (denoted by ^ ) to an integer.

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 61/62

The line

var iptr : int_pointer;

declares a working variable called iptr of type int_pointer . The

variable iptr will not contain numeric values, but will contain the address inmemory of a dynamically created variable (by using new ). Currently, there isno storage space allocated with iptr , which means you cannot use it till youassociate some storage space to it. Pictorially, it looks like,

The linenew( iptr );

creates a new dynamic variable (ie, its created when the program actually runson the computer). The pointer variable iptr points to the location/address inmemory of the storage space used to hold an integer value. Pictorially, it lookslike,

The line

iptr^ := 10;

means go to the storage space allocated/associated with iptr , and write in thatstorage space the integer value 10. Pictorially, it looks like,

8/3/2019 Attributes and Attribute Grammars

http://slidepdf.com/reader/full/attributes-and-attribute-grammars 62/62

The line

dispose( iptr )

means deallocate (free up) the storage space allocated/associated with iptr , and

return it to the computer system. This means that iptr cannot be used againunless it is associated with another new() statement first. Pictorially, it lookslike,