Presentation is loading. Please wait.

Presentation is loading. Please wait.

Louden, Programming Languages

Similar presentations


Presentation on theme: "Louden, Programming Languages"— Presentation transcript:

1 Louden, Programming Languages
For Java (and most other langauges we will cover), you are given a ticket. We can’t possibly cover everything you need to know, but we have allowed you admitance to the “show”. You know the debugger. You can run hello world. You understand key differences. You know where the documenation is. What you do with your admission ticket is up to you! Chapter 1 Louden, Programming Languages

2 Chapter 1 - Introduction
9/12/2018 Chapter 1 - Introduction Programming Languages: Principles and Practice, 2nd Ed. © Kenneth C. Louden, Adapted by Vicki Allan 2006

3 Louden, Programming Languages
Course Motivation Why are programming languages the way they are? How are particular language features implemented/supported? Chapter 1 Louden, Programming Languages

4 Course Motivation cont…
understand the underlying ideas of the main programming paradigms know more about the huge variety of programming languages understand how the syntax and semantics of languages can be defined precisely. know how important features are supported have a deeper understanding of the history and rationale behind languages like C++. Chapter 1 Louden, Programming Languages

5 Relationship between thought and language.
The Sapir-Whorf hypothesis in linguistics states that the structure of one's mother-tongue influences the way one's mind perceives the world. It has found at best very limited experimental support, at least in its strong form. One study has shown that subjects in memory tests are more likely to remember a given color if their mother language includes a word for that color. Example – if you had no identified concept of recursion, how would that affect the ability to reason about it? Chapter 1 Louden, Programming Languages

6 Why study programming languages?
Increased capacity to express ideas improved background for choosing language increased ability to learn new languages Better understanding of significance of implementation Ability to design new languages - or user interface Chapter 1 Louden, Programming Languages

7 Louden, Programming Languages
Example Beginning students – always wanted to know specific answers:Can I do X? What happens if I do Y? Often hadn’t tried the specific test, but could reason about it from general knowledge of implementation. Ex: What happens if I try to return a reference to a local variable? Chapter 1 Louden, Programming Languages

8 Louden, Programming Languages
The human-computer semantic gap Human: Interested in modelling the real world More interested in what computer should do than how Computer: Only data it can manipulate is sequences of zeros and ones. Understands low-level “how” instructions. Chapter 1 Louden, Programming Languages

9 What are programming languages…
High-level languages bridge the human-computer semantic gap by providing a higher level notation that can still be executed by computer Chapter 1 Louden, Programming Languages

10 What is a Programming Language?
Definition: A programming language is a notational system for describing computation in machine-readable and human-readable form. Chapter 1 Louden, Programming Languages

11 Louden, Programming Languages
Computation: Described by a Turing Machine - a very simple computer that can carry out all known computations (albeit not very efficiently). A programming language is Turing complete if it can be used to describe any computation performed by a Turing Machine. Chapter 1 Louden, Programming Languages

12 Turing Machine – 1936 Alan Turing
based on the idea of a person executing a well-defined procedure by changing the contents of an unlimited paper tape, divided into squares that can contain one of a finite set of symbols. "If your state is 42 and the symbol you see is a '0' then replace this with a '1', move one symbol to the right, and assume state 17 as your new state." A Turing machine is equivalent to a pushdown automaton made more powerful by relaxing the last-in-first-out requirement of its stack. More precisely, a Turing machine consists of: A tape which is divided into cells, one next to the other. Each cell contains a symbol from some finite alphabet. The alphabet contains a special blank symbol (here written as '0') and one or more other symbols. The tape is assumed to be arbitrarily extendible to the left and to the right, i.e., the Turing machine is always supplied with as much tape as it needs for its computation. Cells that have not been written to before are assumed to be filled with the blank symbol. A head that can read and write symbols on the tape and move left and right. A state register that stores the state of the Turing machine. An action table (or transition function) that tells the machine what symbol to write, how to move the head ('L' for one step left, and 'R' for one step right) and what its new state will be, given the symbol it has just read on the tape and the state it is currently in. If there is no entry in the table for the current combination of symbol and state then the machine will halt. Note that every part of the machine is finite; it is the potentially unlimited amount of tape that gives it an unbounded amount of storage space. Chapter 1 Louden, Programming Languages

13 What is needed for Turing completeness?
Virtually nothing: A programming language is Turing complete provided it has integer variables and arithmetic and sequentially executes statements, which include assignment, selection (if) and loop (while) statements. Even if statements are unnecessary Chapter 1 Louden, Programming Languages

14 Machine-readability:
Also not a huge requirement: Basically, the existence of a (more or less) linear-time translation algorithm. Usually boils down to: The syntax must be given by a context-free grammar. Chapter 1 Louden, Programming Languages

15 Louden, Programming Languages
Human-readability: This is the real issue! Virtually all the complex details of a programming language are there to (supposedly) enhance human readability. Still not very well understood. Is strongly dependent on good choice of abstractions. Chapter 1 Louden, Programming Languages

16 What about human “writability??”
Aren’t programming languages there to promote the writing of programs, not the reading of them? Nonsense! Writability is a hacker’s goal: Perl is very writable, but try to read it! Readability is the real goal: many people are going to have to read your program after you have written it. Chapter 1 Louden, Programming Languages

17 Louden, Programming Languages
Abstractions: Chapter 1 Louden, Programming Languages

18 Computational Paradigms
Programming languages began by imitating the operations of a computer. It is not surprising that the kind of computer for which they were written had significant effect on their design. variables representing memory assignment to change values sequential execution of statements Chapter 1 Louden, Programming Languages

19 Louden, Programming Languages
Language Paradigms: Imperative (procedural): traditional sequential programming (passive data, active control). Characterized by variables, assignment, and loops. Object-oriented: data-centric, data controls its own use, action by request to data objects. Characterized by messages, instance variables, and protection. Extension of imperative paradigm. Functional: passive data, but no sequential control; all action by function evaluation (“call”), particularly recursion. No local variables! ~ to mathematics Chapter 1 Louden, Programming Languages

20 Language Paradigms (cont.):
Logic: Assertions are the basic data; logic inference the basic control. Again, no sequential operation. ~ to mathematics ex: I am your sister if I am female and we have common parents. Parallel: well, maybe not really a paradigm, but some think so. Again, no sequential operation. “Declarative”: Logic and functional paradigms share this property: state “what” needs computing, not “how” (sequence). Chapter 1 Louden, Programming Languages

21 Languages and paradigms
Imperative: C, Pascal, core Ada, FORTRAN Functional: Lisp (Scheme), ML, Haskell Object-oriented: C++, Java, Smalltalk Logic: Prolog Parallel: Java (threads), Ada (tasks) Chapter 1 Louden, Programming Languages

22 Louden, Programming Languages
Perl The overall structure of Perl derives broadly from the programming language C. Perl is a procedural programming language, with variables, expressions, assignment statements, brace-delimited code blocks, control structures, and subroutines. Like the Unix shells, Perl has many built-in functions for common tasks, like sorting, and for accessing system facilities. Perl takes lists from Lisp, associative arrays from awk, and regular expressions from sed. These simplify and facilitate all manner of parsing, text handling, and data management tasks. Perl has many and varied applications. It has been used since the early days of the Web to write CGI scripts, and is an integral component of the popular LAMP (Linux / Apache / MySQL / (Perl / PHP / Python)) platform for web development. Perl is often used as a "glue language", tying together systems and interfaces that were not specifically designed to interoperate. Chapter 1 Louden, Programming Languages

23 Are functional languages Turing-complete?
Previous theorem on Turing-completeness depends on the existence of variables and loops. Functional programs do not have variables or loops. Can all computation be expressed? Yes!: A programming language is Turing complete if it has integer values, arithmetic functions on those values, and if it has a mechanism for defining new functions using existing functions, selection, and recursion. Chapter 1 Louden, Programming Languages

24 Paradigm use is rarely “pure”:
The C program (in text)defined gcd function in a purely functional style, even though C is mainly imperative. The Java program used some imperative code to compute the gcd, and was not completely object-oriented (integers aren’t objects). The Scheme code used sequencing to do I/O, an imperative feature. Chapter 1 Louden, Programming Languages

25 Examples of languages that are pure (mostly):
Imperative: (old) FORTRAN Functional: Haskell Object-oriented: Smalltalk Chapter 1 Louden, Programming Languages

26 Louden, Programming Languages
Language definition Syntax: the structure of a program. Usually given a formal (i.e., mathematical) definition using a context-free language. (Lexical structure - the structure of the words or tokens - uses regular expressions.) Semantics: the actual result of execution. Usually described in English, but can be done mathematically. Semantics can have a static component: type checking, definition checking, other consistency checks prior to execution. What are dynamic components of semantics? Chapter 1 Louden, Programming Languages

27 Louden, Programming Languages
Language translation inputs compiler outputs source executable run Compiler: two-step process that (1) translates source code into target code; then (2) the user executes the target code. Chapter 1 Louden, Programming Languages

28 Louden, Programming Languages
Language translation inputs outputs source run Interpreter: one-step process in which the source code is executed directly. Hybrids are also possible (Java). inputs Java compiler machine dependent interpreter source bytecode outputs Bytecode is composed of instructions that have been brought to the lowest level possible without making them machine dependent. Chapter 1 Louden, Programming Languages

29 Louden, Programming Languages
Compilation, Interpretation, and Hybrid systems Consider this piece of code: public class Test {    public static void main(String args[])    {       int i;       i = 2;       i = i + 7;    } } Chapter 1 Louden, Programming Languages

30 Louden, Programming Languages
If we were to compile it, we would change it to machine instructions that would only work for one architecture.  If we were to interpret it, our interpreter would have to be able to understand the high level code AND would repeatedly parse it (if the code was in a loop). When we use the hybrid approach of Java, we produce the file Test.class, which is a binary file that's not readable by most humans. We can convert the file to a readable form with the javap tool as shown here: Chapter 1 Louden, Programming Languages

