SCHEME INTERPRETER GUIDE 4

Similar documents
TAIL RECURSION, SCOPE, AND PROJECT 4 11

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

SCHEME AND CALCULATOR 5b

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

Control Structures 1 / 17

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

Principles of Programming Languages 2017W, Functional Programming

Review 3. Exceptions and Try-Except Blocks

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

Intro. Scheme Basics. scm> 5 5. scm>

6.001 Notes: Section 15.1

61A Lecture 26. Monday, October 31

CS61A Notes Week 13: Interpreters

Structure and Interpretation of Computer Programs

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

CS 360 Programming Languages Interpreters

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Project 6 Due 11:59:59pm Thu, Dec 10, 2015

Interpreters and Tail Calls Fall 2017 Discussion 8: November 1, 2017 Solutions. 1 Calculator. calc> (+ 2 2) 4

UNIVERSITY of CALIFORNIA at Berkeley Department of Electrical Engineering and Computer Sciences Computer Sciences Division

Python review. 1 Python basics. References. CS 234 Naomi Nishimura

CS1 Lecture 3 Jan. 22, 2018

Project 2 Due 11:59:59pm Wed, Mar 7, 2012

Functional Programming. Pure Functional Programming

COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 21 Scheme TODAY REVIEW: DISPATCH DICTIONARIES DISPATCH DICTIONARIES 7/27/2012

Discussion 12 The MCE (solutions)

CSE 341, Autumn 2005, Assignment 3 ML - MiniML Interpreter

Public-Service Announcement

Structure and Interpretation of Computer Programs

Chapter 9: Dealing with Errors

Project 5 Due 11:59:59pm Wed, July 16, 2014

STRUCTURE AND INTERPRETATION COMPUTER PROGRAMS COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 23 Py TODAY 7/26/2012

INTERPRETERS 8. 1 Calculator COMPUTER SCIENCE 61A. November 3, 2016

CS1 Lecture 3 Jan. 18, 2019

TUPLES AND RECURSIVE LISTS 5

Mid-Term 2 Grades

Python Evaluation Rules

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

SCHEME The Scheme Interpreter. 2 Primitives COMPUTER SCIENCE 61A. October 29th, 2012

A LISP Interpreter in ML

6.S189 Homework 2. What to turn in. Exercise 3.1 Defining A Function. Exercise 3.2 Math Module.

STRUCTURE AND INTERPRETATION COMPUTER PROGRAMS >>> COMPUTER SCIENCE IN THE NEWS. CS61A Lecture 23 Py TODAY REVIEW: INTERPRETATION 7/26/2012 READ PRINT

Structure and Interpretation of Computer Programs

Environment Diagrams. Administrivia. Agenda Step by Step Environment Diagram Stuff. The Rules. Practice it s on! What are The Rules?

Parsing Scheme (+ (* 2 3) 1) * 1

You ve encountered other ways of signalling errors. For example, if you lookup an unbound key in a hashtable, Java (and Scala) produce nulls:

Ruby: Introduction, Basics

Ruby: Introduction, Basics

There are four numeric types: 1. Integers, represented as a 32 bit (or longer) quantity. Digits sequences (possibly) signed are integer literals:

Control Structures. Lecture 4 COP 3014 Fall September 18, 2017

Lecture Programming in C++ PART 1. By Assistant Professor Dr. Ali Kattan

Variables, expressions and statements

CS61A Lecture 23 Py. Jom Magrotker UC Berkeley EECS July 26, 2012

INTERPRETERS AND TAIL CALLS 9

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science

Bindings & Substitution

CS109A ML Notes for the Week of 1/16/96. Using ML. ML can be used as an interactive language. We. shall use a version running under UNIX, called

CSCI 3155: Homework Assignment 4

INTERPRETATION AND SCHEME LISTS 12

CS61A Notes Week 1A: Basics, order of evaluation, special forms, recursion

Python Essential Reference, Second Edition - Chapter 5: Control Flow Page 1 of 8

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

