CS558 Programming Languages

Similar documents
CS558 Programming Languages. Winter 2013 Lecture 4

CS558 Programming Languages

CS558 Programming Languages

CS558 Programming Languages

CS558 Programming Languages

Scope, Functions, and Storage Management

CS558 Programming Languages

CS558 Programming Languages Winter 2018 Lecture 4a. Andrew Tolmach Portland State University

CS558 Programming Languages

CS558 Programming Languages

Semantic Analysis. Outline. The role of semantic analysis in a compiler. Scope. Types. Where we are. The Compiler Front-End

The role of semantic analysis in a compiler

G Programming Languages - Fall 2012

Weeks 6&7: Procedures and Parameter Passing

CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion. Zach Tatlock Winter 2018

CS558 Programming Languages

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

G Programming Languages - Fall 2012

Stack. 4. In Stack all Operations such as Insertion and Deletion are permitted at only one end. Size of the Stack 6. Maximum Value of Stack Top 5

CS558 Programming Languages

Project Compiler. CS031 TA Help Session November 28, 2011

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

G Programming Languages Spring 2010 Lecture 4. Robert Grimm, New York University

CS24: INTRODUCTION TO COMPUTING SYSTEMS. Spring 2018 Lecture 11

COL728 Minor2 Exam Compiler Design Sem II, Answer all 5 questions Max. Marks: 20

CS321 Languages and Compiler Design I Winter 2012 Lecture 13

Chapter 20: Binary Trees

Lecture 10 Notes Linked Lists

Semantic Analysis. Outline. The role of semantic analysis in a compiler. Scope. Types. Where we are. The Compiler so far

Lecture Notes on Queues

LECTURE 18. Control Flow

Le L c e t c ur u e e 5 To T p o i p c i s c t o o b e b e co c v o e v r e ed e Exception Handling

CSC System Development with Java. Exception Handling. Department of Statistics and Computer Science. Budditha Hettige

CS24: INTRODUCTION TO COMPUTING SYSTEMS. Spring 2015 Lecture 11

Lecture 10 Notes Linked Lists

Lecture 7: Type Systems and Symbol Tables. CS 540 George Mason University

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

The Compiler So Far. Lexical analysis Detects inputs with illegal tokens. Overview of Semantic Analysis

CS558 Programming Languages. Winter 2013 Lecture 3

Overview of today s lecture. Quick recap of previous C lectures. Introduction to C programming, lecture 2. Abstract data type - Stack example

Anatomy of a Compiler. Overview of Semantic Analysis. The Compiler So Far. Why a Separate Semantic Analysis?

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Two approaches. array lists linked lists. There are two approaches that you can take:

DATA STRUCTURE AND ALGORITHM USING PYTHON

Iterators. Lecture 24: Iterators & Exceptions. Implementing Iterators. When Good Programs Go Bad! - where! Abstract over control structures (in Clu)!

Continuations and Continuation-Passing Style

Object Oriented Programming Exception Handling

Programming Languages and Techniques (CIS120)

Programming Languages

Next: What s in a name? Programming Languages. Data model in functional PL. What s in a name? CSE 130 : Fall Lecture 13: What s in a Name?

Today: Revisit some objects. Programming Languages. Key data structure: Dictionaries. Using Dictionaries. CSE 130 : Winter 2009

Programming Languages Third Edition. Chapter 9 Control I Expressions and Statements

Programming Languages and Compilers (CS 421)

Run-time Environments - 2