31 Louden, Programming Languages
C:\ > javap -c Test Compiled from Test.java public class Test extends java.lang.Object {    public Test(); // a default constructor created    public static void main(java.lang.String[]); } Method Test()    0 aload_0    1 invokespecial #3    4 return Method void main(java.lang.String[])    0 iconst_2 // Put integer 2 on stack    1 istore_1 // Store the top stack value at location 1    2 iload_1  // Put the value at location 1 on stack    3 bipush 7 // Put the value 7 on the stack    5 iadd     // Add two top stack values together    6 istore_1 // The sum, on top of stack, stored at location 1    7 return   // Finished processing Chapter 1 Louden, Programming Languages

32 Louden, Programming Languages
Although the bytecode cannot access registers or directly reference memory locations and must obey various other restrictions, the actual JVM (java virtual machine) program can use internally whatever techniques are convenient to use for a particular platform. As long as the Java bytecode sees only a JVM specification compliant system, the JVM programmer has broad discretion for its implementation Chapter 1 Louden, Programming Languages

33 Language Implementation Methods
Compilation: lexical analysis: characters grouped into logical chunks (keywords, constants, etc) syntax analysis: figure out what it means - usually use parse trees (grammars to define). Like diagraming sentences. small dogs and cats – what is meant? optimization - to make smaller or faster linking: supplying missing addresses to system code load module: user code augmented with system code Chapter 1 Louden, Programming Languages

34 Louden, Programming Languages
The tall boy ran fast.  Chapter 1 Louden, Programming Languages

35 Louden, Programming Languages
"Maria gave Joe the rice" Chapter 1 Louden, Programming Languages

36 Language Implementation Methods (cont)
Pure Interpretation: no translation phase - fetch, decode, and execute the source code (not a machine code) Advantages/Disadvantages easy to write debugger - as source lines are unchanged execution is times slower; statement decoding is bottleneck better for simple structure - as not so slow to decode natural for some kinds of features - like dynamic binding of type. Ex: a = a+b If a may be integer, string, or a set, how can we know what code to generate? Chapter 1 Louden, Programming Languages

37 What is meant by dynamic binding?
Girls choice dance: Will you go with Sofie? (early binding) Will you go with Sofie/Ann/Betty (whoever shows up at your door)? (delayed binding) No specific partner assigned, but will change throughout the night. (changing binding) Chapter 1 Louden, Programming Languages

38 Hybrid Implementation System
Compile into intermediate code. Java - compiles to bytecode. Then bytecode is interpreted. JVM (Java Virtual Machine) is byte code interpreted and run time system. Java is delivered partially compiled. Developers compile it into byte codes, downloaded across the network. Then those byte codes are interpreted by the browser. Architecture-neutral: Sun designed the language so that it is also partially interpreted. Creating Bytecode does about 80% of the compilation work. However, one set of Bytecode can run on any Java-enabled computer. The last 20% is performed at runtime by the Java environment provided by the machine specific browser. Eliminates the version mismatch problems: All external program references are resolved when the application is executed. Chapter 1 Louden, Programming Languages

39 Louden, Programming Languages
Compilation Steps Figure out the full name of the class to be invoked Determine which method signature to use If there is more than one matching signature, the one that is most specific is chosen. Example: doit(Object o) or doit(ColoredPoint p) or doit (Point p) A method is applicable if the number of parameters matches the type of each actual argument can be converted to the type of the corresponding parameter. A method is accessible if the access modifier allows access. Chapter 1 Louden, Programming Languages

40 Louden, Programming Languages
Error classification Lexical: character-level error, such as illegal character (hard to distinguish from syntax). Syntax: error in structure (e.g., missing semicolon or keyword). Static semantic: non-syntax error prior to execution (e.g., undefined vars, type errors). Dynamic semantic: non-syntax error during execution (e.g., division by 0). Logic: programmer error, program not at fault. Chapter 1 Louden, Programming Languages

41 Notes on error reporting
A compiler will report lexical, syntax, and static semantic errors. It cannot report dynamic semantic errors. An interpreter will often only report lexical and syntax errors when loading the program. Static semantic errors may not be reported until just prior to execution. Indeed, most interpreted languages (e.g. Lisp, Smalltalk) do not define any static semantic errors. No translator can report a logic error. Chapter 1 Louden, Programming Languages

42 Louden, Programming Languages
Sample Errors (Java): public int gcd ( int v# ) // lexical { int z = value // syntax - missing ; y = v; // static semantic - y undefined while ( y >= 0 ) // dynamic semantic - // division by zero { int t = y; y = z % y; z = t; } return y; // logic - should return z Chapter 1 Louden, Programming Languages

43 Louden, Programming Languages
Language design Good, consistent set of abstractions. Tie-in to other technology: C : Unix Java : Internet C++ : most efficient OO language Now also: Ease of interface with other languages and systems Good libraries Chapter 1 Louden, Programming Languages

44 Programming Languages: Principles and Practice, 2nd Ed.
Chapter 2 - History Programming Languages: Principles and Practice, 2nd Ed. Louden, Chapter 2

45 Example of Babylonian “Programming” (p
Example of Babylonian “Programming” (p. 35 of text) to make an underground cistern to hold water: A cistern. The length equals the height. A certain volume of dirt has been excavated. The cross-sectional area plus this volume comes to 120. The length is 5. What is the width? Add 1 to 5, getting 6. Divide 6 into 120, obtaining 20. Divide 5 into 20, obtaining the width, 4. This is the procedure. Louden, Chapter 2

46 Notes on Babylonian “Program”:
Despite the numbers, it is expressing an algebraic solution to equations: Area + Volume = T (120 in the example) Area = length  width Volume = length  width  height height = length In code: T = length * width + length * width * length Solving for the width: w = T / (length + length * length) = T / (1 + length) / length Louden, Chapter 2

47 Jacquard Loom (Early 1800’s)
Machines for weaving cloth Weaving pattern was programmed by cards / paper tape Louden, Chapter 2

48 Babbage’s Analytical Engine
Mechanical digital computer (cogs, levers…) Programmed by a sequence of data and operation cards Never fully built, but some programs were written by Ada Lovelace (first programmer) Charles Babbage ( ) was an eminent figure in his day, He was a close acquaintance of Charles Darwin, Sir John Herschel, Laplace, and Alexander Humboldt, and was author of more than eighty papers and books on a broad variety of topics. His vision of a massive brass, steam-powered, general-purpose, mechanical computer inspired some of the great minds of the nineteenth century but failed to persuade any backer to provide the funds to actually construct it. It was only after the first computers had been built that designers discovered the extent to which Babbage had anticipated almost every aspect of their work. Louden, Chapter 2

49 1940’s: Languages without Machines
Lambda Calculus by Alonzo Church (1941) The basis for functional programming languages Konrad Zuse, a German engineer working alone while hiding out in the Bavarian Alps, develops Plankalkul (1945) Louden, Chapter 2

50 1950’s: first Implemented high level languages
Early 1950’s: First few stored program computers working. Mark 1, EDSAC, ACE, EDVAC, UNIVAC 1 Small memory machines Programmed in machine code The good old days!! Louden, Chapter 2

51 Grace Mary Hopper Programmer on Mark I, Mark II, and Mark III computers and UNIVAC I, the first large-scale electronic digital computer 1949 began work on first compiler A-0 Translated symbolic mathematical code into machine code Then came B-0, later called FLOW-MATIC. automatic billing and payroll calculation Technical advisor to CODASYL responsible for COBOL (1959) Louden, Chapter 2

52 Grace Hopper… It's easier to ask forgiveness than it is to get permission A ship in port is safe, but that is not what ships are for. Sail out to sea and do new things the most damaging phrase in the language is ‘We've always done it this way’ Louden, Chapter 2

53 FORTRAN - the first language
John Backus: leader of the group at IBM that developed FORTRAN and its compiler A tool for scientific calculation (FORmula TRANslation). Execution efficiency the main goal. Still very much in use today (Fortran I, II, III, IV, Fortran66, Fortran77, Fortran90, Fortran95). Louden, Chapter 2

54 Overview of FORTRAN IV Column 1 used to indicate comment lines
Column 2-5 used for line numbers (optional) Data: integer, real, arrays (no chars, records or pointers!) Variable declarations are optional (variables starting with I..N are integer, others are real) Louden, Chapter 2

55 Overview of FORTRAN IV…
Has a three-way if test, goto statements and computed gotos, but no recursion EQUIVALENCE declaration causes variables to be aliased (dangerous!) Louden, Chapter 2

56 Other languages followed quickly:
Algol: (Backus also involved). “Algorithmic language”. Finalized as Algol60. Much like C, except only arrays. Lives on in C, Java, C++. Lisp: (John McCarthy, still active at Stanford). Lives on as Scheme, Common Lisp. COBOL: (Grace Hopper). Lives on in many business applications. Louden, Chapter 2

57 COBOL (1959-1960) Common Business-Oriented Language
Developed in 1959 by a group of computer professionals called the Conference on Data Systems Languages (CODASYL). COBOL was the first programming language whose use was mandated by the US Department of Defense Louden, Chapter 2

58 COBOL… English – like verbose syntax (Goal: Human readability – but didn’t really help) Largely ignored by the academic community And if you thought COBOL was dead… Think again.. Object-oriented COBOL is a subset of COBOL 97, which is the fourth edition in the continuing evolution of ANSI/ISO standard COBOL Louden, Chapter 2

59 000100 IDENTIFICATION DIVISION. 000200 PROGRAM-ID. HELLOWORLD. 000300
000400* ENVIRONMENT DIVISION. CONFIGURATION SECTION. SOURCE-COMPUTER. RM-COBOL. OBJECT-COMPUTER. RM-COBOL. 000900 DATA DIVISION. FILE SECTION. 001200 PROCEDURE DIVISION. 100100 MAIN-LOGIC SECTION. BEGIN. DISPLAY " " LINE 1 POSITION 1 ERASE EOS DISPLAY "Hello world!" LINE 15 POSITION STOP RUN. MAIN-LOGIC-EXIT. EXIT. Louden, Chapter 2

60 ALGOL 60 ( ) ALGOrithmic Language: general expressive language for describing algorithms Used widely in Europe and academia in USA Modern syntax: defined using BNF, free format, structure statements, with begin/end pairs Type declarations required for all variables Louden, Chapter 2

61 ALGOL60… Introduced recursion, call-by-name and call-by-value
Required stack-based runtime environment Huge influence on later languages: Pascal, C, Module-2, Ada etc Louden, Chapter 2

62 Call-by-name Page 321 Louden
The argument is not evaluated until its actual use (as a parameter) in the called procedure The name of the argument replaces the name of the parameter it corresponds to Louden, Chapter 2

63 LISP (1956-1962) The first functional language
The first language to include garbage collection Intended for list processing and symbolic manipulation Syntax was radically different – lots of parentheses Efficiency not a huge concern. Ideas more important Still heavily used today for AI research and applications IDE Louden, Chapter 2

64 (T (append (reverse (rest start)) (list (first start))))))
Reverse a list (Defun reverse (x) (cond ((null x) NIL) (T (append (reverse (rest start)) (list (first start)))))) Louden, Chapter 2

65 Many new languages followed:
PL/I: “Universal language”. Forward-looking, but a failure. Too big, ahead of its time. Algol68: a theoretical triumph. A practical disaster. Still a very interesting language. BASIC: Distilled the most elementary ideas for the simplest computers. Still alive. Simula67: the first OO language. Way ahead of its time. But inefficient. Louden, Chapter 2

66 The 70s: simplicity and abstraction.
C (Ritchie, 1972), Pascal (Wirth,1971). Much simpler than 60s languages, but they add data structures a la COBOL and structured control a la Algol60. Added no new concepts to language design. C sometimes called middle layer (rather than high level a it is so close to underlying architecture “ADT” languages CLU, Euclid, Mesa, examined the ways programs can be decomposed into independent units ( ). Scheme (Sussman, Steele, 1975): a “regularized” LISP. Louden, Chapter 2

67 The 80s: the rise of modularity and object-orientation.
Modula-2 (Wirth, 1982). A not-so-successful sequel to Pascal. (After its time.) Ada (Ichbiah, 1980). A real attempt at a universal language, and much more successful than PL/I. But too bureaucratic for most programmers. Smalltalk80 (Alan Kay). Advances the cause of OO programming. C++ (Stroustrup, ). Shows that OO programming can be efficient. Louden, Chapter 2

68 The 90s: technology takes off
Increasing need for big libraries, APIs. Java (Gosling, 1995). The first language to come with an API already developed. System programming becomes huge (Perl, Tcl, Javascript, VBScript, Python, PHP) Scripting languages tie together utilities, library components, and operating system commands into complete programs. Functional languages keep pace: ML (Milner, ); Haskell (Hudak, Peyton-Jones, Wadler, ). Louden, Chapter 2

69 What’s next? C# (2000). Will it really replace Java?
Where will Java be in 5 or 10 years? (Most platform-specific applications are still written in C++ or C.) Will a “new” language come along? What happened to Prolog (Colmerauer, )? Does it make any sense to try to predict? Louden, Chapter 2

70 Chapter 3 - Language Design Principles
9/12/2018 Chapter 3 - Language Design Principles Programming Languages: Principles and Practice, 2nd Ed. Kenneth C. Louden Chapter 3 Louden

71 The language design problem
Language design is difficult, and success is hard to predict: Pascal a success, Modula-2 a failure Algol60 a success, Algol68 a failure FORTRAN a success, PL/I a failure Conflicting advice Chapter 3 Louden

72 Efficiency The “first” goal (FORTRAN): execution efficiency.
Still an important goal in some settings (C++, C). Many other criteria can be interpreted from the point of view of efficiency: programming efficiency: writability or expressiveness (ability to express complex processes and structures) reliability (security). maintenance efficiency: readability. (saw this as a goal for first time in Cobol) Chapter 3 Louden

73 Other kinds of efficiency
efficiency of execution (optimizable) efficiency of translation. Are there features which are extremely difficult to check at compile time (or even run time)? e.g. Alogol – prohibits assignment to dangling pointers Implementability (cost of writing translator) Chapter 3 Louden

74 Features that aid efficiency of execution
Static data types allow efficient allocation and access. Manual memory management avoids overhead of “garbage collection”. Simple semantics allow for simple structure of running programs (simple environments - Chapter 8). Chapter 3 Louden

75 Note conflicts with efficiency
Writability, expressiveness: no static data types (variables can hold anything, no need for type declarations). [harder to maintain] Reliability, writability, readability: automatic memory management (no need for pointers). [runs slower] Expressiveness, writability, readability: more complex semantics, allowing greater abstraction. [harder to translate] Chapter 3 Louden

76 Internal consistency of a language design: Regularity
Regularity is a measure of how well a language integrates its features, so that there are no unusual restrictions, interactions, or behavior. Easy to remember. Regularity issues can often be placed in subcategories: Generality: are constructs general enough? (Or too general?) Orthogonality: are there strange interactions? Uniformity: Do similar things look the same, and do different things look different? Chapter 3 Louden

77 Generality deficiencies
In pascal, procedures can be passed as parameters, but no procedure variable. Pascal has no variable length arrays –length is defined as part of definition (even when parameter) Chapter 3 Louden

78 Orthogonality: independence
Not context sensitive Seems similar to “generality” but more of an “odd” decision rather than a limitation. For example, if I buy a sweater, I may have the following choices: short sleeve, long sleeve, or sleeveless small, medium, or large red, green, or blue Chapter 3 Louden

79 Limitations to sweater example:
If it is not possible to get sleeveless sweaters, that may be a lack of generality. If any combination of any attributes can be used together, it is orthogonal. If red sweaters cannot be purchased in a small size, but other sweaters can, it is non-orthogonal Chapter 3 Louden

80 Orthogonality a relatively small set of primitive constructs can be combined in a relatively small number of ways. Every possible combination is legal. For example - in IBM assembly language there are different instructions for adding memory to register or register to register (non-orthogonal). In Vax, a single add instruction can have arbitrary operands. Closely related to simplicity - the more orthogonal, the fewer rules to remember. Chapter 3 Louden

81 For examples of non-orthogonality consider C++:
We can convert from integer to float by simply assigning a float to an integer, but not vice versa. (not a question of ability to do – generality, but of the way it is done) Arrays are pass by reference while integers are pass by value. A switch statement works with integers, characters, or enumerated types, but not doubles or Strings. Chapter 3 Louden

82 Regularity examples from C++
Functions are not general: there are no local functions (simplicity of environment). Declarations are not uniform: data declarations must be followed by a semicolon, function declarations must not. Lots of ways to increment – lack of uniformity (++i, i++, i = i+1) i=j and i==j look the same, but are different. Lack of uniformity Chapter 3 Louden

83 What about Java? Are function declarations non-general?
There are no functions, so a non-issue. (Well, what about static methods?) Are class declarations non-general? No multiple inheritance (but there is a reason: complexity of environment). Java has a good replacement: interface inheritance. Do declarations require semicolons? Local variables do, but is that an issue? (Not really - they look like statements.) Chapter 3 Louden

84 Java regularity, continued
Are some parameters references, others not? Yes: objects are references, simple data are copies. This is a result of the non-uniformity of data in Java, in which not every piece of data is an object. The reason is efficiency: simple data have fast access. What is the worst non-regularity in Java? My vote: arrays. But there are excuses. Chapter 3 Louden

85 Other design principles
Simplicity: make things as simple as possible, but not simpler (Einstein). (Pascal, C) We can make things so simple that it doesn’t work well – no string handling, no reasonable I/0 Can be cumbersome to use or inefficient. Chapter 3 Louden

86 Other design principles
Expressiveness: make it possible to express conceptual abstractions directly and simply. (Scheme) Helps you to think about the problem. Perl, for example, allows you to return multiple arguments: ($a,$b) = swap($a,$b); Chapter 3 Louden

87 Other design principles
Extensibility: allow the programmer to extend the language in various ways. (Scheme, C++) Types, operators Security: programs cannot do unexpected damage. (Java) discourages errors allows errors to be discovered type checking Chapter 3 Louden

88 Other design principles (cont.)
Preciseness: having a definition that can answer programmers and implementors questions. (Most languages today, but only one has a mathematical definition: ML). If it isn’t clear, there will be differences. Example: Declaration in local scope (for loop) unknown/known after exit Example: implementation of switch statement Example: constants – expressions or not? Example: how much accuracy of float? Chapter 3 Louden

89 Other design principles (cont.)
Machine-independence: should run the same on any machine. (Java- big effort) Consistent with accepted notations – easy to learn and understand for experienced programmers (Most languages today, but not Smalltalk & Perl) Restrictability: a programmer can program effectively in a subset of the full language. (C++: avoids runtime penalties) Chapter 3 Louden

90 Wikipedia moment: Syntactic sugar is a term coined by Peter J. Landin for additions to the syntax of a computer language that do not affect its expressiveness but make it "sweeter" for humans to use. Syntactic sugar gives the programmer (designer, in the case of specification computer languages) an alternative way of coding (specifying) that is more practical, either by being more succinct or more like some familiar notation. Chapter 3 Louden

91 C++ case study Thanks to Bjarne Stroustrup, C++ is not only a great success story, but also the best-documented language development effort in history: 1997: The C++ Programming Language, 3rd Edition (Addison-Wesley). 1994: The Design and Evolution of C++ (Addison-Wesley). 1993: A History of C , SIGPLAN Notices 28(3). Chapter 3 Louden

92 Major C++ design goals OO features: class, inheritance
Strong type checking for better compile-time debugging Efficient execution Portable Easy to implement Good interfaces with other tools Chapter 3 Louden

93 Supplemental C++ design goals
C compatibility (but not an absolute goal: no gratuitous incompatibilities) Incremental development based on experience. No runtime penalty for unused features. Multiparadigm Stronger type checking than C Learnable in stages Compatibility with other languages and systems Chapter 3 Louden

94 C++ design errors Too big?
C++ programs can be hard to understand and debug Not easy to implement Defended by Stroustrup: multiparadigm features are worthwhile No standard library until late (and even then lacking major features) Stroustrup agrees this has been a major problem Chapter 3 Louden

95 Lexical Analysis Why split it from parsing? Simplifies design
Parsers with whitespace and comments are more awkward Efficiency Only use the most powerful technique that works And nothing more No parsing sledgehammers for lexical nuts Portability More modular code More code re-use

96 Source Code Characteristics
Identifiers Count, max, get_num Language keywords: reserved or predefined switch, if .. then.. else, printf, return, void Mathematical operators +, *, >> …. <=, =, != … Literals “Hello World” Comments Whitespace

97 Reserved words versus predefined identifiers
Reserved words cannot be used as the name of anything in a definition (i.e., as an identifier). Predefined identifiers have special meanings, but can be redefined (although they probably shouldn’t). Examples of predefined identifiers in Java: anything in java.lang package, such as String, Object, System, Integer.

98 Language of Lexical Analysis
Tokens: category Patterns: regular expression Lexemes:actual string matched

99 Tokens are not enough… Clearly, if we replaced every occurrence of a variable with a token then …. We would lose other valuable information (value, name) Other data items are attributes of the tokens Stored in the symbol table

100 Token delimiters When does a token/lexeme end? e.g xtemp=ytemp

101 Ambiguity in identifying tokens
A programming language definition will state how to resolve uncertain token assignment <> Is it 1 or 2 tokens? Reserved keywords (e.g. if) take precedence over identifiers (rules are same for both) Disambiguating rules state what to do ‘Principle of longest substring’: greedy match

102 Regular Expressions To represent patterns of strings of characters REs
Alphabet – set of legal symbols Meta-characters – characters with special meanings  is the empty string 3 basic operations Choice – choice1|choice2, a|b matches either a or b Concatenation – firstthing secondthing (a|b)c matches the strings { ac, bc } Repetition (Kleene closure)– repeatme* a* matches { , a, aa, aaa, aaaa, ….} Precedence: * is highest, | is lowest Thus a|bc* is a|(b(c*))

103 Regular Expressions… We can add in regular definitions
digit = 0|1|2 …|9 And then use them: digit digit* A sequence of 1 or more digits One or more repetitions: (a|b)(a|b)*  (a|b)+ Any character in the alphabet . .*b.* - strings containing at least one b Ranges [a-z], [a-zA-Z], [0-9], (assume character set ordering) Not: ~a or [^a]

104 your tokens using regular expression.
SKIP : { " " | "\r" | "\t" } TOKEN : < EOL: "\n" > TOKEN : /* OPERATORS */ < PLUS: "+" > | < MINUS: "-" > | < MULTIPLY: "*" > | < DIVIDE: "/" > < FLOAT: ( (<DIGIT>)+ "." ( <DIGIT> )* ) | ( "." ( <DIGIT> )+ ) > | < INTEGER: (<DIGIT>)+ > | < #DIGIT: ["0" - "9"]> < TYPE: ("float"|"int")> | < IDENTIFIER: ( (["a"-"z"]) | (["A"-"Z"]) ) ( (["a"-"z"]) | (["A"-"Z"]) | (["0"-"9"]) | "_" )*> In JavaCC, you specify your tokens using regular expression.

105 Some exercises Describe the languages denoted by the following regular expressions 0 ( 0 | 1 ) * 0 ( ( 11 | 0 ) * ) * 0* 1 0* 1 0* 1 0 * Write regular definitions for the following regular expressions All strings that contain the five vowels in order (but not necessarily adjacent) aabcaadggge is okay All strings of letters in which the letters are in ascending lexicographic order All strings of 0’s and 1’s that do not contain the substring 011

106 For example, nested Tags in HTML
Limitations of REs REs can describe many language constructs but not all For example Alphabet = {a,b}, describe the set of strings consisting of a single a surrounded by an equal number of b’s S= {a, bab, bbabb, bbbabbb, …} For example, nested Tags in HTML

107 Lookahead It is the start of the next token!
<=, <>, < When we read a token delimiter to establish a token we need to make sure that it is still available as part of next token It is the start of the next token! This is lookahead Decide what to do based on the character we ‘haven’t read’ Sometimes implemented by reading from a buffer and then pushing the input back into the buffer And then starting with recognizing the next token

108 Classic Fortran example
DO 99 I=1,10 becomes DO99I=1,10 versus DO99I=1.10 The first is a do loop, the second an assignment. We need lots of lookahead to distinguish. When can the lexical analyzer assign a token? Push back into input buffer or ‘backtracking’

109 Finite Automata A recognizer determines if an input string is a sentence in a language Uses a regular expression Turn the regular expression into a finite automaton Could be deterministic or non-deterministic

110 Transition diagram for identifiers
RE Identifier -> letter (letter | digit)* letter accept start letter other 1 2 digit

111 An NFA is similar to a DFA but it also permits multiple transitions over the same character and transitions over  . In the case of multiple transitions from a state over the same character, when we are at this state and we read this character, we have more than one choice; the NFA succeeds if at least one of these choices succeeds. The  transition doesn't consume any input characters, so you may jump to another state for free. Clearly DFAs are a subset of NFAs. But it turns out that DFAs and NFAs have the same expressive power.

112 From a Regular Expression to an NFA Thompson’s Construction
(a | b)* abb e a 2 3 e e start e e 1 a b b 6 7 8 9 10 e e 4 5 accept b e

113 a start a b b 1 2 3 accept b Non-deterministic finite state automata NFA b a start a b b accept 01 02 03 a b a Equivalent deterministic finite state automata DFA

114 NFA -> DFA (subset construction)
We can covert from an NFA to a DFA using subset construction. To perform this operation, let us define two functions: The -closure function takes a state and returns the set of states reachable from it based on (one or more)  -transitions. Note that this will always include the state tself. We should be able to get from a state to any state in its -closure without consuming any input. The function move takes a state and a character, and returns the set of states reachable by one transition on this character. We can generalize both these functions to apply to sets of states by taking the union of the application to individual states. Eg. If A, B and C are states, move({A,B,C},`a') = move(A,`a')  move(B,`a') move(C,`a').

115 NFA -> DFA (cont) The Subset Construction Algorithm
Create the start state of the DFA by taking the -closure of the start state of the NFA. Perform the following for the new DFA state: For each possible input symbol: Apply move to the newly-created state and the input symbol; this will return a set of states. Apply the -closure to this set of states, possibly resulting in a new set. This set of NFA states will be a single state in the DFA. Each time we generate a new DFA state, we must apply step 2 to it. The process is complete when applying step 2 does not yield any new states. The finish states of the DFA are those which contain any of the finish states of the NFA.

116 Transition Table (DFA)
State a b 01 02 03 Input Symbol

117 Writing a lexical analyzer
The DFA helps us to write the scanner. Figure 4.1 in your text gives a good example of what a scanner might look like.

118 LEX (FLEX) Tool for generating programs which recognize lexical patterns in text Takes regular expressions and turns them into a program

119 Lexical Errors Only a small percentage of errors can be recognized during Lexical Analysis Consider if (good == “bad)

120 Line ends inside literal string Illegal character in input file
Examples from the PERL language Line ends inside literal string Illegal character in input file missing semi-colon missing operator missing paren unquoted string unopened file handle

121 In general What does a lexical error mean?
Strategies for dealing with: “Panic-mode” Delete chars from input until something matches Inserting characters Re-ordering characters Replacing characters For an error like “illegal character” then we should report it sensibly

122 Syntax Analysis also known as Parsing
Grouping together tokens into larger structures Analogous to lexical analysis Input: Tokens (output of Lexical Analyzer) Output: Structured representation of original program

123 A Context Free Grammar A grammar is a four tuple (, N,P,S) where
 is the terminal alphabet N is the non terminal alphabet P is the set of productions S is a designated start symbol in N

124 Parsing Need to express series of added operands
Expression  number plus Expression | number Similar to regular definitions: Concatenation Choice No Kleene closure – repetition by recursion Expression  number Operator Expression operator  + | - | * | /

125 BNF Grammar Expression  number Operator number
Structure on the left is defined to consist of the choices on the right hand side Meta-symbols:  | Different conventions for writing BNF Grammars: <expression> ::= number <operator> number Expression  number Operator number

126 Derivations Derivation:
Sequence of replacements of structure names by choices on the RHS of grammar rules Begin: start symbol End: string of token symbols Each step one replacement is made Exp  Exp Op Exp | number Op  + | - | * | /

127 Example Derivation Note the different arrows:
 Derivation applies grammar rules  Used to define grammar rules Non-terminals: Exp, Op Terminals: number, * Terminals: because they terminate the derivation

128 E  ( E ) | a What sentences does this grammar generate? An example derivation: E  ( E ) ((E))  ((a)) Note that this is what we couldn’t achieve with regular definitions

129 Recursive Grammars E  E  |  At seats, try using grammar to generate
anbn E  E  |  derives ,  ,   ,     ,      …. All strings beginning with  followed by zero or more repetitions of   *

130 Given the grammar rules shown below, derive the sentence This is the house that Jack built. Draw the parse tree, labeling your subtrees with the numbers of the grammar rules used to derive them. Grammar rules: S → NP VP NP → NP REL S | PRO | N | ART N | NAME VP → V | V NP N → house PRO →this |that REL→ that ART→ the V→ built | is NAME → Jack Which is easier – bottom up or top down?

131 Parse Trees & Derivations
Leafs = terminals Interior nodes = non-terminals If we replace the non-terminals right to left The parse tree sequence is right to left A rightmost derivation -> reverse post-order traversal If we derive left to right: A leftmost derivation pre-order traversal parse trees encode information about the derivation process

132 Abstract Syntax Trees Parse trees contain surplus information
+ exp exp op exp 3 4 This is all the information we actually need Token sequence number + number 3 4

133 An exercise Consider the grammar S->(L) | a L->L,S |S
What are the terminals, nonterminals and start symbol Find leftmost and rightmost derivations and parse trees for the following sentences (a,a) (a, (a,a)) (a, ((a,a), (a,a)))

134 Parsing token sequence: id + id * id
E  E + E | E * E | ( E ) | - E | id How many ways can you find a tree which matches the expression?

135 Example of Ambiguity Grammar: Expression: 2 + 3 * 4 Parse trees:
expr ® expr + expr | expr  expr | ( expr ) | NUMBER Expression: * 4 Parse trees:

136 Ambiguity If a sentence has two distinct parse trees, the grammar is ambiguous Or alternatively:is ambiguous if there are two different right-most derivations for the same string. In English, the phrase ``small dogs and cats'' is ambiguous as we aren't sure if the cats are small or not. `I see flying planes' is also ambiguous A language is said to be ambiguous if no unambiguous grammar exists for it. Dance is at the old main gym.  How it is parsed?

137 Ambiguous Grammars E  E + E | E * E | ( E ) | - E | id
Problem – no clear structure is expressed A grammar that generates a string with 2 distinct parse trees is called an ambiguous grammar 2+3*4 = 2 + (3*4) = 14 2+3*4 = (2+3) * 4 = 20 How does the grammar relate to meaning? Our experience of math says interpretation 1 is correct but the grammar does not express this: E  E + E | E * E | ( E ) | - E | id

138 Removing Ambiguity Two methods Disambiguating Rules
The basic notion is to write grammar rules of the form expr : expr OP expr and expr : UNARY expr for all binary and unary operators desired. This creates a very ambiguous grammar with many parsing conflicts. You specify as disambiguating rules the precedence of all the operators and the associativity of the binary operators. positives: leaves grammar unchanged negatives: grammar is not sole source of syntactic knowledge

139 Removing Ambiguity Two methods
2. Rewrite the Grammar Using knowledge of the meaning that we want to use later in the translation into object code to guide grammar alteration

140 Sometimes we can remove ambiguity from a grammar by by restructuring the productions, but sometimes the language is inherently ambiguous. For example, L={aibjck|i=j or j=k for i,j,k>=1} An ambiguous grammar to generate this language is shown below:

141 Precedence E  E addop Term | Term Addop  + | -
Term  Term * Factor | Term/Factor |Factor Factor  ( exp ) | number | id Operators of equal precedence are grouped together at the same ‘level’ of the grammar  ’precedence cascade’ The lowest level operators have highest precedence (The first shall be last and the last shall be first.)

142 Associativity ? 30 or 40 Subtraction is left associative, left to right (=30) E  E addop E | Term Does not tell us how to split up E  E addop Term | Term Forces left associativity via left recursion Precedence & associativity remove ambiguity of arithmetic expressions Which is what our math teachers took years telling us!

143 Extended BNF Notation Notation for repetition and optional features.
{…} expresses repetition: expr ® expr + term | term becomes expr ® term { + term } […] expresses optional features: if-stmt ® if( expr ) stmt | if( expr ) stmt else stmt becomes if-stmt ® if( expr ) stmt [ else stmt ]

144 Notes on use of EBNF Use {…} only for left recursive rules: expr ® term + expr | term should become expr ® term [ + expr ] Do not start a rule with {…}: write expr ® term { + term }, not expr ® { term + } term Exception to previous rule: simple token repetition, e.g. expr ® { - } term … Square brackets can be used anywhere, however: expr ® expr + term | term | unaryop term should be written as expr ® [ unaryop ] term { + term }

145 Syntax Diagrams An alternative to EBNF.
Rarely seen any more: EBNF is much more compact. Example (if-statement, p. 101):

146 Formal Methods of Describing Syntax
1950: Noam Chomsky (noted linguist) described generative devices which describe four classes of languages (in order of decreasing power) recursively enumerable x y where x and y can be any string of nonterminals and terminals. context-sensitive x  y where x and y can be string of terminals and non-terminals but y must be the same length or longer than x. Can recognize anbncn context-free (yacc) - nonterminals appear singly on left-side of productions. Any nonterminal can be replaced by its right hand side regardless of the context it appears in. Ex: If you were in the boxing ring and said ``Hit me'' it would imply a different action than if you were playing cards. Ex: If a IDENTSY which is between brackets is treated differently in terms of what it matches than an IDENTSY between parens, this is context sensitive Can recognize  anbn, palindromes regular (lex) Can recognize  anbm Chomsky was interested in the theoretic nature of natural languages.

147 Context Sensitive Allows for left hand side to be more than just a single non-terminal. It allows for “context” Context - sensitive : context dependent Rules have the form xYz->xuz with Y being a non-terminal and x,u,z being terminals or non-terminals.

148               . Consider the following context sensitive grammar G=(S,B,C,x,y,z,P,S) where P are S ®xSBC S® xyC CB ®BC yB® yy yC® yz zC® zz At seats, what strings does this generate?

149 How is Parsing done? Recursive descent (top down).
Bottom up – tries to match input with the right hand side of a rule. Sometimes called shift-reduce parsers.

150 Predictive Parsing Which rule to use?
I need to generate a symbol, which rule? Top down parsing LL(1) parsing Table driven predictive parsing (no recursion) versus recursive descent parsing where each nonterminal is associated with a procedure call No backtracking E -> E + T | T T -> T * F | F F -> (E) | id

151 Two grammar problems Eliminating left recursion (without changing associativity) E-> E+a|a I can’t tell which rule to use as both generate same symbol removing two rules with same prefix E->aB|aC

152 Removing Left Recursion
Before A --> A x A --> y After A --> yB B --> x B  B --> 

153 Removing common prefix (left factoring)
Stmt -> if Exp then Stmt else Stmt | if Expr then Stmt Change so you don’t have to pick rule until later Before: A -> ab1 | ab2 After: A -> aA’ A’ -> b1 | b2

154 Exercises Eliminate left recursion from the following grammars.
S->(L) | a L->L,S | S Bexpr ->Bexpr or Bterm | Bterm Bterm -> Bterm and Bfactor | Bfactor Bfactor -> not Bfactor | (Bexpr) | true | false

155 Table Driven Predictive Parsing
id + id * id Input a + b $ Predictive Parsing Program X Output Y Z $ Stack Parsing Table Partial derivation Not yet matched

156 Table Driven Predictive Parsing
Input Symbol Non Terminal id + ( ) * $ E E->TE’ E->TE’ E’ E’->+TE’ E’->e E’->e T T->FT’ T->FT’ T’ T’->e T’->*FT’ T’->e T’->e F F->id F->(E) Use parse table to derive: a*(a+a)*a

157 Table Driven Predictive Parsing
Parse id + id * id Leftmost derivation and parse tree using the grammar E -> TE’ E’ -> +TE’ | e T -> FT’ T’ -> *FT’ | e F -> (E) | id

158 But where did the parse table come from? First and Follow Sets
First and Follow sets tell when it is appropriate to put the right hand side of some production on the stack. (i.e. for which input symbols) E -> TE’ E’ -> +TE’ | e T -> FT’ T’ -> *FT | e F -> (E) | id id + id * id

159 First Sets If X is a terminal, then FIRST(X) is {X}
Get rid of left recursion E -> E + T | T T -> T * F | F F -> (E) | id E -> TE’ E’->+TE’|є T -> FT’ T’->*FT’ |є F -> (E) | id Left Factor First Sets If X is a terminal, then FIRST(X) is {X} IF X -> e is a production, then add e to FIRST(X) IF X is a non terminal and X -> Y1Y2…Yk is a production, then place a in FIRST(X) if for some i, a is in FIRST(Yi), and e is in all of First(Y1), …First(Yi-1). If e is in FIRST(Yj) for all j = 1, 2, …k, then add e to FIRST(X).

160 At seats – what are the FIRST sets
E -> TE’ E’ -> +TE’ | e T -> FT’ T’ -> *FT | e F -> (E) | id

161 Follow Sets Place $ in FOLLOW(S), where S is the start symbol and $ is the input right endmarker If there is a production A -> aBb, then everything in FIRST(b) except for e is placed in FOLLOW(B). Notice that b can be a combination of terminals and non-terminals. You need to compute FIRST using your rules. For each production A -> aB or A -> aBb where FIRST(b) contains e (i.e., b  e), then everything in FOLLOW(A) is in FOLLOW(B)

162 At seats – what are Follow Sets
E -> TE’ E’ -> +TE’ | e T -> FT’ T’ -> *FT | e F -> (E) | a

163 First E = { a ( } First E' = {+  } First T = { a ( } First T' = {*  } First F = { a ( } Follow E = { ) $} Follow E' = { ) $} Follow T = {+ ) $ } Follow T' = {+ ) $ } Follow F = {+ * ) $ }

164 FIRST and FOLLOW sets Construct first and follow sets for the following grammar after left recursion has been eliminated S->(L) | a L->L,S | S

165 First Follow A bc $bc B b c ik C bcd R gh є D dbc
Compute First and Follow Sets for this grammar A → BCD B→ bB B →c C → iR C →kR R →gR R →hR R →є D → AB D →d First Follow A bc $bc B b c ik C bcd R gh є D dbc

166 b c d g h i k $ A B C R D

167 Construction of the Predictive Parsing Table
For each production A -> a of the grammar do For each terminal a in FIRST(a), add A -> a to M[A,a]. Note that you are asked to find the FIRST of the whole right hand side (but you have only computed FIRST for non-terminals). If e is in FIRST(a), add A -> a to M[A, b] for each terminal b in FOLLOW(A). If e is in FIRST(a) and $ is in FOLLOW(A), add A -> a to M[A, $]. Make each undefined entry of M be error

168 a + * ( ) $ E E’ T T’ F Follow E = { ) $} First E = { a ( }
Follow T = {+ ) $ } Follow T' = {+ ) $ } Follow F = {+ * ) $ } First E = { a ( } First E' = {+  } First T = { a ( } First T' = {*  } First F = { a ( } a + * ( ) $ E E’ T T’ F

169 LL(1) grammars S -> if E then S S’ | a S’ -> else S | e
E -> b FIRST(S) = {if, a} FIRST(S’) = {else, e} FIRST(E) = {b} FOLLOW(S) = {$, else} FOLLOW(S’) ={$, else} FOLLOW(E) = {then} Construct the parsing table

170 LL(1) grammars An LL(1) grammar has no multiply defined entries in its parsing table Left-recursive and ambiguous grammars are not LL(1) A grammar G is LL(1) iff whenever A -> a | b are two distinct productions of G For no terminal a do both a and b derive strings beginning with a At most one of a and b can derive the empty string If be then a does not derive any string beginning with a terminal in FOLL0W(A) *

171 Recursive Descent Parsers
A function for each nonterminal example expression grammar Function Expr If the next input symbol is a ( or id then call function Term followed by function Expr’ Else Error Expr -> Term Expr’ Expr’ -> +Term Expr’ | e Term -> Factor Term’ Term’ -> *Factor Term’ | e Factor -> (Expr) | id

172 Bottom Up Parsing The correct RHS in a given right sentential form to rewrite to get the previous right sentential form, is called a handle. Consider the following grammar: EE + T|T T T * F |F F (E) |a Show the rightmost derivation for a+a*a E E + T E + T*F E + T*a E + F*a E + a*a T + a*a F + a*a a + a*a

173 Now, start at the last step of the rightmost derivation and go backwards.
This is what we are trying to do with a bottom up parser. We need to first determine that the a becomes an F, then that the F becomes a T, etc. In order to do bottom up parsing, we will rewrite the grammar only slightly so we can distinguish between the two productions shown on the same line. And we will give the productions numbers so we can refer to them 1. EE + T 2. E T 3. T T * F 4. TF 5. F (E) 6. Fa

174 The parsing table is generated by a tool like YACC, and the generation is beyond the scope of this course. However, we need to be able to use the parsing table. We want to read the input one symbol at a time and make a decision as to what to do, given a history of what we have done in the past. We have a parse stack to help us remember these important details. As we look at an input, there are only two choices of actions we take: We read the input and shift it over to our stack along with a “state”. Thus, the action S4 means to shift the current symbol to the stack and then push the state 4 to the stack. We look at the input and decide not to delete it from the input. Instead, we look at the top symbols of our stack and remove some and place others on. This step is called a reduce step.

175 The number of the reduce does NOT refer to a state
The number of the reduce does NOT refer to a state. It refers to the production which you use. For R5 (for example), the steps are as follows: Look at production 5 F(E). In our case, there are three symbols on the right hand side. This means for us to remove three sets of symbols from our stack. You should see a “(“, an “E”, and a “)” (along with their states) on the top of the stack. Remove these. They want to become an F (going backwards on the production). Before you place the F on the stack, look at the exposed state on the stack. Use that exposed state and F to determine the new state (using the Goto part of the table). That new state is then placed after F on the stack.

176

177

178 Chapter 5 Basic Semantics
K. Louden, Programming Languages

179 Ways to specify semantics
Language reference manual – commonly used, but lack of precision By a defining translator – questions cannot be answered in advance – only by trying it By formal definition – denotational semantics – complex and abstract A fundamental step in defining semantics is to describe the meaning of each identifier. Chapter 5 K. Louden, Programming Languages

180 K. Louden, Programming Languages
Attributes Properties of language entities, especially identifiers used in a program. Important examples: Value of an expression Data type of an identifier Maximum number of digits in an integer Location of a variable Code body of a function or method Declarations ("definitions") bind attributes to identifiers. Different declarations may bind the same identifier to different sets of attributes. Chapter 5 K. Louden, Programming Languages

181 Binding times can vary widely:
Value of an expression: during execution or during translation (constant expression). Data type of an identifier: translation time (Java) or execution time (Smalltalk, PERL). Maximum number of digits in an integer: language definition time or language implementation time. Location of a variable: load or execution time. Code body of a function or method: translation time or link time or execution time. Chapter 5 K. Louden, Programming Languages

182 K. Louden, Programming Languages
Two general categories of binding: static (prior to execution) and dynamic Interpreters will perform most bindings dynamically Concern is earliest time when it COULD be bound, not when it actually is Possible times Language definition Language implementation Translation time Link time Load time Execution time - dynamic }static Chapter 5 K. Louden, Programming Languages

183 K. Louden, Programming Languages
Many of the most important and subtle difference between languages involve binding times. Simple changes to the language (say adding recursion) may drastically change binding times. Languages in which types are dynamically bound are dramatically different from those in which types are statically bound. For example: Dynamic Type Binding flexibility - can write a generic sort routine where type isn't specified error detection is diminished is expensive as type checking must be done at run time; often implemented using pure interpreters Chapter 5 K. Louden, Programming Languages

184 K. Louden, Programming Languages
Classes of Binding Times (listed from late to early) 1. Execution Time (Late Binding). Variables to their values. Variables to a particular storage location (termed dynamic storage allocation). At entry to a subprogram or block. Example: formal to actual parameters and formal parameters to actual locations. At arbitrary points during execution. Example: variables to values. In some languages, variables to types. Consider Prolog - variable type is determined dynamically 2. Load time: globals bound to location 3. Link time: body of external function bound to call instruction 4. Compile time (Early Binding). Bindings chosen by programmer. Variable names, types, names. Bindings chosen by translator. Example: variables to storage (load time) (termed static storage allocation). Example: particular machine instruction for a statement. Example: initial values of variables (if none specified) Example: in C declaration defines type by gives no space 5. Language Implementation Time. Example: Association of enumerated type values with integers. Example: maxint 6. Language definition Time - probably the most important binding time. Example: structure of language fixed, set of all basic data types, set of statements: syntax and semantics fixed, predefined types. Chapter 5 K. Louden, Programming Languages

185 Symbol table and environment
A dictionary or table is used to maintain the identifier/attribute bindings. It can be maintained either during translation (symbol table) or execution (environment) or both. Pre-translation entities are entered into the initial or default table. If both are maintained, the environment can usually dispense with names, keeping track only of locations (names are maintained implicitly). Chapter 5 K. Louden, Programming Languages

186 K. Louden, Programming Languages
Declarations bind identifiers to attributes. Examples of declarations (C) int x = 0; Explicitly specifies data type and initial value. Implicitly specifies scope (see next slide) and location in memory. int f(double); Explicitly specifies type (double  int). Implicitly specifies nothing else: needs another declaration specifying code. The former is called a definition (as specifies all potential attributes) in C, the latter is simply a declaration (as other attributes- like function body - need to be specified later). Chapter 5 K. Louden, Programming Languages

187 K. Louden, Programming Languages
Scope The scope of a declaration is the region of the program to which the bindings established by the declaration apply. Informally - Scope of a variable: range of statements in which the variable is visible A variable is visible in a statement if it can be referenced in that statement. (Scope holes caused by new declarations) In a block-structured language, the scope is typically the code from the end of the declaration to the end of the "block" (indicated by braces {…} in C and Java) in which the declaration occurs. Scope can extend backwards to the beginning of the block in certain cases (class declarations in Java and C++, top-level declarations in Scheme). Chapter 5 K. Louden, Programming Languages

188 Lexical vs. dynamic scope
Scope is maintained by the properties of the lookup operation in the symbol table or environment. If scope is managed statically (prior to execution), the language is said to have static or lexical scope ("lexical" because it follows the layout of the code in the file). If scope is managed directly during execution, then the language is said to have dynamic scope. It is possible to maintain lexical scope during execution - via static links in the call stack. Chapter 5 K. Louden, Programming Languages

189 K. Louden, Programming Languages
Java scope example public class Test { public static int x = 2; public static void f() { System.out.println(x); } public static void main(String[] args) { int x = 3; f(); } } This prints 2, but under dynamic scope it would print 3 (the most recent declaration of x in the execution path is found). Chapter 5 K. Louden, Programming Languages

190 Dynamic scope evaluated
Almost all languages use lexical scope With dynamic scope the meaning of a variable cannot be known until execution time, thus there cannot be any static checking. Originally used in Lisp. Scheme could still use it, but doesn't. Some languages still use it: VBScript, Javascript, Perl (older versions). Lisp inventor (McCarthy) now calls it a bug. Still useful as a pedagogical tool to understand the workings of scope. Chapter 5 K. Louden, Programming Languages

191 Symbol table structure – how handle multiple uses of same name?
A table of little stacks of declarations under each name. For example the table for the Test class of slide 13 would look as follows inside main (using lexical scope): Chapter 5 K. Louden, Programming Languages

192 Symbol table structure (2)
Alternatively, a stack of little tables, one for each scope. For example, the previous example would look as follows (lexical scope): Can be deleted after leaving f Current table inside main Chapter 5 K. Louden, Programming Languages

193 Symbol table construction
Symbol table is constructed as declarations are encountered (insert operation). Lookups occur as names are encountered In lexical scope, lookups occur either as names are encountered in symbol table to that point (declaration before use—C), or all lookups are delayed until after the symbol table is fully constructed and then performed (Java class—scope applies backwards to beginning of class). In dynamic scope, need links to tell you which declarations to use Chapter 5 K. Louden, Programming Languages

194 Symbol table structure evaluated
Which organization is better? Table of little stacks is simpler (C, Pascal). Stack of little tables is more versatile, and helpful when you need to recover outer scopes from within inner ones or from elsewhere in the code (Ada, Java, C++). Normally, no specific table structure is part of a language specification: any structure that provides the appropriate properties will do. Chapter 5 K. Louden, Programming Languages

195 K. Louden, Programming Languages
Overloading Overloading is a property of symbol tables that allows them to successfully handle declarations that use the same name within the same scope. It is the job of the symbol table to pick the correct choice from among the declarations for the same name in the same scope. This is called overload resolution. Overloading typically applies only to functions or methods. Overloading must be distinguished from dynamic binding in an OO language. Chapter 5 K. Louden, Programming Languages

196 K. Louden, Programming Languages
An example in Java: public class Overload { public static int max(int x, int y) { return x > y ? x : y;} public static double max(double x, double y) { return x > y ? x : y;} public static int max(int x, int y, int z) { return max(max(x,y),z);} public static void main(String[] args) { System.out.println(max(1,2)); System.out.println(max(1,2,3)); System.out.println(max(4,1.3)); }} Adding more max functions that mix double and int parameters is ok. But adding ones that mix double and int return values is not! Chapter 5 K. Louden, Programming Languages

197 K. Louden, Programming Languages
Allocation Can be constructed entirely statically (Fortran): all vars and functions have fixed locations for the duration of execution. Can also be entirely dynamic: functional languages like Scheme and ML. Names of constants may represent purely compile time quantities. Most language use a mix: C, C++, Java, Ada. Consists of three components: A fixed area for static allocation A stack area for lifo allocation (usually the processor stack) A "heap" area for on-demand dynamic allocation (with or without garbage collection) Chapter 5 K. Louden, Programming Languages

198 Typical environment organization (possible C) [Figure 5.25, p. 165)]
© 2003 Brooks/Cole - Thomson Learning™ Typical environment organization (possible C) [Figure 5.25, p. 165)] Chapter 5 K. Louden, Programming Languages

