CPS 343/543 Lecture notes: Front end, conditional
evaluation, and local binding
Coverage: [EOPL] §§3.1-3.4 (pp. 69-84)
Course recap
- language definition and description methods (BNF);
can use these to define/describe more than just language
(e.g., data structures)
- recursive programming in Scheme and λ-calculus
- (static and dynamic) scoping
- bindings and binding times
- datatypes
- definition (via define-datatype)
- pattern matching (via cases)
- data abstraction
- interface, implementation, application
- multiple representations (list, abstract-syntax, and procedural)
Plan
- now we will use these fundamentals,
to build data-driven interpreters
(in the style of occurs-free?, parse-expression,
and unparse-expression)
- we will progressively add features such as
conditional evaluation, local binding,
statements, and recursion
Preliminaries
- set of values a programming language manipulates:
- expressed values: possible values of expressions (e.g.,
in Scheme: numbers, pairs, characters, strings)
- denoted values: values bound to variables (e.g.,
in Scheme: locations containing expressed values (or pointers))
- in our first language:
- expressed value = number
- denoted value = number
- two languages
- defined language (or source language):
language specified by the interpreter
- defining language (or host language):
language in which we write the interpreter
(here, Scheme)
Execution through interpretation
(adapted version of [EOPL] Fig. 3.1a, p. 59)
Execution through compilation
(adapted version of [EOPL] Fig. 3.1b, p. 59)
Compiler vs. interpreter
- see Figure 3.1 (p. 70) of [EOPL]: both compilers and
interpreters have a front end which consists of a
scanner (lexical analyzer) and parser (syntactic analyzer)
- compiler
- a compiler is a program which translates a program
in one language (the source language) to an equivalent program
in another language (the target language)
- a compiler is just a translator, nothing more
- advantages to compilation:
- fast execution: generates machine code which executes fast
- compile once, execute multiple times
- disadvantages to compilation:
- slow development:
vicious compile-run-debug-re-compile cycle
- less flexibility:
most choices in a program are fixed at compile-time
(e.g., size of an array)
- an interpreter is a software simulation of machine which
natively (i.e., no translation involved) understands
instructions in the source
language [COPL]
- an interpreter provides a virtual machine for a programming language
- advantages to interpretation:
- interpreters provide direct support for source-level debugging
(e.g., consider a run-time array out-of-bounds error)
- interpreters lend themselves to late binding
- dynamic typing (based on run-time data)
- programming on-the-fly and then interpreting the code at run-time
- disadvantages to interpretation:
- slow: decoding high-level expressions (which are more complex
than machine instructions) is the bottleneck as opposed to
pipeline between the processor and memory [COPL]
- source program usually occupies more space (i.e.,
program manipulated by the interpreter is
often stored in a representation which makes interpretation
convenient and this representation is typically not of minimal size)
- hybrid (compilation-interpretation) systems: Perl, Java; why?
A simple interpreter
- grammar on p. 71
- sample expressions in this language on p. 72
- necessary datatypes on p. 72
- simple interpreter in Figure 3.2 on p. 74
Scanning and parsing
- what do we need before we can test our interpreter?
- a program which converts a program into an abstract syntax tree,
called a front end (scanner + parser)
- scanning picks out the lexemes and returns a list of tokens
- parsing organizes this list into an abstract syntax tree, if possible
- building a parser is a well-studied area of computer science
- rarely done by hand anymore
- a parser generator is a program which takes lexical and
syntactic specifications
automatically generates a scanner and parser for them
- how will we specify the tokens? regular expressions
- how will we specify the grammar? BNF
- lex is a scanner generator for C
- yacc is a parser generator for C
SLLGEN
- SLLGEN is a parser-generator system for Scheme (see Appendix A)
- the sllgen:make-define-datatypes
procedure can used to automatically
generate the define-datatype declarations from the grammar (
or we can code them up by hand)
- the sllgen:make-string-parser procedure is used to
automatically generate the scanner and parser;
it returns a procedure which takes a string
and produces an abstract syntax
- read-eval-print loop in Scheme
- Figures 3.3, 3.4, and 3.5
Conditional evaluation
- how can we add conditional evaluation to our interpreter?
- new production rule and constructor on p. 80
- let us avoid boolean for now by associating 0 with false and
anything else with true, true-value? predicate on
p. 80
- sample conditional expressions on p. 80
- augmented eval-expression procedure
Local binding
- how can we add local binding to our interpreter?
- sample expressions on p. 82
- scope holes
- new production rule and constructor on p. 82
- must take care to evaluate operands using passed environment
and body using extending environment
- augmented eval-expression procedure in Figure 3.6 on p. 83
References
| [COPL] |
R.W. Sebesta. Concepts of Programming Languages.
Addison-Wesley, Boston, MA, Sixth edition, 2003. |
| [EOPL] |
D.P. Friedman, M. Wand, and C.T. Haynes.
Essentials of Programming Languages.
MIT Press, Cambridge, MA, Second edition, 2001. |
| [EOPL] |
D.P. Friedman and M. Wand.
Essentials of Programming Languages.
MIT Press, Cambridge, MA, Third edition, 2008. |
|