;; The first three lines of this file were inserted by DrScheme. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "reader.ss" "plai" "lang")
; The job of a parser is to match patterns (i.e., the right
; sides of the BNF rules) and convert them into expressions.
; Macro-expressive patterns are those that can simply be
; re-written into other patterns. We've seen some examples,
; including: LET can be rewritten as an APP and a LAMBDA,
; and AND and OR can be rewritten using IF. Both of those
; arose while writing our own interpreters.
;
; We've also seen how Scheme's DEFINE-SYNTAX-RULE lets us
; define our own patterns for Scheme's existing interpreter.
; DEFINE-SYNTAX-RULE limits us to patterns of the form:
;
; (pattern-name ... )
;
; The underlying parser is able to recognize more sophisticated
; patterns, however. A first example is including extra terminals
; inside a syntax to make it more readable.
;
; DEFINE-SYNTAX, used in conjunction with SYNTAX-RULES, is
; a more general pattern matcher. For example, we can make a
; more natural looking version of LET:
(define-syntax with
(syntax-rules (=)
; e.g., 1 + 2
[(with ` =
...)
(let ([`` ])
(begin ...))]))
(with x = 4
(print x)
(newline)
(set! x (+ x 1))
x)
; We can even parse syntax with multiple forms. For example, there
; are two versions of DEFINE in Scheme, and the parser is able to
; distinguish them by looking at more than the first symbol after
; the open paren.
;
; The example below uses SYNTAX-RULES to rewrite non-nested infix
; expressions for writing "normal" mathematical notation in Scheme:
(define-syntax infix
(syntax-rules ()
; e.g., 1 + 2
[(infix `**) ( **** ****)]
; e.g., not true, sqrt 3
[(infix ****) ( )]
; e.g., 7
[(infix ) ]))
; Examples
(infix 1 + 2)
(infix 7 > 4)
(infix not false)
(infix 4)
; Define some familiar math operators:
(define == equal?)
(define ^ expt)
(define (|| a b) (or a b))
(infix 7 == 7)
(infix 2 ^ 10)
(infix sqrt 144)
(infix true || false)
; We might want to make more complicated syntax patterns. For example,
; the above does not let us put parentheses inside the infix operation:
;
; (infix (3 + 4) * 5)
;
; That's because the internal parentheses begin a new expression. If
; we want to stay in infix syntax we have to keep "turning it back on":
(infix (infix 3 + 4) * 5)
; It *is* possible in Scheme to make it so that infix looks inside of the
; child expressions. In fact, it is possible to make even something like
; the following evaluate correctly. Note that I've turned semi-colons into
; full colons, since semi-colon is the Scheme comment character:
;
; (Java
; int x = 3:
; println(x + 4):
; while (x < 10) {
; ++x:
; }
; )
;
; See the SYNTAX documentation for a discussion of all of the capabilities.
;
; Scheme's macro system is Turing Equivalent. That means that we can
; implement any computable function using it. This ensures that
; the macro system is powerful enough for any kind of macro we want,
; but it introduces a new problem: with that much complexity, Scheme
; can't check to make sure that the *parser* halts! A simple
; example of this is just:
(define-syntax-rule (infinite-loop) (infinite-loop))
;(define (loop) (loop))
(infinite-loop)
;(loop)
; Including the next line causes the program to hang in the parser:
;
; (infinite-loop)
; Overloading in languages like Java and C++ is similar to this
; pattern matching notion. If you define two constructors with
; different numbers of arguments in Java, the compiler decides
; which one to invoke *at compile time*, by matching the pattern:
;
; class Cow {
; public Cow(boolean isBrown);
; public Cow(boolean isBrown, float weight);
; }
;
; Cow c = new Cow(true); // Calls the first constructor
; Cow d = new Cow(true, 400); // Calls the second constructor
;
; It might be nice to program in a Scheme-like language where
; PROCEDURES were defined using this pattern-matching idea, not just
; macros. It would be especially nice in a functional language, since
; our semantics of applying rules to reduce expressions to values
; would exactly match the programming style. For example, imagine we
; could write something like:
;
; (define-procedure factorial
; [(factorial 0) 1] ; base case
; [(factorial exp) (* (factorial (- exp 1)) exp)])
;
; or
;
; (define-procedure -
; [(- a b) (sub a b)] ; 2-argument subtraction
; [(-a) (sub 0 a)]) ; 1-argument subtraction
;
; We can't do that in Scheme (without some major macro hacking!),
; but there are languages based on that pattern matching idea. We'll
; be looking at one soon, called ML.
**