199 K. Louden, Programming Languages
The Runtime Stack Used for: Procedure/function/method calls temporaries local variables Temporaries: intermediate results that cannot be kept in registers; not considered here further. Procedure calls: (parameters and return values) Local variables: part of calls, but can be considered independently, showing LIFO behavior for nested scopes (next slide). Chapter 5 K. Louden, Programming Languages

200 Example of stack-based allocation in C within a procedure:
(1) A: { int x; (2) char y; (4) B: { double x; (5) int a; (7) } /* end B */ (8) C: { char y; (9) int b; (11) D: { int x; (12) double y; (14) } /* end D */ (16) } /* end C */ (18) } /* end A */ Point #1 Point #2 Chapter 5 K. Louden, Programming Languages

201 K. Louden, Programming Languages
An alternative: a flat local space All locations can be determined at compile time All local variables allocated at onceWastes some space, but not critically. the primary structure of the stack is still the call structure: a complete record of a call on the stack is called an activation record or frame, and the stack is referred to as the call stack. (Chapter 8) Java promotes a flat space by forbidding nested redeclarations, but this is not an essential property: a symbol table can easily distinguish nested declarations as A.x, A.y, A.B.x, A.B.a, etc. Chapter 5 K. Louden, Programming Languages

202 K. Louden, Programming Languages
Heap Allocation In "standard" languages (C, C++, Java) heap allocation requires a special operation: new. Any kind of data can be allocated on the heap in C/C++; in Java all objects and only objects are allocated on the heap. Even with heap allocation available in Java & C/C++, the stack is still used to represent calls. In C/C++, deallocation is typically by hand (destructors), but it is hard to do right. Java uses a garbage collector that periodically sweeps the heap looking for data that cannot be accessed any more by the program and adding it back to free space. Chapter 5 K. Louden, Programming Languages