INHERITANCE AND INTERFACES 7

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

EXPRESSIONS, STATEMENTS, AND FUNCTIONS 1

4.3 FURTHER PROGRAMMING

CS 61A Discussion 8: Scheme. March 23, 2017

Class extension and. Exception handling. Genome 559

RECURSION AND LINKED LISTS 3

\n is used in a string to indicate the newline character. An expression produces data. The simplest expression

Text Input and Conditionals

Homework 3 COSE212, Fall 2018

CIS192 Python Programming

CIS192 Python Programming

INTERPRETATION AND SCHEME LISTS 12

CSCI 204 Introduction to Computer Science II. Lab 6: Stack ADT

tolerance Documentation

Project 2: Scheme Interpreter

CS 115 Data Types and Arithmetic; Testing. Taken from notes by Dr. Neil Moore

Computer Science 9608 (Notes) Chapter: 4.3 Further programming

Project 1: Scheme Pretty-Printer

Ruby: Introduction, Basics

CS 115 Lecture 4. More Python; testing software. Neil Moore

Lecture 21. Programming with Subclasses

News. Programming Languages. Recap. Recap: Environments. Functions. of functions: Closures. CSE 130 : Fall Lecture 5: Functions and Datatypes

Below are example solutions for each of the questions. These are not the only possible answers, but they are the most common ones.

Exercise 1: Graph coloring (10 points)

CMSC 201 Fall 2016 Lab 09 Advanced Debugging

CS61A Midterm 2 Review (v1.1)

Advanced Python. Executive Summary, Session 1

EXCEPTIONS, CALCULATOR 10

CS52 - Assignment 8. Due Friday 4/15 at 5:00pm.

UNIVERSITY OF CALIFORNIA Department of Electrical Engineering and Computer Sciences Computer Science Division

Fall 2018 Discussion 8: October 24, 2018 Solutions. 1 Introduction. 2 Primitives

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

What is an Exception? Exception Handling. What is an Exception? What is an Exception? test = [1,2,3] test[3]

Principles of Programming Languages

CS 11 python track: lecture 4

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

Transcription:

SCHEME INTERPRETER GUIDE 4 COMPUTER SCIENCE 61A July 28, 2014 1 Scheme Values Back in Python, we had all these objects (i.e. lists, tuples, strings, integers) which inherited from the superclass object. Since our interpreter is trying to interpret Scheme, we ve decided to represent our inputs as Scheme objects instead. The Scheme objects implemented are Pairs (lists), Procedures (functions), SchemeSymbol, SchemeStr, etc and all of them inherit from the superclass SchemeValue. The following diagram showcases some of the relationship. For example, when we parse our input stk> (car '(1 2)), we parse it to Pair(SchemeSymbol(car), Pair(Pair(SchemeInt(1), \ Pair(SchemeInt(2), nil)), nil)). When we evaluate this parsing, we eventually call the car method on Pair(SchemeInt(1), Pair(SchemeInt(2), nil)). We give Pairs a car method. However, the user might input bad commands like stk> (car 1). We try to call the car method on a SchemeInt; however, because there is no car method in SchemeInt, Python would throw an AttributeError: SchemeInt has no attribute car 1

DISCUSSION 4: SCHEME INTERPRETER GUIDE Page 2 and our Scheme interpreter would crash. Our program is not allowed to crash. What we can can do is implement a car method into SchemeInt and have that car method raise a SchemeError. Well build our interpreter to catch SchemeErrors and deal with them properly. (Remember that we must distinguish between errors from user input and errors from our program, if it is faulty.) However, what if the user tries to input stk> (car 'hi) or stk> (car 1.1)? Now our program will crash because there is no car method in SchemeStr or SchemeFloat. How do we deal with the other Scheme objects not mentioned? Well we could try putting in a car method in all those Scheme objects, but thats repetitive. What if we only put it in SchemeValue instead? Then if we try to call the car method on a SchemeInt or SchemeStr, Python will take advantage of inheritance to get the car method from SchemeValue. To summarize, SchemeValue was created to contain all the methods that would be called on the implemented Scheme object instances. Most of the methods in SchemeValue will raise a SchemeError because they only are valid on one type of Scheme object. The other methods in SchemeValue are the default definitions for most Scheme objects (like booleanp, which returns True because it checks if the object is a boolean or not.) An instance object of SchemeValue will never be created. The various subclasses of SchemeValue will be implemented and each will override certain methods of SchemeValue when needed. 2 Parsing Scheme When reading the previous guide, you may have wondered when we convert the operator and operands to Scheme expressions? The answer is in the parsing stage. This is because when we get to the eval stage, everything has to be in Scheme expression form in order to be evaluated. Therefore, in the steps before eval, we have to change the 2 that is inputted into a SchemeInt(2). We tackle this when we parse. The parsing we do in our Scheme Interpreter follows the same general idea as the one that you did for your Calculator homework. This is because Scheme uses prefix notation, just like the Calculator did. Below is the code from homework, compared with the code given for the Scheme project. The read exp and read until close function from homework turned the tokenized line into a combination of Pairs once they encountered a expression with parentheses around it. The scheme read and read tail from the project do something analogous to that. Right now, we want to focus on the part where we convert a single token into a Scheme object. We see that we do that in the beginning if conditions for scheme read. Where

DISCUSSION 4: SCHEME INTERPRETER GUIDE Page 3 could the homework equivalent of that go? Which function did we create to focus on a particular token at a time? Numberize. To compare the parts of the code that perform the same function, we ve color coded corresponding section of the code in the side-by-side comparisons. Can you see how we can replace the try-except statement in numberize with the if statements in scheme read? def scheme_read(src): if src.current() is None: raise EOFError val = src.pop() if type(val) is int or \ type(val) is float: return scnum(val) elif type(val) is bool: return scbool(val) elif val not in DELIMITERS: if val[0] == '"': return scstr(eval(val)) return intern(val) elif val == "'": elif val == "(": return read_tail(src) raise SyntaxError("unexpected \ token: {0}".format(val)) def read_tail(src): try: if src.current() is None: raise SyntaxError("unexpected \ end of file") if src.current() == ")": src.pop() return nil first = scheme_read(src) rest = read_tail(src) return Pair(first, rest) except EOFError: raise SyntaxError("unexpected \ end of file") def numberize(atomic_exp): try: return int(atomic_exp) except ValueError: try: return float(atomic_exp) except ValueError: return atomic_exp def read_exp(tokens): if tokens == []: raise SyntaxError('unexpected \ end of input') token, rest = tokens[0], tokens[1:] if token == ')': raise SyntaxError('unexpected )') elif token == '(': if rest == []: raise SyntaxError(' \ mismatched parentheses') elif rest[0] == ')': raise SyntaxError('empty \ combination') return read_until_close(rest) return numberize(token), rest) def read_until_close(tokens): if tokens == []: raise SyntaxError('unexpected \ end of input') token, rest = tokens[0], tokens[1:] if token == ')': raise SyntaxError('unexpected )') elif token == '(': if rest == []: raise SyntaxError(' \ mismatched parentheses') elif rest[0] == ')': raise SyntaxError('empty \ combination') It seems that in scheme read, instead of having SchemeSymbol, SchemeStr, etc, we have intern, scstr, etc. The latter are functions that we wrote to create their analogous Scheme objects. (If you look at the code for these functions in scheme primitives.py, you ll understand why we used these functions instead of directly instantiating the class.)

DISCUSSION 4: SCHEME INTERPRETER GUIDE Page 4 The other main type of Scheme object that hasn t been mentioned yet is Procedures. Procedures are our way of implementing functions. We ll discuss more of that in the next section. To summarize the relationship between Scheme data types to their respective Python code, we ve created this handy table: Scheme Data Type Our Internal Representation Class Python Code Types defined in scheme primitives.py Numbers: 0, 3.2 SchemeInt and SchemeFloat scnum(0), scnum(3.2) Symbols: merge, x SchemeSymbol intern( merge ), intern( x ) Strings: foo SchemeStr scstr( foo ) Booleans: #t, #f scheme true and scheme false scheme true, scheme false Pairs: (a. b) Pair Pair(intern( a ), intern( b )) nil: () nil nil Lists: (a b) Pair and nil Pair(intern( a ), Pair(intern( b ), nil)) okay okay okay Types defined in scheme.py Functions PrimitiveProcedure, PrimitiveProcedure(), LambdaProcedure, LambdaProcedure(), MuProcedure MuProcedure() 3 Functions in Scheme There are two types of functions: Procedures and Special Forms. Lets talk about Procedures first. There are two types of procedures: Built-In and User-Defined. User-Defined Procedures are functions that we define. So in our Scheme interpreter, it ll be lambdas and mus (mus are lambdas but with a different type of lookup process). Because you ll be implementing user-defined procedures for the project, we ll just talk about the built-in procedure. 3.1 Built-In Procedures Let s go back to the example in the beginning: stk> (car '(1 2)). This gets parsed to: Pair(SchemeSymbol(car), Pair(Pair(SchemeInt(1), \ Pair(SchemeInt(2), nil)), nil)). When we evaluate this expression, we evaluate each subexpression: SchemeSymbol(car) and Pair(SchemeInt(1), Pair(SchemeInt(2), nil)). When SchemeSymbol(car) gets evaluated in scheme eval, we see that it is a SchemeSymbol

DISCUSSION 4: SCHEME INTERPRETER GUIDE Page 5 and then lookup its value in the Frames that we create in our interpreter. The value that we associate with SchemeSymbol(car) is Procedure(scheme car). def scheme_eval(expr, env): while env is not None: if expr is None: raise SchemeError("Cannot evaluate an undefined \ expression.") if scheme_symbolp(expr): expr, env = env.lookup(expr).get_actual_value(), None Procedures are created by taking in a function. In our example, scheme car is a function that we created. scheme car takes in a variable and calls the car method of that variable. When we finish evaluating the subexpressions, we then apply the operator to the operands with scheme apply(procedure(scheme car), Pair(SchemeInt(1), Pair(SchemeInt(2), nil))). The apply method of Procedure will call scheme car with the operand and our interpreter will get back SchemeInt(1). def scheme_eval(expr): procedure = scheme_eval(first, env) args = procedure.evaluate_arguments(rest, env) if proper_tail_recursion: expr, env = scheme_apply(procedure, args, env), None 3.2 Special Forms Special Forms are different from Procedures in that Procedures will evaluate all of its operands while a Special Form will only evaluate some. Some examples of Special Forms include the functions or, and, let. Where must we handle these special forms? In scheme eval because if it was in scheme apply, then all of the operands will have already been evaluated, which we don t want. Since each special form is evaluated differently, each special form will have its own function. For example, if we get a or then we ll call do or form. If we get an and, then we ll call do and form. Since we have about 10 special forms, we don t want to have a series

DISCUSSION 4: SCHEME INTERPRETER GUIDE Page 6 of ifelse statements inside of our scheme eval that checks for each different special form. Instead, well use a dispatch dictionary, which we ve called SPECIAL FORMS. If you find SPECIAL FORMS, you can see that the keys are the different commands and the values are the respective do * form function. Therefore, what would have been 20 lines of code was condensed to 2 lines of code. When the interpreter does SPECIAL FROMS[first], we get the do * form function out and then call it with the expression. def scheme_eval(expr): first, rest = scheme_car(expr), scheme_cdr(expr) if (scheme_symbolp(first) and first in SPECIAL_FORMS): if proper_tail_recursion: expr, env = SPECIAL_FORMS[first](rest, env) expr, env = scheme_eval(expr, env), None ***One important thing to note is that after you call do * form, you scheme eval the result again. Why is that? Also what values can the expr and env (from SPECIAL FROMS[first]) be? Hint: there is more than one pair of expression, environment that you can return. ***