CPS 343/543 Lecture notes: Front end, conditional evaluation, and local binding



Coverage: [EOPL2] §§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 (through define-datatype)
    • pattern matching (through cases)
  • data abstraction
    • interface, implementation, application
    • multiple representations (list, abstract-syntax, and procedural)


Plan

  • now we 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


Execution through compilation


Compiler vs. interpreter

  • see [EOPL2] Figure 3.1 (p. 70): 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 [EOPL2] 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 that 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 from them
  • how will we specify the tokens? regular expressions
  • how will we specify the grammar? EBNF
  • lex is a scanner generator for C
  • yacc is a parser generator for C


SLLGEN

  • SLLGEN is a parser-generator system for Scheme (see [EOPL2] Appendix A)
  • the sllgen:make-define-datatypes procedure is 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 that takes a string and produces an abstract syntax representation
  • read-eval-print loop in Scheme
  • see [EOPL2] 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.
    [EOPL2] D.P. Friedman, M. Wand, and C.T. Haynes. Essentials of Programming Languages. MIT Press, Cambridge, MA, Second edition, 2001.

Return Home