203 K. Louden, Programming Languages
Lifetime The lifetime of a program entity is the duration of its allocation in the environment. Allocation is static when the lifetime is the duration of the entire program execution. Lifetime is related to but not identical to scope. With scope holes, lifetime can extend to regions of the program where the program entity is not accessible. It is also possible for scope to exceed lifetime when a language allows locations to be manipulated directly (as for example manual deallocation). This is of course very dangerous! Chapter 5 K. Louden, Programming Languages

204 Variables and Constants
A variable is an object whose stored value can change during execution. x=y (we want value of y but address of x) referred to as l-value and r-value A constant is an object whose value does not change throughout its lifetime. Constants are often confused with literals: constants have names, literals do not. Chapter 5 K. Louden, Programming Languages

205 K. Louden, Programming Languages
Constants Compile-time constant in Java: static final int zero = 0; Load-time constant in Java: static final Date now = new Date(); Dynamic constant in Java: any non-static final assigned in a constructor. Java takes a very general view of constants, since it is not very worried about getting rid of them during compilation. C takes a much stricter view of constants, essentially forcing them to be capable of elimination during compilation. Chapter 5 K. Louden, Programming Languages

206 K. Louden, Programming Languages
Aliases An alias occurs when the same object is bound to two different names at the same time. This is fairly common with Java objects. Side Effect: changes in value that persists after execution. Many side effects are intentional x=5 intent was to change x Obj.setT(2) – intent was to set value of t Swap(a,b) - intent was to change values Sometimes a side effect is not intentional Sqrt(x) - what if it set x to zero? T= what if an aliased variable was also changed? Chapter 5 K. Louden, Programming Languages

207 Dangling References, and Garbage
A dangling reference is a location that has been deallocated from the environment, but is still accessible within the program. Dangling references are impossible in a garbage-collected environment with no direct access to addresses. Garbage is memory that is still allocated in the environment but has become inaccessible to the program. Garbage can be a problem in a non-garbage collected environment, but is much less serious than dangling references. Chapter 5 K. Louden, Programming Languages

208 Chapter 6 Data Types What is a data type? A set of values versus
A set of values + set of operations on those values

209 Why data types? Data abstraction
Programming style – incorrect design decisions show up at translation time Modifiability – enhance readability Type checking (semantic analysis) can be done at compile time - the process a translator goes through to determine whether the type information is consistent Compiler uses type information to allocate space for variables Translation efficiency: Type conversion (coercion) can be done at compile time

210 Overview cont… Declarations Type declarations Type checking
Explicit type information Type declarations Give new names to types in type declararion Type checking Type Inference Rules for determining the types of constructs from available type information Type equivalence determines if two types are the same Type system Type construction methods + type inference rules + type equivalence algorithm

211 Simple Types Predefined types Enumerated types Pascal
type fruit = (apple, orange, banana); C/C++ enum fruit { apple, orange, banana };

212 Simple Types Subrange types Pascal type byte = 0..255; minors = 0..19;
teens = ; ADA subtype teens is INTEGER range ;

213 Data Aggregates and Type Constructors
Aggregate (compound) objects and types are constructed from simple types Recursive – can also construct aggregate objects and types from aggregate types Predefined – records, arrays, strings……

214 Constructors - Cartesian Product
U X V = { (u, v) | u is in U and v is in V} Projection functions p1: U X V -> U and P2: U X V -> V Pascal family of languages - record type polygon = record edgeCt: integer; edgeLen: real end; var a : polygon; Cartesian product type INTEGER X REAL Projections a.edgeCt a.edgeLen

215 Constructors – Mapping (arrays)
The array constructor defines mappings as data aggregates Mapping from an array index to the value stored in that position in the array The domain is the values of the index The range is the values stored in the array

216 Constructors Mapping (arrays)
C/C++ typedef int little_people_num[3]; little_people_num gollum_city = {0, 0, 0} gollum_city[3] = 5 typedef int matrix[10][20];

217 Arrays Design Issues: 1. What types are legal for subscripts?
2. Are subscripting expressions in element references range checked? 3. When are subscript ranges bound? 4. When does allocation take place? 5. What is the maximum number of subscripts? 6. Can array objects be initialized? 7. Are any kind of slices allowed?

218 Arrays Indexing is a mapping from indices to elements
map(array_name, index_value_list)  an element

219 Arrays Subscript Types: FORTRAN, C - integer only
Pascal - any ordinal type (integer, boolean, char, enum) Ada - integer or enum (includes boolean and char) Java - integer types only

220 Arrays Categories of arrays (based on subscript binding and binding to storage) 1. Static - range of subscripts and storage bindings are static e.g. FORTRAN 77, some arrays in Ada Advantage: execution efficiency (no allocation or deallocation)

221 Arrays 2. Fixed stack dynamic - range of subscripts is statically bound, but storage is bound at elaboration time (at function call time) e.g. Most Java locals, and C locals that are not static Advantage: space efficiency

222 Arrays 3. Stack-dynamic - range and storage are dynamic (decided at run time), but fixed after initial creation on for the variable’s lifetime e.g. Ada declare blocks declare STUFF : array (1..N) of FLOAT; begin ... end; Advantage: flexibility - size need not be known until the array is about to be used

223 Arrays 4. Heap-dynamic – stored on heap and sizes decided at run time.
e.g. (FORTRAN 90) INTEGER, ALLOCATABLE, ARRAY (:,:) :: MAT (Declares MAT to be a dynamic 2-dim array) ALLOCATE (MAT (10,NUMBER_OF_COLS)) (Allocates MAT to have 10 rows and NUMBER_OF_COLS columns) DEALLOCATE MAT (Deallocates MAT’s storage)

224 Arrays 4. Heap-dynamic (continued)
Truly dynamic: In APL, Perl, and JavaScript, arrays grow and shrink as needed Fixed dynamic: Java, once you declare the size, it doesn’t change

225 Arrays Number of subscripts Array Initialization
FORTRAN I allowed up to three FORTRAN 77 allows up to seven Others - no limit Array Initialization Usually just a list of values that are put in the array in the order in which the array elements are stored in memory

226 Arrays Array Operations 1. APL - many, see book (p. 240-241) 2. Ada
Assignment; RHS can be an aggregate constant or an array name Catenation; for all single-dimensioned arrays Relational operators (= and /= only) 3. FORTRAN 90 Intrinsics (subprograms) for a wide variety of array operations (e.g., matrix multiplication, vector dot product)

227 Arrays Slices A slice is some substructure of an array; nothing more than a referencing mechanism Slices are only useful in languages that have array operations

228 Arrays Slice Examples: INTEGER MAT (1:4, 1:4)
1. FORTRAN 90 INTEGER MAT (1:4, 1:4) MAT(1:4, 1) - the first column MAT(2, 1:4) - the second row

229 Example Slices in FORTRAN 90

230 Arrays Implementation of Arrays
Access function maps subscript expressions to an address in the array Row major (by rows) or column major order (by columns)

231 Locating an Element

232 Compile-Time Descriptors
Single-dimensioned array Multi-dimensional array

233 Accessing Formulas – 1D Address(A[i]) = BegAdd + (i-lb)*size
+ VirtualOrigin +i*size lb: lower bound size: number of bytes of one element Virtual origin allows us to do math once, so don’t have to repeat each time. You must check for valid subscript before you use this formula, as obviously, it doesn’t care what subscript you use.

234 Accessing Formulas Multiple Dimensions
ubi: upper bound in ith dimension lbi: lower bound in ith dimension lengthi = ubi –lbi +1 In row-major order Address(A[i,j]) = begAdd + size((i-lbi)*lengthj + j-lbj) = VO + i*multi + j*multj

235 VO 40 lbi ubi 6 multi 28 lbj 3 ubj 7 multj 20
Address(A[i,j]) = begAdd + size((i-lbi)*lengthj + j-lbj) = VO + i*multi + j*multj = i+20j For Example: array of floats A[0..6, 3..7] beginning at location 100 begAdd = 100 size = 4 (if floats take 4 bytes) lbi = 0 ubi = 6 lengthi = 7 lbj = 3 ubj = 7 lengthj = 5 VO = *(-3)*5 = 40 multi = 28 multj = 20 VO 40 lbi ubi 6 multi 28 lbj 3 ubj 7 multj 20 repeated for each dimension

236 Accessing Formulas Multiple Dimensions
In column-major order Address(A[i,j]) = begAdd + size((i-lbi) + (j-lbj)*lengthi) In 3D in row major: Addr(A[I,j,k]) = begAdd + size*((i-lbi)*lengthj*lengthk) + (j-lbj)lengthk + k-lbk)

237 Accessing Formulas Slices
Suppose we want only the second row of our previous example. We would need array descriptor to look like a normal 1D array (as when you pass the slice, the receiving function can’t be expected to treat it any differently)

238 Address(A[i,j]) = begAdd + size((i-lbi)*lengthj + j-lbj)
= VO + i*multi + j*multj = i+20j For Example: array of floats A[0..6, 3..7] beginning at location 100 If we want only the second row, it is like we have hardcoded the j=2 in the accessing formula, A[0..6,2] The accessing formula is simple Just replace j with 2, adjust the VO, and remove the ub,lb, and length associated with j so i+20j = 80+28i and the table is changed accordingly (and looks just like the descriptor for a regular 1D array) VO 80 lbi ubi 6 multi 28

239 Constructors Union Cartesian products – conjunction of fields
Union – disjunction of fields Discriminated or undiscriminated

240 Pascal Variant Record – discriminated union
type address_range = 0..maxint; address_type = (absolute, offset); safe_address = record case kind: address_type of absolute: (abs_addr:address_range); offset: (off_addr: integer); end

241 References A reference is the address of an object under the control of the system – which cannot be used as a value or operated on C++ is perhaps the only language where pointers and references exist together. References in C++ are constant pointers that are dereferenced everytime they are used.

242 Constructors Pointer and Recursive Types
Some languages (Pascal, Ada) require pointers to be typed PL/1 treated pointers as untyped data objects What is the significance of this for a type checker? C pointers are typed but C allows arithmetic operations on them unlike Pascal and Ada

243 Type Equivalence When are two types the same Structural equivalence
Declaration equivalence Name equivalence

244 Structural Equivalence
Two types are the same if they have the same structure i.e. they are constructed in exactly the same way using the same type constructors from the same simple types May look alike even when we wanted them to be treated as different.