n What is its running time? 9/18/17 2 n poor_rev [1,2,3] = n (poor_rev [1] = n ((poor_rev [1] =

Computing Fundamentals Advanced functions & Recursion

! Tree: set of nodes and directed edges. ! Parent: source node of directed edge. ! Child: terminal node of directed edge

CS 3410 Ch 7 Recursion

CSE 130, Fall 2006: Final Examination

INF 102 CONCEPTS OF PROG. LANGS FUNCTIONAL COMPOSITION. Instructors: James Jones Copyright Instructors.

Name CPTR246 Spring '17 (100 total points) Exam 3

COMP-520 GoLite Tutorial

Announcements. Working on requirements this week Work on design, implementation. Types. Lecture 17 CS 169. Outline. Java Types

The Stack ADT. Stacks. The Stack ADT. The Stack ADT. Set of objects in which the location an item is inserted and deleted is prespecified.

OCaml Data CMSC 330: Organization of Programming Languages. User Defined Types. Variation: Shapes in Java

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

CS 3 Introduction to Software Engineering. 3: Exceptions

DO NOT OPEN UNTIL INSTRUCTED

CS4215 Programming Language Implementation. Martin Henz

Chapter 9 STACK, QUEUE

Lecture 9: Control Flow

Outline. Introduction Stack Operations Stack Implementation Implementation of Push and Pop operations Applications. ADT for stacks

Run-time Environments

G Programming Languages - Fall 2012

Run-time Environments

The fringe of a binary tree are the values in left-to-right order. For example, the fringe of the following tree:

G Programming Languages - Fall 2012

Forward recursion. CS 321 Programming Languages. Functions calls and the stack. Functions calls and the stack

Short Notes of CS201

CSE 130, Fall 2005: Final Examination

Cpt S 122 Data Structures. Course Review FINAL. Nirmalya Roy School of Electrical Engineering and Computer Science Washington State University

CS143 - Written Assignment 4 Reference Solutions

Data Structures Week #3. Stacks

Topics Covered Thus Far CMSC 330: Organization of Programming Languages

CS201 - Introduction to Programming Glossary By

Lecture Notes on Interfaces

CMSC330. Objects, Functional Programming, and lambda calculus

Attributes of Variable. Lecture 13: Run-Time Storage Management. Lifetime. Value & Location

Principles of Programming Languages

Konzepte von Programmiersprachen

Overview. finally and resource cleanup

Programming Languages and Techniques (CIS120e)

mywbut.com Exception Handling

CSci 4223 Lecture 12 March 4, 2013 Topics: Lexical scope, dynamic scope, closures

Module 8: Binary trees

Implementing Subprograms

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

TAIL RECURSION, SCOPE, AND PROJECT 4 11

Code Generation & Parameter Passing

Topic 7: Activation Records

Transcription:

CS558 Programming Languages Winter 2017 Lecture 6a Andrew Tolmach Portland State University 1994-2017

Iteration into Recursion Any iteration can be written as a recursion, e.g. while (c) {e Scala is equivalent to def f(b:boolean):unit = if (b) { e; f(c) f(c) assuming the variables used by c and e are in scope

Recursion into iteration? When can we do the converse? A recursion can be rewritten as an iteration (without needing any extra storage) whenever all the recursive calls are in tail position Call in tail position iff it is the last thing performed by the caller before it itself returns Often worthwhile, in order to avoid pushing a stack activation frame for each recursive call (lowers total stack needed and eliminates push/pop time) A decent compiler can turn tail-calls into iterations automatically. This is essential for functional languages, which use recursion heavily, but is useful even for imperative ones.

Scala list tail-call examples def find (y:int,xs:list[int]):boolean = xs match { case Nil => false case (x::xs1) => (x == y) find(y,xs1) // tail-recursive def length (xs:list[int]):int = xs match { case Nil => 0 case (_::xs1) => 1 + length(xs1) // not tail-recursive def length_tr (xs:list[int]):int = { // use an auxiliary function with an accumulating parameter def f (xs:list[int],len:int):int = xs match { case Nil => len case (_::xs1) => f (xs1,len+1) // tail-recursive f(xs,0)

Systematic Removal of Recursion But what about general (non-tail) recursion? One way to get a better appreciation for how recursion is implemented is to see what is required to get rid of it Additional explicitly-allocated memory space is needed!

typedef struct tree *Tree; struct tree { int value; Tree left, right; ; void printtree(tree t) { if (t) { print(t->value); printtree(t->left); printtree(t->right); ORIGINAL PROGRAM (Adapted from R. Sedgewick, Algorithms, 2nd ed.)

STEP 1 Remove tail-recursion. void printtree(tree t) { top: if (t) { print(t->value); printtree(t->left); t = t->right; goto top;

STEP 2 Use explicit stack to replace non-tail recursion. Simulate behavior of compiler by pushing local variables and return address onto the stack before call and popping them back off the stack after call. Assume this stack interface, specialized to use Tree as the stack element type. Stack empty; void push(stack s,tree t); Tree pop(stack s); bool isempty(stack s);

STEP 2(CONT.) Here there is only one local variable (t) and the return address is always the same, so there s no need to save it. void printtree(tree t) { Stack s = empty; top: if (t) { print(t->value); push(s,t); t = t->left; goto top; retaddr: t = t->right; goto top; if (!(isempty(s))) { t = pop(s); goto retaddr;

Simplify by: STEP 3 Rearranging to avoid the retaddr label. Pushing right child instead of parent on stack. Replacing first goto with a while loop. void printtree(tree t) { Stack s = empty; top: while (t) { print(t->value); push(s,t->right); t = t->left; if (!(isempty(s))) { t = pop(s); goto top;

STEP 4 Rearrange some more to replace outer goto with another while loop. (This is slightly wasteful, since an extra push, stackempty check and pop are performed on root node.) void printtree(tree t) { Stack s = empty; push(s,t); while(!(isempty(s))) { t = pop(s); while (t) { print(t->value); push(s,t->right); t = t->left;

STEP 5 A more symmetric version can be obtained by pushing and popping the left children too. Compare this to the original recursive program. void printtree(tree t) { Stack s = empty; push(s,t); while(!(isempty(s))) { t = pop(s); if (t) { print(t->value); push(s,t->right); push(s,t->left);

STEP 6 We can also test for empty subtrees before we push them on the stack rather than after popping them. void printtree(tree t) { Stack s = empty; if (t) { push(s,t); while(!(isempty(s))) { t = pop(s); print(t->value); if (t->right) push(s,t->right); if (t->left) push(s,t->left);

Exceptions Programs often need to handle exceptional conditions, i.e., deviations from normal control flow Exceptions may arise from failures of built-in or system operations (e.g. division by zero, reading past end of file) user-defined events (e.g. key not found in dictionary) Handling these conditions in-line distorts the code for the normal case Most recent languages (Ada, C++,Java, Python, etc.) provide a way to define, raise (or throw), and handle exceptions

Scala exceptions example class Help extends Exception // define a new exception try {... if (gone wrong) throw new Help // raise user-defined exception... x = a / b // might raise a built-in exception... catch { case _: Help =>...report problem... case _: ArithmeticException => x = -99 // repair damage

Semantics of exceptions If there is a statically enclosing handler, throwing an exception behaves much like a goto.... if (gone wrong) goto help_label;... help_label:...report problem... But what if no handler encloses the throw point? In most languages, uncaught exceptions propagate to the next dynamically enclosing handler Caller can handle uncaught exceptions thrown in callee A few languages support resumption of the program at throw point. Many languages permit a value to be returned with the exception

Dynamic exception handling case class BadThing(problem:String) extends Exception def foo() = {... throw BadThing("my problem")... def bar() { try { foo() catch { case BadThing(problem) => println("oops:" + problem )

Exceptions vs. Error Values An alternative to user-thrown exceptions is to return status values, which must be checked by caller. def find (k0:string,env:list[(string,int)]) : Option[Int] = env match { case Nil => None case (k,v)::t => if (k == k0) Some(v) else find(k0,t)... find("abc",e) match { case Some(v) =>... v... case None =>...perform error recovery...

Exceptions vs Error Values (2) With exceptions, we can defer checking for (rare) error conditions until a more convenient point. class NotFound extends Exception def find (k0:string,env:list[(string,int)]) : Int = env match { case Nil => throw new NotFound case (k,v)::t => if (k == k0) v else find(k0,t)... try { val v = find ("abc",e)... v... catch { case _:NotFound =>...perform error recovery...

Implementing exceptions (1) One approach to implementing exceptions is for runtime system to maintain a handler stack, with an entry for each currently active context Each entry contains a handler code address and call stack pointer When the scope of a handler is entered (e.g. by evaluating a try with expression), handler s address is paired with current stack pointer and pushed onto handler stack When an exception occurs, top of handler stack is popped, resetting the call stack pointer and passing control to the handler s code. If this handler itself raises an exception, control passes to the next handler on the stack etc. Selective handlers work by simply re-throwing any exception they don t want to handle (passing control to next handler on the stack)

Exceptions on purpose In this model, throwing an exception provides a way to return quickly from a deep recursion, with no need to pop stack frames one at a time class Zero extends Exception def product(l:list[int]) : Int = { def prod(l:list[int]) : Int = l match { case Nil => 1 case h::t => if (h==0) throw new Zero else h * prod(t) try { prod(l) catch { case _:Zero => 0

Implementing Exceptions (2) The handler-stack implementation makes handling very cheap, but incurs cost each time we enter a new handler scope. If throws are very rare, this is a bad tradeoff. An alternative: runtime system uses a static table that maps each code address to the address of the statically enclosing handler (if any) If an exception occurs, table is inspected to find the appropriate handler If there is no handler covering the current address, runtime system looks for a handler that covers the return address (in the caller), and so on up the call stack