245 Structural Type Equivalence
(Note we are just using the syntax of C as an example. C does NOT use structural equivalence for structs typedef int anarray[10]; typedef struct { anarray x; int y;} struct1; typedef struct { int x[10]; int y; }struct2; typedef int anarray[10]; typedef struct { int b; anarray a; }struct4; typedef int anarray[10]; typedef struct { anarray a; int b; }struct3;

246 Structural Equivalence
Check representing types as trees Check equivalence recursively on subtrees Consider… Dynamic arrays Type array1 = array[-1..9] of integer; array2 = array[0..10] of integer; Array (INTEGER range <>) of INTEGER

247 Name Equivalence Two name types are equivalent only if they have the exact same type name Name equivalence in Ada and C ar1 and ar2 are not considered name equivalent typedef int ar1[10]; typedef ar1 ar2; typedef int age; type ar1 is array (INTEGER range1..10) of INTEGER; type ar2 is new ar1; type age is new INTEGER;

248 Name equivalence… v1: ar1; v2: ar1; v3: ar2;
v4: array (INTEGER range ) of INTEGER; v5: array (INTEGER range ) of INTEGER; v4 and v4 cannot be name equivalent, as there is no name. v6,v7: array (INTEGER range ) of INTEGER;

249 Declaration Equivalent
Lead back to the same original structure declaration via a series of redeclarations type t1 = array [1..10] of integer; t2 = t1; t3 = t2; These are the same type type t4 = array [1..10] of integer; t5 = array [1..10] of integer; These are different types.

250 Type Checking Involves the application of a type equivalence algorithm to expressions and statements to determine if they make sense Any attempt to manipulate a data object with an illegal operation is a type error Program is said to be type safe (or type secure) if guaranteed to have no type errors Static versus dynamic type checking Run time errors

251 Type Checking… All variables are declared with an associated type
Strong typing and type checking Strong guarantees type safety A language is strongly typed if its type system guarantees statically (as far as possible) that no data-corrupting errors can occur during execution. Statically typed versus dynamically typed Static (type of every program expression be known at compile time) All variables are declared with an associated type All operations are specified by stating the types of the required operands and the type of the result Java is strongly typed Python is dynamic typed and strong typed

252 Strongly typed /* Python code */ >>> foo = "x" >>> foo = foo + 2 Traceback (most recent call last):  File "<pyshell#3>", line 1, in ?    foo = foo + 2 TypeError: cannot concatenate 'str' and 'int' objects >>> foo is of str type. In the second line, we're attempting to add 2 to a variable of str type. A TypeError is returned, indicating that a str object cannot be concatenated with an int object. This is what characterizes strong typed languages: variables are bound to a particular data type.

253 Weakly Typed /* PHP code */ <?php $foo = "x"; $foo = $foo + 2; // not an error echo $foo; ?> In this example, foo is initially a string type. In the second line, we add this string variable to 2, an integer. This is permitted in PHP, and is characteristic of all weak typed languages. C is static typed and weak typed as we can cast a variable to be a different type (which should not be allowed if it isn’t of that type)

254 Type Conversion r is float and j is integer r = j + 45.6
i is integer and j is integer i = j

255 Type Conversion… Modula2 i := TRUNC (FLOAT(j) + 45.6)
Explicit type conversion type conversion functions Implicit conversion coercion can weaken type checking – as lots of coercions are allowed. For example, if (a=b) coerces int to boolean.

256 Type conversion… Casts
A value or object of one type is preceded by a type name (int) 3.14 Often does not cause a conversion to take place. Internal representation is reinterpreted as a new type CARDINAL(-1) 65535 This is very dangerous!

257 Type Conversions Def: A mixed-mode expression is one that has operands of different types Def: A coercion is an implicit type conversion The disadvantage of coercions: They decrease in the type error detection ability of the compiler In most languages, all numeric types are coerced in expressions, using widening conversions In Ada, there are virtually no coercions in expressions

258 Chapter 7 - Control I: Expressions and Statements
9/12/2018 Chapter 7 - Control I: Expressions and Statements "Control" is the general study of the semantics of execution paths through code: what gets executed, when, and in what order. Chapter 7 Louden, Programming Languages

259 Louden, Programming Languages
Expressions In their purest form, expressions do not involve control issues: subexpressions can be evaluated in arbitrary order, and the order does not affect the result. Functional programming tries to achieve this goal for whole programs. If expressions could have arbitrary evaluation order, programs would become non-deterministic: any of a number of different outcomes might be possible. Chapter 7 Louden, Programming Languages

260 Louden, Programming Languages
Side Effects A side effect is any observable change to memory, input or output. A program without any side effect is useless. Side effects expose evaluation order: class Order { static int x = 1; public static int getX() { return x++; } public static void main(String[] args) { System.out.println( x+getX() ); } } This prints 2, but the corresponding C program will usually print 3! Why? Chapter 7 Louden, Programming Languages

261 Louden, Programming Languages
Strictness An evaluation order for expressions is strict (eager) if all subexpressions of an expression are evaluated, whether or not they are needed to determine the value of the result, non-strict (lazy) otherwise. Arithmetic is almost always strict. Every language has at least a few non-strict expressions (?:, &&, || in Java). Some languages use a form of non-strictness called normal-order evaluation: no expression is ever evaluated until it is needed (Haskell). Also called delayed evaluation. A form of strict evaluation called applicative-order is more common: "bottom-up" or "inside-out". Still leaves open whether left-to-right or not. Chapter 7 Louden, Programming Languages

262 Short Circuit Evaluation
Suppose Java did not use short-circuit evaluation Problem: table look-up index = 1; while (index <= length) && (LIST[index] != value) index++; Chapter 7 Louden, Programming Languages

263 Short Circuit Evaluation
C, C++, and Java: use short-circuit evaluation for the usual Boolean operators (&& and ||), but also provide bitwise Boolean operators that are not short circuit (& and |) Ada: programmer can specify either (short-circuit is specified with and then and or else) FORTRAN 77: short circuit, but any side-affected place must be set to undefined Short-circuit evaluation exposes the potential problem of side effects in expressions e.g. (a > b) || (b++ / 3) Chapter 7 Louden, Programming Languages

264 Louden, Programming Languages
Function calls Obey evaluation rules like other expressions. Applicative order: evaluate all arguments (left to right?), then call the procedure. Normal order: pass in unevaluated representations of the arguments. Only evaluate when needed. With side effects, order makes a difference. Chapter 7 Louden, Programming Languages

265 Louden, Programming Languages
Case Statements Rules cases can be constants, constant expressions, constant ranges no overlapping cases error if unspecified case occurs (or may decide to do nothing if unspecfied case) Implemented via jump table: vector stored sequentially in memory - each of whose components is an unconditional jump Need one location in jump table for every value between range of possible values Chapter 7 Louden, Programming Languages

266 Louden, Programming Languages
Switch Statements typedef enum {ADD, MULT, MINUS, DIV, MOD, BAD} op_type; char unparse_symbol(op_type op) { switch (op) { case ADD : return '+'; case MULT: return '*'; case MINUS: return '-'; case DIV: return '/'; case MOD: return '%'; case BAD: return '?'; } Implementation Options Series of conditionals Good if few cases Slow if many Jump Table Lookup branch target Avoids conditionals Possible when cases are small integer constants GCC Picks one based on case structure Bug in example code No default given Chapter 7 Louden, Programming Languages

267 Louden, Programming Languages
Jump Table Structure Jump Targets Switch Form Jump Table Code Block Targ0: 1 Targ1: 2 Targ2: n–1 Targn-1: switch(op) { case 0: Block 0 case 1: Block 1 • • • case n-1: Block n–1 } Targ0 Targ1 Targ2 Targn-1 jtab: Approx. Translation target = JTab[op]; goto *target; Chapter 7 Louden, Programming Languages

268 Louden, Programming Languages
A jump table is an array of code addresses: Tbl[ i ] is the address of the code to execute if the expression evaluates to i. if the set of case labels have “holes”, the correspond jump table entries point to the default case. Bounds checks: Before indexing into a jump table, we must check that the expression value is within the proper bounds (if not, jump to the default case). The check lower_bound  exp_value  upper bound can be implemented using a single unsigned comparison. Chapter 7 Louden, Programming Languages

269 Louden, Programming Languages
Jump Table Space Costs jump tables best for large no. of case labels ( 8) may take a large amount of space if the labels are not well-clustered. A jump table with max. and min. case labels cmax and cmin needs  cmax – cmin entries. This can be wasteful if the entries aren’t “dense enough”, e.g.: switch (x) { case 1: … case 1000: … case : … } Define the density of a set of case labels as density = number of case labels/(cmax – cmin ) Compilers will not generate a jump table if density below some threshold (typically, 0.5). Chapter 7 Louden, Programming Languages

270 Use of Switch Statements
if no. of case labels is small ( ~ 8), use linear or binary search. use no. of case labels to decide between the two. if density  threshold (~ 0.5) : generate a jump table; else : divide the set of case labels into sub-ranges s.t. each sub-range has density  threshold; generate code to use binary search to choose amongst the sub-ranges; handle each sub-range recursively. Chapter 7 Louden, Programming Languages

271 Louden, Programming Languages
Guarded Commands Selection: if <boolean> -> <statement> []<boolean> -> <statement> ... [] <boolean> -> <statement> fi Semantics: when this construct is reached, Evaluate all boolean expressions If more than one are true, choose one nondeterministically If none are true, it is a runtime error Chapter 7 Louden, Programming Languages

272 Louden, Programming Languages
Guarded Commands Idea: if the order of evaluation is not important, the program should not specify one In Haskell, first one that matches is used. Chapter 7 Louden, Programming Languages

273 Louden, Programming Languages
Guarded Commands Loops do <boolean> -> <statement> [] <boolean> -> <statement> ... od Chapter 7 Louden, Programming Languages

274 Louden, Programming Languages
Guarded Commands Semantics: For each iteration: Evaluate all boolean expressions If more than one are true, choose one nondeterministically; then start loop again If none are true, exit loop Chapter 7 Louden, Programming Languages

275 Louden, Programming Languages
Summary Every language has three major program components: expressions, statements, and declarations. Expressions are executed for their values (but may have side effects), and may or may not be sequenced. Statements are executed solely for their side effects, and they must be sequenced. Declarations define names; they can also give values to those names. They may or may not be viewed by a language as expressions or statements. Chapter 7 Louden, Programming Languages

276 Chapter 8 - Control II: Procedures and Environments
9/12/2018 Chapter 8 - Control II: Procedures and Environments Louden, 2003

277 Three major parts of a runtime environment:
Static area allocated at load/startup time. Examples: global/static variables and load-time constants. Stack area for execution-time data that obeys a last-in first-out lifetime rule. Examples: nested declarations and temporaries. Heap or dynamically allocated area for "fully dynamic" data, i.e. data that does not obey a LIFO rule. Chapter 8 K. Louden, Programming Languages

278 K. Louden, Programming Languages
Procedure Overview When functions are “first class” data items themselves, they can be dynamically created and used like values just like any other data structure. Thus, we need to be able to pass functions as arguments A procedure is called or activated. Activation record: collection of data needed to maintain a single execution of a procedure. Worry about local and non-local references. Static or dynamic environment (depending on scoping) must be accessible. When a procedure depends only of parameters and fixed language features – closed form. The code for a function together with its defining environment is called closure – as we can resolve all outstanding non-local environments. Chapter 8 K. Louden, Programming Languages

279 Implementing “Simple” Subprograms
Call Semantics: 1. Save the execution status of the caller (calling environment) 2. Carry out the parameter-passing process by putting the parameters somewhere that the called function can access. 3. Pass the return address to the callee 4. Transfer control to the callee Chapter 8 K. Louden, Programming Languages

280 Implementing “Simple” Subprograms
Return Semantics: 1. If it is a function, move the functional value to a place the caller can get it 2. Restore the execution status of the caller 3. Transfer control back to the caller Chapter 8 K. Louden, Programming Languages

281 Implementing “Simple” Subprograms
Required Storage: Status information of the caller, parameters, return address, and functional value (if it is a function) The format, or layout, of the noncode part of an executing subprogram is called an activation record An activation record instance is a concrete example of an activation record (the collection of data for a particular subprogram activation) Chapter 8 K. Louden, Programming Languages

282 An Activation Record for “Simple” Subprograms (no scoping issues)
Chapter 8 K. Louden, Programming Languages

283 K. Louden, Programming Languages
Code and Activation Records of a Program with “Simple” Subprograms If there is no recursion, we can have static activation records. If we have no non-local variables, everything we need is local and easy to find. Chapter 8 K. Louden, Programming Languages

284 K. Louden, Programming Languages
Parameter Passing Aliases may be created Type checking parameters a formal reference parameter is a nonlocal variable the same data object passed for two parameters CALL S(X,X) With aliasing, interesting problems in optimizations occur. Chapter 8 K. Louden, Programming Languages

285 Models of Parameter Passing
Chapter 8 K. Louden, Programming Languages

286 K. Louden, Programming Languages
1. Pass-by-value (in mode) Typically we copy the value in, but can do with a constant reference pointer. Parameters are viewed as local variables of the procedure (with initial values given by values of arguments in the call) Note can still change values outside procedure if we pass a reference. Disadvantages of copy: Requires more storage (duplicated space) Cost of the moves (if the parameter is large) Disadvantages of constant reference: Must write-protect in the called subprogram or compiler check that there are no assignments. Accesses cost more (indirect addressing) Chapter 8 K. Louden, Programming Languages

287 K. Louden, Programming Languages
2. Pass-by-result (out mode) function return value Local’s value is passed back to the caller Physical move is usually used (copy to call stack) Disadvantages: If value is passed, time and space order dependence may be a problem If return value is an address, when is it evaluated? e.g. procedure sub1(y: int, z: int);{y=0;z=5; } sub1(x, x); Value of x in the caller depends on order of assignments at the return Chapter 8 K. Louden, Programming Languages

288 K. Louden, Programming Languages
OUT only parameters formal parameter is a local variable with no initial value copied back at termination of subprogram Pass by result Explicit function Values: may be considered an extra OUT parameter return(expr) value to be returned by assignment to function name if return is an address (e.g., list[index]), when is it evaluated? time of call? time of return? Chapter 8 K. Louden, Programming Languages

289 K. Louden, Programming Languages
3. Inout mode Pass by value-result (aka copy-in copy-out or copy-restore) Physical move, both ways value-result (or pass by copy) Disadvantages ordering may be a problem with a call like doit(x,x) time/space issues Need to know whether address is computed again before copying back. doit(i,a[i]) Chapter 8 K. Louden, Programming Languages

290 K. Louden, Programming Languages
IN OUT parameters Value-restore. Copy value in on call Copy changed value back on return. Used to save cost of indirect access. aliases may be problematic - especially likely if pass same parameter twice. Then if arguments are changed, original values may be changed differently depending on order of change (if value-restore) Chapter 8 K. Louden, Programming Languages

291 K. Louden, Programming Languages
4. In-out mode - pass by reference. Issues: access is slower (as indirect) passing is faster called program can access parameter through alias Array element collisions: e.g. sub1(a[i], a[j]); /* if i = j */ Also, sub2(a, a[i]); (a different one) Collision between formals and globals Root cause of all of these is: The called subprogram is provided wider access to nonlocals than is necessary Chapter 8 K. Louden, Programming Languages

292 K. Louden, Programming Languages
IN OUT parameters transmission by reference formal parameter is local object of type pointer If expression is passed as an in/out parameter: a temporary location may be passed (and then the copy is changed, not the original) Disadvantages: access slower as is indirect (always follow a pointer to access), but passing is fast (only copy a pointer, not a whole structure) may make inadvertent changes to parameters (if out only was desired) Chapter 8 K. Louden, Programming Languages

293 Parameter Passing Methods
5. Pass-by-name (Unevaluated parameters) By textual substitution intended to be an advanced inlining essentially like late evaluation Name of t he argument (or its textual substitution at the point of call) replaces the name of the parameter it corresponds to. Formals are bound to an access method at the time of the call, but actual binding to a value or address takes place at the time of a reference or assignment Purpose: flexibility of late binding Chapter 8 K. Louden, Programming Languages

294 K. Louden, Programming Languages
Pass-by-name Resulting semantics: If actual is a scalar variable, it is pass-by-reference If actual is a constant expression, it is pass-by-value If actual is an array element, it is like nothing else e.g. procedure sub1(x: int; y: int); begin x := 1; Seems like nothing is happening y := 2; with first assignments but it is x := 2; y := 3; end; sub1(i, a[i]); Chapter 8 K. Louden, Programming Languages

295 K. Louden, Programming Languages
5. Pass-by-name (continued) If actual is an expression with a reference to a variable that is also accessible in the program, it is also like nothing else e.g. (assume k is a global variable) procedure sub1(x: int; y: int; z: int); begin k := 1; y := x;k := 5;z := x; end; sub1(k+1, j, a[i]); Thunks: pass by name arguments are implemented by little procedure which evaluate the arguments. Presumably the image was of little machines that “thunked” into place each time they were needed. Chapter 8 K. Louden, Programming Languages

296 Parameter Passing (In Mode)
Pass-by-name: text for argument is passed to subprogram and expanded in each place parameter is used Roughly same as using macros Note, you can’t evaluate late without having “code” to execute You also need to know a context for evaluating non-local variables Achieves late binding Chapter 8 K. Louden, Programming Languages

297 K. Louden, Programming Languages
Pass-by-name Example integer INDEX= 1; integer array ARRAY[1:2] procedure UPDATE (PARAM); integer PARAM begin PARAM := 3; INDEX := INDEX + 1; PARAM := 5; end UPDATE(ARRAY[INDEX]); Chapter 8 K. Louden, Programming Languages

298 K. Louden, Programming Languages
Pass-by-name Example Previous code puts 3 in ARRAY[1] and 5 in ARRAY[2] How is this accomplished if the compiled code must work for ANY argument that is passed? PARAM must be something that has a value, but can be x, Array[x+y], Array[2*t[6*x]+7] How can you generate code for UPDATE when you don’t know what is passed? If pass by name argument appears on left hand side, need to be able to compute the address. If pass by name argument appears on right hand side (of assignment), need to be able to compute a value. Chapter 8 K. Louden, Programming Languages

299 K. Louden, Programming Languages
New interest in functional languages means more interest in delayed evaluation. Very flexible, but inefficient. Difficult to implement. Confusing to read/write. Some simple operations are not possible with pass by name. Lazy evaluation is another form of late binding. Only evaluate when it becomes necessary. Substitute name or expression (in calling environment) for formal parameter The name location binding is delayed until (and established fresh each time) the formal parameter is encountered. Implemented by passing parameter-less subprograms (thunks) rather than variable name. An expression needs to be evaluated IN the proper environment. Don't have mechanism to do that other than thru procedure call. Whenever formal parameter is referenced, a call is made to thunk, which evaluates the parameter in the proper (caller) environment and returns proper resulting value (or location) Chapter 8 K. Louden, Programming Languages

300 K. Louden, Programming Languages
Example: procedure R(var i,j: integer); begin var m:boolean; m := true; i := i + 1; j := j + 1; write(i,j); end; m := 2; for(i=0;i<10;i++) c[i]=10*i; R(m,c[m]); pass by reference: adds 1 to m and c[2] Pass by name: adds 1 to m and c[3] Chapter 8 K. Louden, Programming Languages

301 Example for Pass by Name
b1: begin real x,y; y := 0.0; procedure G(t): name t; begin integer w; integer x; w := 10; y := 20; x := 50 print t x:= 0; print t end G; b2: begin real y; y := 0.5; x := 1.0; call G(y-x) end end thunk() return(y-x) end; Chapter 8 K. Louden, Programming Languages

302 Parameter Passing Methods
Disadvantages of pass by name: Very inefficient references Too tricky; hard to read and understand Chapter 8 K. Louden, Programming Languages

303 Parameter Passing Methods
Multidimensional Arrays as Parameters If a multidimensional array is passed to a subprogram and the subprogram is separately compiled, the compiler needs to know the declared size of that array to build the storage mapping function Programmer is required to include the declared sizes of all but the first subscript in the actual parameter This disallows writing flexible subprograms Solution: run time descriptor Chapter 8 K. Louden, Programming Languages

304 Parameter Passing Methods
Design Considerations for Parameter Passing 1. Efficiency 2. One-way or two-way These two are in conflict with one another! Good programming => limited access to variables, which means one-way whenever possible Efficiency => pass by reference is fastest way to pass structures of significant size Also, functions should not allow reference parameters Chapter 8 K. Louden, Programming Languages

305 Languages and Environments
Languages differ on where activation records must go in the environment: Fortran is static: all data, including activation records, are statically allocated. (Each function has only one activation record—no recursion!) Functional languages (Scheme,ML) and some OO languages (Smalltalk) are heap-oriented: all (or almost all) data, including activation records, are allocated dynamically. Most languages are in between: data can go anywhere (depending on its properties); activation records go on the stack. Chapter 8 K. Louden, Programming Languages

306 Simple stack-based allocation
Described in Chapter 5. Nested declarations are added to the stack as their code blocks are entered, and removed as their code blocks are exited. Example: Stack at Point 1: { int x; int y; { int z; } { int w; // Point 1 } } Note ,z has been removed at point 1 as have exited scope x y w Chapter 8 K. Louden, Programming Languages

307 K. Louden, Programming Languages
Example (C): main →q →p int x; void p( int y) { int i = x; char c; ... } void q ( int a) { int x; p(1); main() { q(2); return 0; Chapter 8 K. Louden, Programming Languages

308 Activation record of p:
© 2003 Brooks/Cole - Thomson Learning™ Chapter 8 K. Louden, Programming Languages

309 K. Louden, Programming Languages
Environment stack during exec of p: main →q →p (stack is shown growing down) © 2003 Brooks/Cole - Thomson Learning™ Note: the ep in this picture actually points to the "bottom" of the frame, as do the control links (which are stored old ep values), so ep  top of stack. Chapter 8 K. Louden, Programming Languages

310 Local variable access using the ep
In a typical language with a stack-based runtime environment, the local declarations in a procedure are fixed at compile-time, both in size and in sequence. This information can be used to speed up accesses to local variables, by precomputing these locations as offsets from the ep. Then the local frame need not have a name-based lookup operation (unlike the symbol table). In fact, names can be dispensed with altogether. The next slide shows how that would look for the procedure p of slide 7. Chapter 8 K. Louden, Programming Languages

311 Non-local variable access
Requires that the environment be able to identify frames representing enclosing scopes. Using the control link results in dynamic scope (and also kills the fixed-offset property as you are not sure which method will contain the x. Thus, you can’t depend on a fixed location). If procedures can't be nested (C, C++, Java), the enclosing scope is always locatable by other means: it is either global (accessed directly) or belongs to the current object. If procedures can be nested, to maintain lexical scope a new link must be added to each frame: the access link, pointing to the activation of the defining environment of each procedure. Chapter 8 K. Louden, Programming Languages

312 Example (Ada-like) q→r → p → z → z:
1 procedure q is x,u: integer procedure p (y: integer) is i: integer := x; procedure z (t:float) returns float is m: float; begin if (t >5) return z(t/2); return t+m/i*y + u/x; end z; begin ... end p; procedure r (u:integer) is x: float; p(1);... end r; r; end q; Chapter 8 K. Louden, Programming Languages

313 Environment during exec of p:
© 2003 Brooks/Cole - Thomson Learning™ Chapter 8 K. Louden, Programming Languages

314 K. Louden, Programming Languages
Nested Subprograms The process of locating a nonlocal reference: 1. Find the correct activation record instance 2. Determine the correct offset within that activation record instance May need to follow several links (access chaining) The number of links is known from compile time. If used stack of symbol tables, can count how many tables you had to search to find it. If used individual stacks for each value, you can record the nesting depth of each variable. Chapter 8 K. Louden, Programming Languages

315 Procedure values as pointer pairs
With nested procedures in lexically scoped languages requiring an access link, when a procedure is called, this access link must be available at the point of call. One way it can be is for the procedure itself to record its access link (necessary if procedures can be parameters). Then each procedure becomes a pair of pointers: a code pointer (called the instruction pointer or ip in the text), and an environment pointer (ep in the text) pointing to the definition environment of the procedure (which will become the access link during a call). Such an <ep,ip> pair is sometimes called a closure. Chapter 8 K. Louden, Programming Languages

316 Fully dynamic environments
Languages with lambdas or where functions can be created locally and returned from a call (ML, Scheme). Activation records cannot in general be popped after a call. Thus, activations no longer behave as LIFO structures and must be allocated in the heap. Control links make little sense, since each control link might have to point far back in the stack. Access links and closures still make perfect sense, however. Chapter 8 K. Louden, Programming Languages

317 Implementing Subprograms in ALGOL-like Languages
The collection of dynamic links in the stack at a given time is called the dynamic chain, or call chain Local variables can be accessed by their offset from the beginning of the activation record. This offset is called the local_offset The local_offset of a local variable can be determined by the compiler Assuming all stack positions are the same size, the first local variable declared has an offset of three plus the number of parameters, e.g., in main, the local_offset of Y in A is 3 Chapter 8 K. Louden, Programming Languages

318 The Process of Locating a Nonlocal Reference
Finding the offset is easy Finding the correct activation record instance: Static semantic rules guarantee that all nonlocal variables that can be referenced have been allocated in some activation record instance that is on the stack when the reference is made Chapter 8 K. Louden, Programming Languages

319 K. Louden, Programming Languages
Nested Subprograms Technique 1 - Static Chains A static chain is a chain of static links that connects certain activation record instances The static link in an activation record instance for subprogram A points to one of the activation record instances of A's static parent The static chain from an activation record instance connects it to all of its static ancestors Chapter 8 K. Louden, Programming Languages

320 Static Chains (continued)
To find the declaration for a reference to a nonlocal variable: You could chase the static chain until the activation record instance (ari) that has the variable is found, searching each ari as it is found, if variable names were stored in the ari Def: static_depth is an integer associated with a static scope whose value is the depth of nesting of that scope Chapter 8 K. Louden, Programming Languages

321 Static Chains Show the static/dynamic chains when main →C →A →B →C
main static_depth = 0 A static_depth = 1 B static_depth = 2 C static_depth = 1 Chapter 8 K. Louden, Programming Languages

322 Static Chains (continued)
Def: The chain_offset or nesting_depth of a nonlocal reference is the difference between the static_depth of the reference and that of the scope where it is declared A reference can be represented by the pair: (chain_offset, local_offset) where local_offset is the offset in the activation record of the variable being referenced Chapter 8 K. Louden, Programming Languages

323 K. Louden, Programming Languages
Nested Subprograms Static Chain Maintenance At the call : The activation record instance must be built The dynamic link is just the old stack top pointer The static link must point to the most recent ari of the static parent (in most situations) Two Methods to set static chain: 1. Search the dynamic chain until the first ari for the static parent is found--easy, but slow Chapter 8 K. Louden, Programming Languages

324 K. Louden, Programming Languages
Nested Subprograms 2. Treat procedure calls and definitions like variable references and definitions (have the compiler compute the nesting depth, or number of enclosing scopes between the caller and the procedure that declared the called procedure; store this nesting depth and send it with the call) e.g. Look at MAIN_2 and the stack contents. At the call to SUB1 in SUB3, this nesting depth is 1, which is sent to SUB1 with the call. The static link in the new ari for SUB1 is set to point to the ari that is pointed to by the second static link in the static chain from the ari for SUB3 Chapter 8 K. Louden, Programming Languages

325 K. Louden, Programming Languages
Nested Subprograms Evaluation of the Static Chain Method Problems: 1. A nonlocal reference is slow if the number of scopes between the reference and the declaration of the referenced variable is large 2. Time-critical code is difficult, because the costs of nonlocal references are not equal, and can change with code upgrades and fixes Chapter 8 K. Louden, Programming Languages

326 K. Louden, Programming Languages
Nested Subprograms Technique 2 (for locating non-local variables) - Displays The idea: Put the static links in a separate stack called a display. The entries in the display are pointers to the ari's that have the variables in the referencing environment. Represent references as (display_offset, local_offset) where display_offset is the same as chain_offset Can access via computation. display offset of 10 is one lookup (not a chain of length 10) Chapter 8 K. Louden, Programming Languages

327 K. Louden, Programming Languages
Main – level 1 p level 1 p p level 2 t level 2 q level 4 s level 3 r level 3 Chapter 8 K. Louden, Programming Languages

328 K. Louden, Programming Languages
Display contains pointers to each activation record at each reachable level 100 main 100 Level 1 200 Level 2 300 Level 3 400 Level 4 200 t 300 s 400 main-> t -> s-> q q When s calls q, a single element is added to the table. Chapter 8 K. Louden, Programming Languages

329 K. Louden, Programming Languages
100 main 100 Level 1 500 Level 2 300 Level 3 400 Level 4 200 t 300 s 400 main-> t -> s-> q-> p q old level 2 is 200 When q calls p, a new level 2 entry is needed. Store the old one, so you can get it back. Level 3 and level 4 are unused (but unchanged) 500 p Chapter 8 K. Louden, Programming Languages

330 K. Louden, Programming Languages
100 main 100 Level 1 600 Level 2 300 Level 3 400 Level 4 200 t 300 s q 400 main-> t -> s-> q-> p->t old level 2 is 200 p When p calls t, a new level 2 entry is needed Level 3 and level 4 are unused (but unchanged) 500 old level 2 is 500 600 t Chapter 8 K. Louden, Programming Languages

331 K. Louden, Programming Languages
Displays (continued) Mechanics of references: Use the display_offset to get the pointer into the display to the ari with the variable Use the local_offset to get to the variable within the ari Chapter 8 K. Louden, Programming Languages

332 K. Louden, Programming Languages
Displays (continued) Display maintenance (assuming no parameters that are subprograms and no pass-by-name parameters) Note that display_offset depends only on the static depth of the procedure whose ari is being built. It is exactly the static_depth of the procedure. There are k+1 entries in the display, where k is the static depth of the currently executing unit (k=0 is for the main program) Chapter 8 K. Louden, Programming Languages

333 K. Louden, Programming Languages
Displays (continued) For a call to procedure P with a static_depth of k: a. Save, in the new ari, a copy of the display pointer at position k (you will need a stack) b. Put the link to the ari for P at position k in the display At an exit, move the saved display pointer from the ari back into the display at position k Chapter 8 K. Louden, Programming Languages

334 K. Louden, Programming Languages
Displays (continued) To see that this works: Let Psd be the static_depth of P, and Qsd be the static_depth of Q Assume Q calls P There are three possible cases: 1. Qsd = Psd 2. Qsd < Psd (would only differ by one) 3. Qsd > Psd (ignore higher levels) Show example where each of these could happen. Chapter 8 K. Louden, Programming Languages

335 K. Louden, Programming Languages
Blocks Two Methods: 1. Treat blocks as parameterless subprograms Use activation records 2. Allocate locals on top of the ari of the subprogram Must use a different method to access locals A little more work for the compiler writer Chapter 8 K. Louden, Programming Languages

336 Implementing Dynamic Scoping
1. Deep Access (search) - nonlocal references are found by searching the activation record instances on the dynamic chain Length of chain cannot be statically determined Every activation record instance must have variable names recorded Chapter 8 K. Louden, Programming Languages

337 Implementing Dynamic Scoping
2. Shallow Access - put locals in a central place Methods: a. One stack for each variable name b. Central referencing table with an entry for each variable name At subprogram entry, add location for each variable. At subprogram exit, remove location for each variable. Chapter 8 K. Louden, Programming Languages

338 Using Shallow Access to Implement Dynamic Scoping
Chapter 8 K. Louden, Programming Languages

339 Fundamentals of Subprograms
Actual/Formal Parameter Correspondence: 1. Positional (this is what we are used to) 2. Keyword e.g. SORT(LIST => A, LENGTH => N); Advantage: order is irrelevant Disadvantage: user must know the formal parameter’s names Default Values: e.g. procedure SORT(LIST : LIST_TYPE; LENGTH : INTEGER := 100); ... SORT(LIST => A); Chapter 8 K. Louden, Programming Languages

340 Overloaded Subprograms
Def: An overloaded subprogram is one that has the same name as another subprogram in the same referencing environment C++ and Ada have overloaded subprograms built-in, and users can write their own overloaded subprograms Chapter 8 K. Louden, Programming Languages

341 K. Louden, Programming Languages
Generic Subprograms A generic or polymorphic subprogram is one that takes parameters of different types on different activations Overloaded subprograms provide ad hoc polymorphism A subprogram that takes a generic parameter that is used in a type expression that describes the type of the parameters of the subprogram provides parametric polymorphism Chapter 8 K. Louden, Programming Languages

342 Separate & Independent Compilation
Def: Independent compilation is compilation of some of the units of a program separately from the rest of the program, without the benefit of interface information Def: Separate compilation is compilation of some of the units of a program separately from the rest of the program, using interface information to check the correctness of the interface between the two parts. Chapter 8 K. Louden, Programming Languages

343 K. Louden, Programming Languages
Benefit (of system controlled storage management): ability to delay the binding of a storage segment's size and/or location reuse of a storage segment for different jobs (from system supervisor point of view) reuse of storage for different data structures increased generality, not have to specify maximum data structure size dynamic data structures recursive procedures - garbage collection is automatic Benefits of programmer controlled storage management Disadvantage: burden on programmer & may interfere with necessary system control May lead to subtle errors May interfere with system-controlled storage management Advantage: difficult for system to determine when storage may be most effectively allocated and freed Chapter 8 K. Louden, Programming Languages

344 K. Louden, Programming Languages
Heap management Single-size cells vs. variable-size cells Reference counters (eager approach) vs. garbage collection (lazy approach) 1. Reference counters: maintain a counter in every cell that store the number of pointers currently pointing at the cell Disadvantages: space required, complications for cells connected circularly Expensive - when making a pointer assignment p=q decrement count for old value of p if 0, return to free storage. Check if contains references to other blocks. Could be recursive do pointer assignment Increment reference count for q Chapter 8 K. Louden, Programming Languages

345 One-bit reference counting
Another variation on reference counting, called one-bit reference counting, uses a single bit flag to indicate whether each object has either "one" or "many" references. If a reference to an object with "one" reference is removed, then the object can be recycled. If an object has "many" references, then removing references does not change this, and that object will never be recycled. It is possible to store the flag as part of the pointer to the object, so no additional space is required in each object to store the count. One-bit reference counting is effective in practice because most actual objects have a reference count of one. Chapter 8 K. Louden, Programming Languages

346 K. Louden, Programming Languages
2. Garbage collection: allocate until all available cells allocated; then begin gathering all garbage Every heap cell has an extra bit used by collection algorithm All cells initially set to garbage All pointers traced into heap, and reachable cells marked as not garbage All garbage cells returned to list of available cells Disadvantages: when you need it most, it works worst (takes most time when program needs most of cells in heap) Chapter 8 K. Louden, Programming Languages

347 K. Louden, Programming Languages
Mark-Sweep - Java uses In a mark-sweep collection, the collector first examines the program variables; any blocks of memory pointed to are added to a list of blocks to be examined. For each block on that list, it sets a flag (the mark) on the block to show that it is still required, and also that it has been processed. It also adds to the list any blocks pointed to by that block that have not yet been marked. In this way, all blocks that can be reached by the program are marked. In the second phase, the collector sweeps all allocated memory, searching for blocks that have not been marked. If it finds any, it returns them to the allocator for reuse Can find circular references. Easy if regular use of pointers (Like in LISP) All elements must be reachable by a chain of pointers which begins outside the heap Have to be able to know where all pointers are - both inside the heap and outside. How can a chain be followed from a pointer if there is no predefined location for that pointer in the pointed-to cell? Chapter 8 K. Louden, Programming Languages

348 K. Louden, Programming Languages
Conservative garbage collection Although garbage collection was first invented in 1958, many languages have been designed and implemented without the possibility of garbage collection in mind. It is usually difficult to add normal garbage collection to such a system, but there is a technique, known as conservative garbage collection, that can be used. The usual problem with such a language is that it doesn't provide the collector with information about the data types, and the collector cannot therefore determine what is a pointer and what isn't. A conservative collector assumes that anything might be a pointer. It regards any data value that looks like a pointer to or into a block of allocated memory as preventing the recycling of that block. You might think that conservative garbage collection could easily perform quite poorly, leaving a lot of garbage uncollected. In practice, it does quite well, and there are refinements that improve matters further. Because references are ambiguous, some objects may be retained despite being actually unreachable. In practice, this happens rarely, and refinements such as black-listing can further reduce the odds. Chapter 8 K. Louden, Programming Languages

349 K. Louden, Programming Languages
Incremental Garbage Collection Older garbage collection algorithms relied on being able to start collection and continue working until the collection was complete, without interruption. This makes many interactive systems pause during collection, and makes the presence of garbage collection obtrusive. Fortunately, there are modern techniques (known as incremental collection) to allow garbage collection to be performed in a series of small steps while the program is never stopped for long. In this context, the program that uses and modifies the blocks is sometimes known as the mutator. While the collector is trying to determine which blocks of memory are reachable by the mutator, the mutator is busily allocating new blocks, modifying old blocks, and changing the set of blocks it is actually looking at. Chapter 8 K. Louden, Programming Languages

350 K. Louden, Programming Languages
Heap Storage Management - Variable Sized Elements Memory operations Initially one large block Free space list, as space is recovered allocate from free list: first fit, best fit, worst fit compact: maintain a length of each block recover via explicit, garbage collection or reference counts Need length of each to locate pieces and coalesce fragmentation partial compaction (coalescing of adjacent free blocks) full compaction (move blocks) Show how Chapter 8 K. Louden, Programming Languages

351 COMP313A Programming Languages
Object Oriented Progamming Languages (1)

352 Lecture Outline Overview Polymorphism Inheritance and the Type System
Polymorphism and Strong Typing

353 Overview of Object Oriented Programming paradigm
Pure object oriented programming languages unit of modularity is an Abstract Data Type (ADT) implementation, especially the class Can define new classes of objects by modifying existing classes Java, Smalltalk, Eiffel, Dylan, C#

354 Overview Non pure object oriented programming languages
provide support for object oriented programming not exclusively object-oriented C++, Ada 95, CLOS

355 Overview Pure terminology objects are instances of classes
object has instance variables and methods local variables declared as part of the object operations which are used to modify the object message passing smalltalk s push 5 polymorphic – can’t tell which method to invoke until run-time Eiffel, C++, Java restrict polymorphism static type checking

356 public Class Complex { public Complex() { re = 0; im = 0; } public Complex (double realpart, double imagpart) { re = realpart; im = imagpart } public double realpart() { return re; } public double imaginarypart() { return im; } public Complex add ( Complex c ) { return new Complex(re + c.realpart(), im + c.imaginarypart()); public Complex multiply (Complex c) { return new Complex(re * c.realpart() – im * c.imaginary part(), re * c.imaginarypart() + im * c.realpart());

357 Complex z, w; z = new Complex (1, 2); w = new Complex (-1, 1); z = z.add(w); z = z.add(w).multiply(z);

358 Inheritance The subtype principal
a “derived” class inherits all the operations and instance variables of its base class derived class, sub class etc base class, super class, parent class etc Single inheritance versus Multiple inheritance

359 furniture chair table desk lounge chair sofa dining table Relationship amongst the components Use of inheritance Sublasses versus subparts

360 closed figure polygon ellipse triangle rectangle circle square Relationship amongst the components Use of inheritance Sublasses versus subparts

361 Overview Two main goals of object-oriented language paradigm are:
restricting access to the internal (implementation) details of data types and their operations modifiability for reuse

362 The Notion of Software Reuse and Independence
A corollary of Data Abstraction Given a particular Abstract Data Type Extension of data and /or operations (specialisation - subclasses) Redefinition of one or more of the operations Abstraction, or the collection of similar operations from two different components into a new component (multiple inheritance) Extension of the type that operations apply to (polymorphism)

363 public class Queue { // constructors and instance variables go here public void enqueue (int x) {…} public void dequeue() {…} public int front () {…} public boolean empty() {…} } public class Deque extends Queue {// constructors and instance variables go here public void addFront ( int x {… } public void deleteRear() {… }

364 Queue q; Deque d; d = new Deque(); q = d; q.dequeue() and d.queue() OK q.deleteRear() q = d; //a compile time error in Java q = (Deque) d; // downcast

365 class stack{ public: void push(int, item){elements[top++] = item;}; int pop () {return elements[--top];}; private: int elements[100]; int top =0; }; class counting_stack: public stack { int size(); // return number of elements on stack stack s1, s2; // automatic variables stack* sp = new stack; sp->pop()

366 stack* sp = new stack; counting_stack* csp = new counting_stack; sp = csp; // okay csp = sp; // statically can’t tell Why shouldn’t csp be allowed to point to an sp object? C++ strong type system

367 Polymorphism polymorphic variables could refer to objects of different classes what is the problem for a type checker How do we allow dynamic binding and still ensure type safety strong type system limits polymorphism restricted to objects of a class or its derived classes e.g. variables of type stack may refer to a variable of type counting_stack Strict object-oriented languages (Smalltalk, Eiffel, Java all objects accessed through references which may be polymorphic C++ - pointers, reference variables and by-reference parameters are polymorphic

368 If we do not use pointers we do not get inclusion polymorphism. But…
stack s; counting_stack cs; s = cs; //okay coerce cs to a stack cs = s; //not okay

369 COMP313A Programming Languages
Object Oriented Progamming Languages (2)

370 Lecture Outline The Class Hierarchy and Data Abstraction Polymorphism
Polymorphism and Strong Typing

371 furniture chair table desk lounge chair sofa dining table

372 public class Queue { // constructors and instance variables go here public void enqueue (int x) {…} public void dequeue() {…} public int front () {…} public boolean empty() {…} } public class Deque extends Queue {// constructors and instance variables go here public void addFront ( int x {… } public void deleteRear() {… } Is Queue more abstract than Deque or vice versa

373 class stack{ public: void push(int, item){elements[top++] = item;}; int pop () {return elements[--top];}; private: int elements[100]; int top =0; }; class counting_stack: public stack { int size(); // return number of elements on stack stack s1, s2; // automatic variables stack* sp = new stack; sp->pop()

374 stack* sp = new stack; counting_stack* csp = new counting_stack; sp = csp; // okay csp = sp; // statically can’t tell Why shouldn’t csp be allowed to point to an sp object? C++ strong type system

375 Polymorphism polymorphic variables could refer to objects of different classes what is the problem for a type checker How do we allow dynamic binding and still ensure type safety strong type system limits polymorphism restricted to objects of a class or its derived classes e.g. variables of type stack may refer to a variable of type counting_stack Strict object-oriented languages (Smalltalk, Eiffel, Java all objects accessed through references which may be polymorphic C++ - pointers, reference variables and by-reference parameters are polymorphic

376 If we do not use pointers we do not get inclusion polymorphism. But…
stack s; counting_stack cs; s = cs; //okay coerce cs to a stack cs = s; //not okay

377 The Type System The subtype principle a week day is also a day
is-a relationship similarly class and sub-class counting_stack is-a stack but…. type day = (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); weekday = (Monday..Friday);

378 The Type System need to state the conditions under which the isa relationship holds for subclasses of classes because… subclasses can hide the variables and functions or modify them in an incompatible way need to know when they are equivalent with the parent’s definition

379 COMP313A Programming Languages
Object Oriented Progamming Languages (3)

380 Lecture Outline Polymorphism and Strong Typing

381 Polymorphism polymorphic variables could refer to objects of different classes what is the problem for a type checker How do we allow dynamic binding and still ensure type safety strong type system limits polymorphism restricted to objects of a class or its derived classes e.g. variables of type stack may refer to a variable of type counting_stack Strict object-oriented languages (Smalltalk, Eiffel, Java all objects accessed through references which may be polymorphic C++ - pointers, reference variables and by-reference parameters are polymorphic

382 If we do not use pointers we do not get inclusion polymorphism. But…
stack s; counting_stack cs; s = cs; //okay coerce cs to a stack cs = s; //not okay

383 The Type System The subtype principle a week day is also a day
is-a relationship similarly class and sub-class counting_stack is-a stack but…. type day = (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); weekday = (Monday..Friday);

384 The Type System need to state the conditions under which the isa relationship holds for subclasses of classes because… subclasses can hide the variables and functions or modify them in an incompatible way need to know when they are equivalent with the parent’s definition

385 Dynamic Binding of Calls to Member Functions
a derived class can override a member function of the parent class stack* sp = new stack; counting_stack* csp = new counting_stack; sp->push(…); //stack::push csp->push(…); //counting_stack::push sp = csp; //okay sp -> push(…) //which push? dynamic or static binding

386 Type System behavioural equivalence
base::f(x) maybe replaced with derived::f(x) without risking any type errors identical signatures

387 Polymorphism – Strong Typing
Strong typing means that How can we have dynamic binding and a strong type system stack* sp = new stack; counting_stack* csp = new counting_stack; sp = csp; //allowed csp = sp; //not allowed

388 Polymorphism – Strong Typing
the general case: class base {…}; class derived: public base {…}; base* b; derived* d; b = d; //allowed d = b; //not allowed The question of substitutability - ensuring is-a Can we substitute d for b always? Is this kind of polymorphism compatible with strong typing?

389 Polymorphism – Strong Typing
Substitutability impose some restrictions on the use of inheritance Type extension the derived class can only extend the type of base class can’t modify or hide any member variables or functions Ada 95 problem is it rules out dynamic binding (dynamic dispatch) completely

390 Polymorphism – Strong Typing
Overriding of member functions class polygon{ public: polygon (..){…} //constructor virtual float perimeter () {…}; }; class square : public polygon { square (..) {…} //constructor float perimeter() {…}; //overrides the definition of //perimeter in polygon

391 Polymorphism – Strong Typing
under what conditions is it possible for a use of a square object to substitute the use of a polygon object, i.e. p-> perimeter() will be valid whether *p is a polygon or a square object C++ the signature of the overriding function must be identical to that of the overridden function - exactly the same parameter requirements Þ no type violations What happens at runtime?

392 Polymorphism – Strong Typing
Is it possible to relax this last restriction even further but still ensuring type safety? The input parameters of the overriding function must be supertypes of the corresponding parameters of the overriden function contravariance rule The result parameter of the overriding function must be a subtype of the result parameter of the overriden function covariance rule

393 //not C++ input parameters
class base { public: void virtual fnc (s1 par) {..} // S1 is the type of the formal // parameter } class derived: public base { void fnc (s2 par) {…} // C++ requires that s1 is identical to // S2 base* b derived* d s1 v1; s2 v2; if (..) b = d; b-> fnc(v1); //okay if b is base but what if it is //derived

394 //not C++ result parameter
class base { public: t1 virtual fnc (s1 par) {…}; // s1 is the type of formal parameter // t1 is the type of result parameter }; class derived: public base { t2 fnc (s2 par) {…}; // C++ requires that s1 is identical to // s2 and t1 is identical to t2 base* b; derived* d; s1 v1; s2 v2; t1 v0; if (…) b = d; v0 = b->fnc(v1); // okay if b is base but what if it is derived

395 Polymorphism – Strong Typing
Who uses it? Emerald uses both contravariance and covariance C++, Java, Object Pascal and Modula-3 use neither use exact identity Eiffel and Ada require covariance of both input and result parameters

396 Issues in Inheritance Hierarchies
Ada, Java and Smalltalk have a single inheritance model Java has separate interfaces and supports the idea of inheriting from multiple interfaces C++ and Eiffel have multiple inheritance What if both elephant and circus_performer have a member function ‘trunk_wag’ Diamond inheritance class circus_elephant: public elephant, circus_performer

397 root child2 child1 grandchild

398 Implementation and interface inheritance
object oriented programming Þ new software components may be constructed from existing software components inheritance complicates the issue of encapsulation interface inheritance Þ derived class can only access the parent class through the public interface implementation inheritance Þ derived class can access the private internal representation of the parent class

399 Protected Members class stack{ public:
void push(int, i){elements[top++] = I;}; int pop () {return elements[--top];}; private: int elements[100]; int top =0; }; class counting_stack: public stack { int size(); //return number of elements on stack stack s1, s2; stack* sp = new stack; sp->pop()

400 Protected Members implementation inheritance versus interface inheritance protected entities are visible within the class and any derived classes class stack{ public: void push(int, i){elements[top++] = I;}; int pop () {return elements[--top];}; protected int top = 0; private: int elements[100]; }; class counting_stack: public stack { int size(){return top;}; //return number of elements on stack

401 Protected Members class C { public: // accessible to everyone
// accessible to members and friends and // to members and friends of derived classes only private: //accessible to members and friends only };

402 Friends class Matrix; class Vector { float v[4];
friend Vector operator* (const Matrix&, const Vector&); }; class Matrix { Vector v[4]; }

403 Vector operator* (const Matrix& m, const Vector& v)
{ Vector r; for (int i = 0; i < 4; i++) { r.v[i] = 0; for (int j = 0; j ,4; j++) r.v[i] += m.v[i].v[j] * v.v[j]; } return r;

404 K. Louden, Programming Languages
Exercises What are the types of the following values? (1) [’a’,’b’,’c’] (’a’,’b’,’c’) [(False,’0’),(True,’1’)] ([False,True],[’0’,’1’]) [tail,init,reverse] Chapter 11 - Part II K. Louden, Programming Languages

405 K. Louden, Programming Languages
What are the types of the following functions? (2) second xs = head (tail xs) swap (x,y) = (y,x) pair x y = (x,y) double x = x*2 palindrome xs = reverse xs == xs twice f x = f (f x) Check your answers using Hugs. (3) Chapter 11 - Part II K. Louden, Programming Languages

406 K. Louden, Programming Languages
Curried Functions mult:: Int -> Int -> Int mult x y = x * y We could read this as a function taking two Int arguments and returning a third. OR-- a function taking a single Int argument and returning a function from Int -> Int as a result. Moral: the mult function can take either one or two parameters; if you give it one, it returns a function of the (missing) 2nd parameter Ex: map (mult 3) [1,2,3] Yields [3,6,9] Chapter 11 - Part II K. Louden, Programming Languages

407 K. Louden, Programming Languages
Curried Functions Contrast the previous definition of mult with a definition using a tuple: mult2 :: (Int,Int) -> Int mult2 (x,y) = x *y Now we must always supply both parameters at every call: The mult function is Curried (named after Haskell B. Curry), the mult2 function is not. Chapter 11 - Part II K. Louden, Programming Languages

408 K. Louden, Programming Languages
Curried Functions Definition: A function taking multiple parameters is Curried if it can be viewed as a (higher-order) function of a single parameter. Currying is good, since all functions can be viewed as having just a single parameter, and higher-order functions can be obtained automatically. Chapter 11 - Part II K. Louden, Programming Languages

409 K. Louden, Programming Languages
Haskell A fully-Curried lazy purely functional language with Hindley-Milner static typing. (Fully-Curried means all functions, including built-in arithmetic, are implicitly Curried.) Has many other features that make it perhaps the most advanced functional language available today. Includes overloading and a built-in mechanism for generator-filter programming (called list comprehensions). Chapter 11 - Part II K. Louden, Programming Languages

410 K. Louden, Programming Languages
Sample Haskell code: fact n = if n == 0 then 1 else n * fact (n-1) square x = x * x pi1 = gcd1 u v = if v == 0 then u else gcd1 v (mod u v) squarelist lis = if (null lis) then lis else square (head lis): squarelist (tail lis) Chapter 11 - Part II K. Louden, Programming Languages

411 K. Louden, Programming Languages
Haskell properties Parentheses are only necessary for grouping. Semicolons are usually unnecessary. Definitions have no special syntax, so they must be loaded from a file—they cannot be entered directly into an interpreter. Operators are infix. Lists are written using square brackets and commas (e.g. [1,2,3]), with ++ as the list append operation, and : (colon) as the list constructor operation. All value and function names must start with a lowercase letter, and all types are uppercase: Int. Names cannot be redefined unless you state you are overriding them - hence the use of pi1 & gcd1 in the previous slide (pi & gcd are predefined). Chapter 11 - Part II K. Louden, Programming Languages

412 K. Louden, Programming Languages
Haskell properties (2) Haskell is purely functional, so there are no variables or assignments, and I/O and sequencing cannot be done except using special tools, called monads (which we do not study here). Of course, there are still local definitions (in other words no value is being stored, we are just defining the pattern): let x = 2; y = 3 in x + y or: let x = y = 3 in x + y Note indentation in the previous code to get rid of the semicolon: Haskell uses a two-dimensional Layout Rule to remove extra syntax. Leading white space matters! Chapter 11 - Part II K. Louden, Programming Languages

413 K. Louden, Programming Languages
Haskell properties (3) Note lambda syntax All local definitions are recursive: f x = let z = x g = \y->if y==0 then z else g(y-1) in g x + 2 All expressions are delayed in Haskell: ones = 1:ones -- can also write [1,1..] ints_from n = n : ints_from (n+1) ints = ints_from 1 -- also: [1..] Infix operators can be made prefix by putting parentheses around them: doubleIt lis = map ((*) 2) lis Comment Chapter 11 - Part II K. Louden, Programming Languages

414 K. Louden, Programming Languages
Haskell properties (4) Hindley-Milner type checking is performed on all definitions and expressions. Type variables use the names a, b, c, etc. Types are not automatically displayed, but can be viewed by using the :t command in the interpreter. Small sample interpreter session: > [1]++[2] [1,2] > :t (++) (++) :: [a] -> [a] -> [a] > [1]++['a'] – wants types to match error: No instance for (Num Char) arising from the literal `1' at <interactive>:1:1 Probable fix: add an instance declaration for (Num Char) In the list element: 1 In the first argument of `(++)', namely `[1]' In the definition of `it': it = [1] ++ ['a'] Chapter 11 - Part II K. Louden, Programming Languages

415 K. Louden, Programming Languages
Strong typing Checks whether or not expressions we wish to evaluate or definitions we wish to use obey typing rules without any evaluation taking place. A pattern is consistent with a type if it will match some elements of that type. A variable is consistent with any type A pattern (t:ts) is consistent with [p] if t is consistent with p and ts is consistent with [p] Chapter 11 - Part II K. Louden, Programming Languages

416 K. Louden, Programming Languages
Type Checking f:: a->b->c f x y | g1 = e1 | g2 = e2 |otherwise = e3 We must check g1 and g2 are boolean x is consistent with a and y is consistent with b e1,e2,e3 are all of type c. Chapter 11 - Part II K. Louden, Programming Languages

417 Examples of type inference
f (x,y) = (x,[‘a’..y]) What is the type of f? The argument of f is a pair. We consider separately the constraints of x and y. Y is used with [‘a’..y] so it must be a Char. f :: (a, Char) -> (a, [Char]) Chapter 11 - Part II K. Louden, Programming Languages

418 Examples of type inference
g(m,z) = m + length z What constraints are placed on m and z? M must be numeric, as used with +. z must be a list as used with length. Furthermore, since length returns an int, we assume m is also an Int. Now consider the function composition The input of g must be the output of f g . f :: (Int, Char) -> Int *Main> Chapter 11 - Part II K. Louden, Programming Languages

419 K. Louden, Programming Languages
Unification We describe the intersection of the sets given by two type expressions. The unification of the two is the most general common instance of the two type expressions. Chapter 11 - Part II K. Louden, Programming Languages

420 Unification need not result in a monotype.
(a,[a]) and ([b],c) unify as ([b], [[b]]) Chapter 11 - Part II K. Louden, Programming Languages

421 K. Louden, Programming Languages
Patterns in Haskell Patterns can be used in function definitions Typical patterns are 0 for zero, [] for the empty list, and (x:xs) for a nonempty list. fact and squarelist : fact 0 = 1 fact n = n * fact (n-1) squarelist [] = [] squarelist (x:xs) = (square x) : squarelist xs Of course, it would be better to use map: squarelist lis = map square lis Chapter 11 - Part II K. Louden, Programming Languages

422 Patterns in Haskell (cont.)
Because of patterns, it is unusual in Haskell to use head, tail, and null. The anonymous wildcard pattern (matches anything) is the underscore in Haskell. Overlapping patterns are ok (see fact on previous slide). Non-exhaustive patterns can also be used, and do not generate a warning Patterns are syntactic sugar for case expressions: fact n = case n of > _ -> n * fact (n-1) Chapter 11 - Part II K. Louden, Programming Languages

423 Haskell List Comprehensions
Generators and filters can be expressed together in Haskell in a quasi-list notation called a list comprehension: odds = [n | n <- [1..], mod n 2 /= 0] -- can also write [1,3..] Multiple generators and filters can be combined in a single list comprehension: mystery = [n+m|n <-[1..], m <-[1..], mod n m /= 0] List comprehensions allow many snappy programs to be written: (mod without quotes is prefix) sieve (h:t) = h:sieve [n|n <- t, mod n h /= 0] primes = sieve [2..] Chapter 11 - Part II K. Louden, Programming Languages

424 Haskell Data Structures
So far we have only seen lists in Haskell, although we know that static typing does not allow lists to imitate structs or classes as in Scheme. Haskell has built-in tuple types. which are Cartesian products (like structs but with no field names): intWithRoot:: Int -> (Int,Double) intWithRoot x = (x , sqrt (fromInt x)) Use patterns to get the components out: rootOf x = let (_,r)=intWithRoot x in r Tuple types are written the same way values are, Chapter 11 - Part II K. Louden, Programming Languages

425 Haskell Data Structures (2)
Lists and tuples do not go far enough, since unions cannot be expressed. User-defined types can be introduced using data definitions: data Direction = North|East|South|West Haskell can have polymorphic types and constructors with more than one parameter data Either1 a b = Left1 a | Right1 b fun ::Either1 a b -> Bool fun (Left1 _) = True fun (Right1 _) = False fun (Left1 5) True :t Left1 "hi" Left1 "hi" :: Either1 [Char] b Chapter 11 - Part II K. Louden, Programming Languages

426 Haskell Data Structures (3)
Note how the previous definition expresses a tagged union of two polymorphic types. Binary search tree example (recursive type): data BST a = Nil | Node a (BST a) (BST a) simpleBST = Node "horse" Nil Nil -- value Constructors can be used as patterns: tree_to_list Nil = [] tree_to_list (Node val left right) = (tree_to_list left) ++ [val] (tree_to_list right) Type synonyms can also be defined: type IntDouble = (Int,Double) Note: all type and constructor names must be uppercase. Chapter 11 - Part II K. Louden, Programming Languages

427 K. Louden, Programming Languages
member a -> [a] ->Bool Can check for equality, but this only works when a is a type for which equality is defined. How do we express that? We call the collection of types over which a function is defined to be the type class. For instance, the set of types over which == is defined is the equality class. Chapter 11 - Part II K. Louden, Programming Languages

428 How do we define such a class
We say what is needed for a type a to be in a class. In this case, we need == defined over a. In other words, a ->a->Bool class Eq a where (==) :: a-> a-> Bool Members of a type class are called its instances. Functions from Int->Int are NOT of type Eq since there is no algorithm to decide if two functions have the same behavior Chapter 11 - Part II K. Louden, Programming Languages

429 Overloading in Haskell
Many functions in Haskell are overloaded, in that they can take values from a (finite) number of different types. An easy example is the square function, defined by square x = x * x. The type of square in Haskell is: square :: Num a => a -> a This says basically that square is defined for any Num a type (such types all have a * function). The type Num a => a is called a qualified type, and Num is called a type class. Type classes are a bit like Java interfaces: they require that a certain function be defined, and each associated type must then implement it. Chapter 11 - Part II K. Louden, Programming Languages

430 Overloading in Haskell (2)
Here is an example of how to define a Sizeable type class that provides a measure of the size of a piece of data: class Sizeable a where size:: a -> Int Now any type that you want to implement Sizeable must be declared an instance of the Sizeable class: (i.e., belongs to that class): instance Sizeable [a] where size = length instance Sizeable (BST a) where size Nil = 0 size (Node d l r) = (size l)+(size r) + 1 Chapter 11 - Part II K. Louden, Programming Languages

431 Overloading in Haskell (3)
Now any use of the size function automatically adds the Sizeable qualification to a type: trivial x = size x == 0 This function has type: Sizeable a => a -> Bool Type classes usually require multiple functions: class Num a where (+), (-), (*) :: a -> a -> a negate :: a -> a abs :: a -> a ...etc. instance Num Int where (+) = primPlusInt (-) = primMinusInt negate = primNegInt ...etc. Built-in "hidden" definitions Chapter 11 - Part II K. Louden, Programming Languages

432 Overloading in Haskell (4)
Type classes may need to "inherit" functions from other type classes (similar to interface inheritance in Java): class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x/=y) x /= y = not (x==y) class (Eq a) => Ord a where (<),(<=),(>=),(>) :: a -> a -> Bool max, min :: a -> a -> a instance Eq Int where … instance Ord Int where ... Note default definitions Chapter 11 - Part II K. Louden, Programming Languages

433 Numeric Type Class Hierarchy
is read “is an instance of” Chapter 11 - Part II K. Louden, Programming Languages

434 Overloading in Haskell (5)
The Show class allows a data type to be displayed as a String (e.g. by the interpreter): class Show a where show :: a -> String So many data types need to be made instances of Show and Eq that Haskell can do it automatically: data BST a = Nil | Node a (BST a) (BST a) deriving (Show,Eq) Overloading presents challenges for Hindley-Milner type checking that result in some surprises: squarelist = map square gives squarelist the type [Integer] -> [Integer] (no overloading!) Chapter 11 - Part II K. Louden, Programming Languages

435 COMP313A Programming Languages
Logic Programming (2)

436 Lecture Outline Horn Clauses Resolution Some Prolog

437 A little bit of Prolog Objects and relations between objects
Facts and rules parent(pam, bob). parent(tom,bob). parent(tom, liz). parent(bob, ann). parent(bob, pat). parent(pat, jim). ? parent(bob, pat). ? parent(bob, liz). ? parent(bob, ben). ? parent(bob, X). ? parent(X, Y).

438 Prolog grandparent (X,Y) :- parent(X, Z), parent(Z, Y).
For all X and Y X is the grandparent of Y if X is a parent of Z and Z is a parent of Y sister (X,Y) :- parent(Z, X), parent(Z, Y), female(X) X is the sister of Y if Z is the parent of both X and Y and X is a female

439 Horn Clauses A clause is either a single predicate called a fact or
a rule of the form condition Þ conclusion Conclusion is a single predicate Condition is a conjunction of predicates a1 Ù a2 Ù a3 … Ù an parent(X,Z) Ù parent(Z,Y) Þ grandparent(X,Y)

440 Horn Clauses Horn clauses don’t have any quantifiers
Assumes that the variables in the head are universally quantified and the variables in the body are existentially quantified (if they don’t appear in the head)

441 Horn Clauses grandparent(X,Y) Ü parent(X,Z) Ù parent(Z,Y)
Equivalent to "x : "y : $z parent(X,Z) Ù parent(Z,Y) Þ grandparent(X,Y)

442 Horn clauses continued
A fact mammal(human) Ü A query or goal Ü mammal(human)

443 Horn clause example " x mammal(x) Þ legs(x,2) Ú legs(x,4)
Prolog legs(x,4) :- mammal(x), not legs(x,2) legs(x,2) :- mammal(x), not legs(x,4)

444 legs(x,4) Ü mammal(x) Ù Ø legs(x,2).
mammal(human). mammal(horse). Ø legs(horse, 2).

445 Resolution Inference rule for Horn clauses
Combine left and right hand sides of two horn clauses Cancel those statements which match on both sides

446 Resolution example Example 1 Fact: mammal(human).
Query: Ü mammal(human). Resolution: Ü mammal(human). mammal(human) Ü mammal(human). Ü

447 Resolution example Example 1 Facts: see slide 10
Query: Ü legs(horse,4). Resolution: Ü legs(horse,4). legs(horse,4) Ü legs(horse,4), mammal(horse), Ø legs(horse, 2). Ü mammal(horse), …..

448 Turn the following sentences into formulae in first order predicate logic
John likes all kinds of food Apples are food Chicken is food Anything anyone eats and isn’t killed by is food Bill eats peanuts and is still alive Sue eats everything Bill eats Prove that John likes peanuts using backward chaining

449 " x : food(x) Þ likes(John, x)
food(Apples) food(Chicken) " x : " x : eats(x,y) Ù Ø killed_by(x,y) Þ food(x) eats(Bill, Peanuts) alive (Bill) x : eats(Bill, x) Þ eats(Sue,x) We have no “or”s. Drop the qualifiers to get the horn clauses

450 1. likes(John, x) Ü food(x).
2. food(Apples). 3. food(Chicken). 4. food(x) Ü eats(x,y) Ù Ø killed_by(x,y). 5. eats(Bill, Peanuts). 6. alive (Bill). 7. eats(Sue,x) Ü eats(Bill, x). 8. alive(x,y) Ü Ø killed_by(x,y). 9. Ø killed_by(x,y) Ü alive(x,y). Proves that John likes Peanuts using resolution

451 Horn clause example The Euclidian algorithm to compute the gcd of two positive integers, u and v: The algorithm The gcd of u and 0 is u The gcd of u and v, if v is not 0, is the same as the gcd of v and the remainder of dividing v into u.

452 Horn clause example greatest common divisor "u gcd(u, 0, u)
" u, " v, " w, Ø zero(v) Ù gcd(v, u mod v, w) Þ gcd(u,v,w) gcd(u,0,u) gcd(u,v,w) Ü Øzero(v) Ù gcd(v, u mod v, w) What is the greatest common denominator of 15, and 10. i.e. find and instantiation for X in gcd(15,10,X) What value of X makes gcd(15,10,X) True

453 COMP313A Programming Languages
Logic Programming (3)

454 Lecture Outline Some Prolog Unification

455 Some more prolog Recursive rules example predecessor relation
predecessor(X,Z) :- parent(X,Z). predecessor(X,Z) :- parent(X,Y), parent(Y,Z) And so on… predecessor(X,Z) :- parent(X,Y), predecessor(Y,Z).

456 Some more prolog parent(pam,bob). parent(tom,bob). parent(tom,liz).
parent(bob, ann). parent(bob, pat). parent(pat, jim). ? predecessor(pam,bob). ? predecessor(pam, ann). ? predecessor(pam, liz). ? predecessor(pam, X).

457 A prolog program comprises clauses - facts, rules and queries
big(bear). %clause 1 big(elephant). %clause 2 small(cat). %clause 3 brown(bear). %clause 4 black(cat). %clause 5 grey(elephant). %clause 6 dark(Z) :- black(Z). %clause 7 dark(Z) :- brown(Z). %clause 8 ?dark(X), big(X).

458 Data Objects Atoms versus numbers versus Variables
strings of letters, digits, and the underscore character beginning with a lower case letter some strings of special characters can enclose strings of characters in single quotes e.g. ‘Fred’ Numbers integers and floats

459 Data Objects Atoms versus Variables
Variables are strings of characters, digits and underscores that begin with an uppercase letter or an underscore Can use the anonymous underscore hasachild(X) :- parent(X,Y) hasachild(X) :- parent(X,_) ? parent(X, _)

460 Data Objects Structured objects
objects that have several components location(X, Y, Orientation). location(156, 786, 25). location(156, 786, Orientation). location is the functor

461 Structures date(X,Y,Z). date(20, june, 2005). date(Day, june, 2005).
date(Day, Month, Year) :- Day > 0, Day <= 31,…….

462 These are all terms data objects structures simple objects constants
variables atoms numbers These are all terms

463 Operations on lists Membership
The member relation member(X,L) ? member(d, [a, b, h, d]). ? ? member(d, [a,b,[d h], e]). ?member([d, h], [a, [d,h], e f]).

464 Membership X is a member of L if X is the head of L, or
X is a member of the tail of L. member(X, [X|Tail]). member(X, [Head | Tail]) :- member(X,Tail). Note two separate clauses

465 Membership X is a member of L if X is the head of L, or
X is a member of the tail of L, or X is a member of a sublist of L member(X, [X|Tail]). member(X, [Head | Tail]) :- member(X,Tail). plus one more clause……

466 Concatenation The concatenation relation – conc(L1, L2, L3)
? conc([a,b], [c,d], [a,b,c,d]) yes ? conc([a,b], [c,d], [a, b, [c,d]]) no ?conc([a,b], [c,d], X). X = [a,b,c,d]

467 Concatentation conc(L1, L2, Result) If L1 is the empty list
then L2 and Result must be equal If L1 is nonempty then have [X|L1tail] recursively X becomes the head of Result and we use conc to find the tail of result [X|TailResult] Eventually will have exhausted L1 and TailResult will be L2.

468 Concatentation conc([], L, L).
conc([X | L1], L2, [X | L3]) :- conc(L1, L2, L3).

469 Unification Matching clauses with variables
Have to find the appropriate substitutions for variables so that the clauses match Process is called unification Process by which variables are instantiated

470 GCD example gcd(u,0,u) gcd(u,v,w) Ü not zero(v), gcd(v, u mod v, w)
Using resolution the goal Ü gcd(15, 10, x)

471 Unification in Prolog A constant unifies only with itself ? me = me.
Yes ?me = you. No Gcd(5, 0, 5) Ü Gcd(5, 0, 5) Gcd(5, 10, w) Ü Gcd(5, 0, w)

472 Unification in Prolog…
A variable that is uninstantiated unifies with anything and becomes instantiated with that thing ? me = X. X = me ? f(a,X) = f(Y, b). X = b Y = a ? f(X) = f(Y) gcd(u,v,w) Ü not zero(v), gcd(v, u mod v, w), gcd(15, 10, x). gcd(15, 10, x) Ü not zero(10), gcd(10, 15 mod 10, x), gcd(15, 10, x).

473 Unification in Prolog A structured term (predicate or function applied to arguments requires Same predicate/function name Same number of arguments Arguments can be unified recursively ? f(X) = g(X) ? f(X) = f(a,b) ? f(a, g(X)) = f(Y, b) ? f(a, g(X)) = f(Y, g(b))

474 Unification examples Unify the following : p(X,Y) and p(a, Z)
p(X,X) and p(a,b) ancestor(X,Y) and ancestor(bill, W) p(X,a,Y) and p(Z,Z,b) p(Marcus, g(X,Y)) and f(x, g(Caesar, Marcus)) g(X, X) and g(f(X), f(X))

475 COMP313A Programming Languages
Logic Programming (4)

476 Lecture Outline Some Prolog lists Unification

477 Concatentation conc([], L, L). conc([X | L1], L2, [X | L3]) :-
conc(L1, L2, L3). Can use concat to decompose lists How? Can use concat to define the member predicate - member2(X, L) :- conc(L1, [X|L2], L).

478 Adding and deleting How do you add an item to a list?
Deleting an item from a list – del(X, L, Result) If X is the head of L – result is the tail of L If X is contained in the tail of L then recursively split L into its head and tail until X is at the head. Then 1 will apply.

479 Deleting… del(X, [X|Tail], Tail]).
del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1) Deletes one occurrence from the list What happens when: del(a, [a, b, a, a], X). What if we changed it to del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1), X=\=Y.

480 Delete What if we want to delete every instance from the list

481 del (X, [], []). del (X, [X|Tail], Tail1) :- del (X, Tail, Tail1). del (X, [Y|Tail], [Y|Tail1]) :- del (X, Tail, Tail1).

482 Relations/Terms/Queries An important note
You can define different relations with the same name but with different numbers of arguments e.g. member/1, member/2, member/3 If you leave off an argument prolog thinks it is a different relation If it is undefined for that number of arguments you will get an error message And if there is such a relation predefined…….

483 Define two predicates evenlength(List) and oddlength(List) so that they are true if their argument is a list of even or odd length respectively. For example ? evenlength([a,b,c,d]) ? yes ?oddlength([c, d, e]) ?yes The trick is to define them as mutually recursive clauses. Start with []

484 [] is even A list is even if its tail is odd A list is odd if its tail is even

485 evenlength([]). evenlength([First | Rest]) :- oddlength(Rest). oddlength([First | Rest]) :- evenlength(Rest).

486 Unification Matching clauses with variables
Have to find the appropriate substitutions for variables so that the clauses match Process is called unification Process by which variables are instantiated

487 GCD example gcd(u,0,u) gcd(u,v,w) Ü not zero(v), gcd(v, u mod v, w)
Using resolution the goal Ü gcd(15, 10, x)

488 Unification in Prolog A constant unifies only with itself ? me = me.
Yes ?me = you. No Gcd(5, 0, 5) Ü Gcd(5, 0, 5) Gcd(5, 10, w) Ü Gcd(5, 0, w)

489 Unification in Prolog…
A variable that is uninstantiated unifies with anything and becomes instantiated with that thing ? me = X. X = me ? f(a,X) = f(Y, b). X = b Y = a ? f(X) = f(Y) gcd(u,v,w) Ü not zero(v), gcd(v, u mod v, w), gcd(15, 10, x). gcd(15, 10, x) Ü not zero(10), gcd(10, 15 mod 10, x), gcd(15, 10, x).

490 Unification in Prolog A structured term (predicate or function applied to arguments requires Same predicate/function name Same number of arguments Arguments can be unified recursively ? f(X) = g(X) ? f(X) = f(a,b) ? f(a, g(X)) = f(Y, b) ? f(a, g(X)) = f(Y, g(b))

491 Unification examples Unify the following : p(X,Y) and p(a, Z)
p(X,X) and p(a,b) ancestor(X,Y) and ancestor(bill, W) p(X,a,Y) and p(Z,Z,b) p(Marcus, g(X,Y)) and f(x, g(Caesar, Marcus)) g(X, X) and g(f(X), f(X))

492 COMP313A Programming Languages
Logic Programming (5)

493 Lecture Outline Cut operator Negation as Failure Control Information

494 Backtracking PROGRAM big(bear). %clause 1 big(elephant). %clause 2
small(cat). %clause 3 brown(bear). %clause 4 black(cat). %clause 5 grey(elephant). %clause 6 dark(Z) :- black(Z). %clause 7 dark(Z) :- brown(Z). %clause 8 ?dark(X), big(X).

495 Controlling Backtracking
move(1,8). move(1,6). move(2,9). move(2,7). move(3,8). move(3,4). move(4,3). move(4,9). move(6,1). move(6,7). move(7,2). move(7,6). move(8,1). move(8,3). move(9,2). move(9,4). 1 2 3 4 5 6 7 8 9 Knight’s Tour Problem

496 Controlling Backtracking…
Find all the two-move paths path2(X,Y) :- move(X,Z), move(Z,Y). path2(1,W). ? W = 1 ? W = 3 ? W = 7

497 Controlling Backtracking…
path2(X,Y) :- move(X,Z), !, move(Z,Y). ? path2(1, W). ? W = 1 ? W = 3 ! is the cut operator

498 Controlling Backtracking…
path2(X,Y) :- move(X,Z), move(Z,Y),!. ? path2(1, W).

499 Controlling Recursive Calls
predicate path determines if there is a path between two nodes. path1(Z,Z,L). path1(X,Z,L) :- move(X,Y), not (member(Y,L)), path(Y, Z, [Y|L]). path2((Z,Z,L). path2(X,Z,L) :- move(X,Y), not (member(Y,L)), path(Y, Z, [Y|L]), ! . path1(1,3, []). path2(1,3, []).

500 Negation as Failure Closed world assumption
The goal not (X) succeeds whenever the goal X fails! ?mother(amy, bob). ?not(parent(amy, bob)). Why did it fail?

501 Negation as Failure Nonmonotonic reasoning – more information sometimes means fewer things can be proved human(bob). ?human(X) X = bob ?not human(X) no ?not (not human(X)). X = X why?

502 Control Information Prolog uses a depth first search strateggy
Therefore order is important pred1(X,Z) :- parent(X,Z). pred1(X,Z) :- parent(X,Y), pred1(Y,Z). pred2(X,Z) :- pred2(Y,Z), parent(X,Y). pred2(X,Z) :- parent(X,Z).

503 parent(pam,bob). parent(tom,bob). parent(tom,liz). parent(bob, ann). parent(bob, pat). parent(pat, jim). ? pred(tom, pat).

504 COMP313A Programming Languages
Logic Programming (6)

505 Some More Prolog structures

506 I/O in Prolog Files associated with input and output streams
I/O from terminal – user In some Prologs only two files/streams are active at any one time current input stream current output stream see switches input streams tell switches output streams In prolog I/O is a side effect of the predicate

507 Tkeclipse Prolog stdin, stdout open a stream for input or output
standard input and output streams ?- write("fred"). Yes (0.00s cpu) open a stream for input or output open(SourceSink, Mode, Stream) ? open('//C/Documents and Settings/mjeff/My Documents/prolog/fred.txt', read, Fred), read_string(Fred, "\r\n ", 10, X). Fred = 31 X = "freddy"

508 Bits and pieces Anonymous variables
family(person(tom, fox, date(7,may, 1950), employed), person(ann, fox, date(9, may, 1951), employed), [person(pat, fox, date(5, may, 1973), unemployed), person(jim, fox, date(5, may, 1973), unemployed)]). husband(X) :- family(X,Y, Z). Y and Z are singleton variables husband(X) :- family(X, _, _)

509 More bits and pieces Comparison operators X > Y X < Y X >= Y
X = Y 1 + A = B A = 2 + B X \= Y X =:= Y =:= A =:= B + 2 X =\= Y

510 Structures a family database
family(person(tom, fox, date(7,may, 1950), employed), person(ann, armstrong, date(9, may, 1951), employed), [person(pat, fox, date(5, may, 1973, unemployed), person(jim, fox, date(5, may, 1973), unemployed)]). family(_,_,_). % any family family(person(_, fox, _, _), _, _). % the fox family family(_, person(_,armstrong, _, _), _) % or the armstrong family family(_, _, [_, _, _]) % any three child family family(_,person(Name, Surname, _, _), [_, _, _, | _]). % all married women who have at least three children

511 husband(X) :- family(X, _, _). wife(X) :- f amily(_,X,_).
X = person(tom, fox, date(7, may, 1950), employed) Yes (0.00s cpu) ?- wife(X). X = person(ann, armstrong, date(9, may, 1951), employed) Problem – this may be too simplistic

512 husband2(X,Y) :- family(person(X,Y,_,_), _, _).
married(X,Y) :- family(person(X, _, _, _), person(Y,_, _, _),_). married(X,Y) :- married(Y,X). child(X) :- family(_, _, Children), member(X, Children). ?? child(X) ??

513 but ?? child(pat) child(X) :- family(_, _, Children), mem_children (X, Children). mem_children (X, [person(X, _, _, _) | Tail]). mem_children (X, [person(Y, _, _, _) | Tail]) :- mem_children1(X, Tail).

514 Selectors Define relation which allow us to access particular
components of a structure without knowing the details the structure This is data abstraction These selectors are useful when we want to create instances of families, for example husband(family(Husband, _, _), Husband). wife(family(_, Wife, _), Wife). children(family(_, _, Childlist), Childlist).

515 Selectors… firstchild(Family, First) :- children(Family, [First | _]).
secondchild(Family, Second) :- children(Family, [_, Second | _]). nthchild(N, Family, Child) :- children(Family, ChildList), nth_member(ChildList, N, Child). firstname(person(Name, _, _,_), Name). surname(person(_, Surname, _, _), Surname).

516 nth_member([X|_], 1, X). nth_member([_| L], N, Child) :- N1 is N-1, nth_member(L, N1, Child).

517 Using them.. Tom Fox and Jim Fox belong to the same family and Jim is the second child of Tom firstname(Person1, tom), surname(Person1, fox), % person1 is Tom Fox firstname(Person2, jim), surname(Person2, fox), %person2 is Jim Fox husband(Family, Person1), secondchild(Family, Person2). Person1 = person(tom, fox, _, _) Person2 = person(jim, fox, _, _) Family = family(person(tom, fox, _, _), _, [_, person(jim, fox, _,_) | _])

518 Logic Puzzles Use the following clues to write a prolog program to determine the movies the robots tinman, r2d2, johnny five, and a dalek starred in. Neither Tinman nor Johnny five were one of the daleks in Dr Who and the Daleks The movie Short Circuit did not star Tinman. R2d2 wowed the audiences in Star Wars. A dalek did not star in the Wizard of Oz.

519 Structure is important
Solution(L) :- We want a binding for L which will contain the result we are after What is the result we want?

520 L = [movie(X1, wizardofoz),
movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)], Now we have to supply a mechanism for instantiating X1..X4 We need a way of selecting a value and then checking it against some constraints

521 L = [movie(X1, wizardofoz),
movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)],

522 L = [movie(X1, wizardofoz),
movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)], Robotslist = [tinman, dalek, r2d2, johnnyfive], We will draw the values for X1..X2, from Robotslist We do this using the member predicate

523 L = [movie(X1, wizardofoz),
movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)], Robotslist = [tinman, dalek, r2d2, johnnyfive],

524 L = [movie(X1, wizardofoz),
movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)], Robotslist = [tinman, dalek, r2d2, johnnyfive], member(X1, Robotslist), X1 \= dalek, member(X2, Robotslist), X2 \= tinman, X2 \= johnnyfive, member(X3, Robotslist), X3 = r2d2, member(X4, Robotslist), X4 \= tinman, There are just two more things left to do

525 solution(L):- L = [movie(X1, wizardofoz), movie(X2, drwhoandtheDaleks), movie(X3, starwars), movie(X4, shortcircuit)], Robotslist = [tinman, dalek, r2d2, johnnyfive], member(X1, Robotslist), X1 \= dalek, member(X2, Robotslist), X2 \= tinman, X2 \= johnnyfive, member(X3, Robotslist), X3 = r2d2, member(X4, Robotslist), X4 \= tinman, X2 \= X1, X2 \= X3, X2 \= X4, X3 \= X1, X3 \= X4, X4 \= X1, print_movies(L).

526 print_movies([A|B]) :- !,
write(A), nl, print_movies(B). print_movies([]).


Download ppt "Louden, Programming Languages"

Similar presentations


Ads by Google