comment: -*- Module: expression -*- module: expression language: prefix-dylan ;;; ;;; ********************************************************************** ;;; Copyright (c) 1993 Carnegie Mellon University, all rights reserved. ;;; (begin "$Header$") ;;; ;;; ********************************************************************** ;;; ;;; This file contains the support for expressions and macros. ;;; ;;;; Lexical environment stuff. (define-class () ;; ;; The variable-name this is a binding of. (getter: binding-name required-init-keyword: name:)) (define-class () ;; ;; The module this lexenv is contained in. (lexenv-module required-init-keyword: module:) ;; ;; List of bindings. (lexenv-bindings init-value: '() init-keyword: bindings:)) (define-method make ((c (singleton )) #key module (bindings '()) inside) (if module (if inside (error "Can only supply one of module: or inside:") (next-method c module: module bindings: bindings)) (if inside (bind-methods ((append (new old) (if (id? new '()) old (pair (head new) (append (tail new) old))))) (next-method c module: (lexenv-module inside) bindings: (append bindings (lexenv-bindings inside)))) (error "Must supply either module: or inside:")))) (define-method make-load-form ((lexenv )) (unless (empty? (lexenv-bindings lexenv)) (error "Can't dump non-null lexical environments as constants")) (bind ((?module-name (make object: (module-name (lexenv-module lexenv))))) (expression (make module: (find-module ?module-name))))) (define-class () ;; ;; The lexical environment this context is part of, or #f if none. (lexical-context-lexenv init-value: #f init-keyword: lexenv:)) (define-method make-load-form ((context )) (bind ((?lexenv (make object: (lexical-context-lexenv context)))) (expression (make lexenv: ?lexenv)))) ;;;; Expression classes. (define-class () ;; ;; No slots. ) (define-class () (literal-constant-object required-init-keyword: object:)) (define-method similar? (thing1 thing2) (id? thing1 thing2)) (define-method similar? ((list1 ) (list2 )) (and (similar? (head list1) (head list2)) (similar? (tail list1) (tail list2)))) (define-method similar? ((v1 ) (v2 )) (and (= (size v1) (size v2)) (every similar? v1 v2))) (define-method similar? ((s1 ) (s2 )) (and (= (size s1) (size s2)) (every similar? s1 s2))) (define-method binary= ((lc1 ) (lc2 )) (similar? (literal-constant-object lc1) (literal-constant-object lc2))) (define-class () ;; ;; No slots. ) (define $pattern-ellipsis (make object: (make ))) (define-class () (vn-name init-value: 'temp init-keyword: name:) (vn-context init-function: (method () (make )) init-keyword: context:) (vn-originally-was init-value: #f init-keyword: was:)) (define-method make ((c (singleton )) #key (name 'temp) context was) (if (instance? context ) (bind-methods ((repeat (context) (if context (next-method c name: name context: (vn-context context) was: (repeat (vn-originally-was context))) #f))) (repeat context)) (next-method))) (define-method make-load-form ((vn )) (bind ((?name vn) (?context (make object: (vn-context vn))) (?was (make object: (vn-originally-was vn)))) (expression (make name: '?name context: ?context was: ?was)))) (define-method as ((c (singleton )) (vn )) (vn-name vn)) (define-method as ((c (singleton )) (vn (vn-name vn))) (define-method binary= ((vn1 ) (vn2 )) (and (id? (vn-name vn1) (vn-name vn2)) (id? (vn-context vn1) (vn-context vn2)))) (define find-binding (method ((name ) (lexenv )) (or (any? (method (binding) (= (binding-name binding) name)) (lexenv-bindings lexenv)) (bind ((was (vn-originally-was name))) (if was (lexenv-lookup was (lexical-context-lexenv (vn-context name))) #f))))) (define variable-name-module ((name ) (lexenv )) (bind ((was (vn-originally-was name))) (if was (variable-name-module was (lexical-context-lexenv (vn-context name))) (lexenv-module lexenv)))) (define-class () (combination-pieces required-init-keyword: pieces:)) (define-class () ;; ;; No slots. ) ;;;; Macros (define-class () macro-expander) (define-variable *macro-lexenv* #f) (define expand-macro (method ((macro ) (expr ) (lexenv )) (bind ((old-macro-lexenv *macro-lexenv*)) (unwind-protect (begin (set! *macro-lexenv* lexenv) ((macro-expander macro) expr)) (set! *macro-lexenv* old-macro-lexenv))))) (define-class () macro) ;;;; (define compile-time-eval-also (macro ((? ?form ...) (expression (begin (compile-stage ?form ...) ?form ...))))) ;;;; Utilities used by the expansion of EXPRESSION (define expr-map (method (proc #rest args) (bind ((length #f)) (for-each ((arg args)) () (select arg instance? ( (bind ((this-length (size arg))) (if length (unless (= length this-length) (error "sequences of different length.")) (set! length this-length)))) ())) (unless length (error "ellipsis after template with no sequence variables.")) (bind ((result (make length: length))) (do ((result-state (initial-state result) (next-state result result-state)) (states (map (method (arg) (and (instance? arg ) (initial-state arg))) args) (map (method (arg state) (and (instance? arg ) (next-state arg state))) args states))) ((not result-state) result) (set! (current-element result result-state) (apply proc (map (method (arg state) (if (instance? arg ) (current-element arg state) arg)) args states)))))))) ;;;; EXPRESSION macro. (compile-time-eval-also (define-method gen-pair ((car-form ) (cdr-form )) (make object: (pair (literal-constant-object car-form) (literal-constant-object cdr-form)))) (define-method gen-pair ((?car-form ) (?cdr-form )) (expression (pair ?car-form ?cdr-form))) (define-method gen-append (?list1 ?list2) (expression (concatenate ?list1 ?list2))) (define-method gen-map (?form map-env) (bind ((?var (map tail map-env)) (?val (map head map-env))) (expression (expr-map (method (?var ...) ?form) ?val ..)))) (define gen-ref (var maps emit) (if maps (gen-ref var (tail maps) (method (outer-var outer-maps) (bind ((mapping (any? (method (m) (id? (head m) outer-var)) (car maps)))) (if mapping (emit (tail mapping) maps) (bind ((inner-var (expression temp))) (emit inner-var (pair (pair (pair outer-var inner-var) (head maps)) outer-maps))))))) (emit var maps))) ); compile-time-eval-also (define expression (macro ((? ?template) (bind ((?context-var #f) (bindings '()) (bound-names '())) (bind-methods ((grovel (template maps emit) (select template instance? ( (bind ((?object (literal-constant-object template))) (emit (expression (make object: '?object)) maps))) ( (grovel-list (combination-pieces template) maps (method (?form maps) (emit (expression (make pieces: ?form)) maps)))) ( (bind ((name (as template))) (cond ((= (first name default: #\x) #\?) (gen-ref template maps (method (?form maps) (emit (expression (check-type ?form )) maps)))) ((member? template bound-names) (emit template maps)) (t (set! bound-names (add! bound-names template)) (unless ?context-var (set! ?context-var (expression context)) (set! bindings (pair (bind ((?lower-lexenv (make object: *lower-lexenv*))) (expression (?context-var (make lexenv: ?lower-lexenv)))) bindings))) (set! bindings (pair (bind ((?name template) (?var (make-literal-constant template))) (expression (?name (make name: '?name context: ?context-var originally-was: ?var)))) bindings)) (emit template maps))))))) (grovel-list (list maps emit) (cond ((id? list '()) (emit (make object: '()) maps)) ((and (not (id? (tail list) '())) (= (head (tail list)) $pattern-ellipsis)) (grovel-list (tail (tail list)) maps (method (cddr-form maps) (grovel (head list) (pair '() maps) (method (car-form maps) (if (id? (head maps) '()) (error "....") (emit (gen-append (gen-map car-form (car maps)) cddr-form) (cdr maps)))))))) (t (grovel (head list) maps (method (car-form maps) (grovel-list (tail list) maps (method (cdr-form maps) (emit (gen-cons car-form cdr-form) maps))))))))) (bind ((?guts (grovel ?template '() (method (form maps) form))) (?binding (reverse! bindings))) (expression (bind (?binding ...) ?guts)))))))) ;;;; Expression-case (compile-time-eval-also (define-class () ;; ;; No slots. ) (define-class () ;; ;; The object we are matching against. (literal-pattern-literal required-init-keyword: literal:)) (define-method make-load-form ((lit )) (bind ((?lit (make object: (literal-pattern-literal lit)))) (expression (make literal: ?lit)))) (define-class () ;; ;; The variable name this pattern element is supposed to match. (name-pattern-name required-init-keyword: name:) ;; ;; The lexenv from the program stage one layer down. (name-pattern-lexenv required-init-keyword: lexenv:)) (define-method make-load-form ((pat )) (bind ((?name (make object: (name-pattern-name pat))) (?lexenv (make object: (name-pattern-lexenv pat)))) (expression (make name: ?name lexenv: ?lexenv)))) (define-class () ;; ;; No slots. ) (define-method make-load-form ((vp )) (expression (make ))) (define-class combination-pattern () ;; ;; List of the sub-patterns that make up this combination. (combination-pattern-pieces required-init-keyword: pieces:)) (define-method make-load-form ((pattern )) (bind ((?pieces (make object: (combination-pattern-pieces pattern)))) (expression (make pieces: ?pieces)))) (define-class ellipsis-pattern () ;; ;; The sub-pattern that is repeated at the end of this combination. (ellipsis-pattern-repeated required-init-keyword: repeated:)) (define-method make-load-form ((pattern )) (bind ((?pieces (make object: (combination-pattern-pieces pattern))) (?repeated (make object: (ellipsis-pattern-repeated pattern)))) (expression (make pieces: ?pieces repeated: ?repeated)))) (define-class keyword-combination-pattern () ;; ;; A list of keywords accepted by this pattern. (keyword-pattern-keywords required-init-keyword: keywords:)) (define-method make-load-form ((pattern )) (bind ((?pieces (make object: (combination-pattern-pieces pattern))) (?keywords (make object: (keyword-pattern-keywords pattern)))) (expression (make pieces: ?pieces keyword: ?keywords)))) (define parse-pattern (method ((pattern )) (bind ((variables '())) (bind-methods ((parse (pattern depth) (select pattern instance? ( (when (= pattern $pattern-ellipsis) (error "Invalid use of ...")) (make-literal-pattern (literal-constant-object pattern))) ( (bind ((name (as pattern))) (cond ((= (first name default: #\x) #\?) (set! variables (add! variables pattern)) (make )) (t (make name: pattern lexenv: *lower-lexenv*))))) ( (let ((pieces (combination-pieces pattern))) (parse-seq pieces (initial-state pieces) '() depth))))) (parse-seq (seq state pieces depth) (if (null state) (make pieces: (reverse! pieces)) (bind ((next (current-element seq state)) (nstate (next-state seq state))) (cond ((= next (expression #key)) (make pieces: (reverse! pieces) keywords: (parse-keywords seq nstate '()))) ((and nstate (= (current-element seq nstate) $pattern-ellipsis) (not (next-state seq (copy-state seq nstate)))) (make pieces: (reverse! peices) repeated: (parse next (+ depth 1)))) (t (parse-seq seq nstate (add! pieces (parse next depth)) depth)))))) (parse-keywords (seq state keywords) (if state (bind ((keyword (parse-keyword (current-element seq state)))) (parse-keywords seq (next-state seq state) (add! keyword keywords))) (reverse! keywords))) (parse-keyword (keyword) (bind-exit (return) (expression-case keyword (?name (when (instance? ?name ) (set! variables (add! variables ?name)) (as (as ?name)))) ((?name ?default) (when (instance? ?name ) (set! variables (add! variables (list ?name depth ?default))) (return (as (as ?name))))) ((?keyword ?name) (when (and (instance? ?keyword ) (instance? ?name )) (bind ((keyword (literal-constant-object ?keyword))) (when (instance? keyword ) (set! variables (add! variables ?name)) (return keyword))))) ((?keyword ?name ?default) (when (and (instance? ?keyword ) (instance? ?name )) (bind ((keyword (literal-constant-object ?keyword))) (when (instance? keyword ) (set! variables (add! variables (list ?name depth ?default)) (return keyword)))))))))) (values (parse pattern 0) (reverse! variables)))))) ); compile-time-eval-also (define expression-case (macro ((? ?expr (?pattern ?form ...) ...) (bind ((?return (expression return)) (?temp (expression temp)) (?matcher (map (method (pattern ?forms) (bind ((parsed-pattern variables (parse-pattern pattern)) (?pattern (make parsed-pattern)) (?vars (map (method (x) (if (instance? x ) (head x) x)) variables)) (?vals (map (method (x) (if (instance? x ) (bind ((?var (first x)) (?def (third x))) (iterate repeat ((depth (second x))) (if (zerop depth) (expression (or ?var ?def)) (bind ((?guts (repeat (1- depth)))) (expression (map (method (?var) ?guts) ?var)))))) x)) variables))) (expression (bind ((matches bindings (match-pattern ?pattern ?temp #f))) (when matches (bind ((result (apply (method (?vars ...) (bind ((?vars ... (values ?vals ...))) ?forms ...)) bindings))) (when result (?return (check-type result ))))))))) ?pattern ?form))) (expression (bind-exit (?return) (bind ((?temp ?expr)) ?matcher ... (error "Invalid syntax: ~S" ?temp)))))))) ;;;; Pattern matchings. ;;; MATCH-PATTERN -- internal. ;;; ;;; Check to see if EXPR matches PATTERN. Returns T and a list of the parts ;;; that correspond to the pattern variables if it matches, or NIL and NIL if ;;; not. ;;; ;;; Basically, descend down through the pattern, checking to make sure the ;;; expression matches at that level. At each recursion, we are passed the ;;; result list for everything that is ``after'' this node in the pattern. ;;; If this pattern node corresponds to a pattern variable, then we just ;;; cons the expression onto the front of the result list and return it. ;;; ;;; When we hit ``ellipsis'' nodes (which correspond to occurances of ... in ;;; the pattern) then we have to recurse, using the same subpattern on each ;;; element of the expression (which must be a list). We will then have ;;; several results, one for each subexpression. For example, if we had the ;;; pattern: ;;; ;;; ((foo bar) ...) ;;; ;;; and applied it to the expression: ;;; ;;; ((foo1 bar1) (foo2 bar2) (foo3 bar3)) ;;; ;;; we would have a list of 3 result lists (identical to the expression). ;;; Well, we want to turn that into a result list with two entries, one for foo ;;; and one for bar. To do this, we iterate over the list of result lists, ;;; using (mapcar #'car ...) to extract all the foos, and then (mapcar #'cdr ;;; ...) to advance the lists. ;;; ;;; But if the ellipsis node is matched against an empty list, then we have to ;;; grovel the subpattern to figure out how many pattern variables there are ;;; because there are no sub-expressions to match against the sub-pattern. ;;; This is what the match-empty function does: it just grovels the subpattern ;;; ``matching'' it against nothing. ;;; (define match-pattern (method (pattern expr) (bind-exit (return) (values #t (match pattern expr '() (method () (return #f '()))))))) (define-method match ((pattern ) (expr ) results punt) (punt)) (define-method match ((pattern ) (expr ) results punt) (if (similar? (literal-constant-object expr) (literal-pattern-literal pattern)) results (punt))) (define-method match ((pattern ) (expr ) results punt) ; (multiple-value-bind ; (expr-defn ignore expr-module) ; (lookup expr *macro-lexenv*) ; (declare (ignore ignore)) ; (multiple-value-bind ; (pattern-defn ignore pattern-module) ; (lookup (name-pattern-name pattern) ; (name-pattern-lexenv pattern)) ; (declare (ignore ignore)) ; (if (if (and expr-defn pattern-defn) ; (flet ((meaning (defn) ; (typecase defn ; (macro-binding ; (macro-binding-macro defn)) ; (macro-info ; (macro-info-macro defn)) ; (t ; defn)))) ; (eq (meaning expr-defn) (meaning pattern-defn))) ; (and (eq expr-module pattern-module) ; (eq (variable-name-symbol expr) ; (variable-name-symbol ; (name-pattern-name pattern))))) ; results (punt)) (define-method match ((pattern ) (expr ) results punt) (pair expr results)) (define-method match ((pattern ) (expr ) results punt) (bind ((pattern-pieces (combination-pattern-pieces pattern)) (expr-pieces (combination-pieces expr))) (match-combination pattern pattern-pieces (initial-state pattern-pieces) expr-pieces (initial-state expr-pieces) results punt))) (define-method match-combination ((pattern ) (pattern-pieces ) pattern-state (expr-pieces ) expr-state results punt) (if pattern-state (if expr-state (match (current-element pattern-pieces pattern-state) (current-element expr-pieces expr-state) (match-combination pattern pattern-pieces (next-state pattern-pieces pattern-state) expr-pieces (next-state expr-pieces expr-state) results punt) punt) (punt)) (match-tail pattern expr-pieces expr-state results punt))) (define-method match-tail ((pattern ) (expr-pieces ) expr-state results punt) (if expr-state (punt) results)) (define-method match-tail ((pattern ) (expr-pieces ) expr-state results punt) (bind ((subpattern (ellipsis-pattern-repeated pattern))) (if expr-state (bind ((list-o-lists (iterate repeat ((state expr-state)) (if state (pair (match subpattern (current-element expr-pieces state) '() punt) (repeat (next-state expr-pieces state))) '())))) (iterate repeat ((states (map initial-state list-o-lists))) (if (first states) (pair (map (curry current-element list-o-lists) states) (repeat (map (curry next-state list-o-lists) states))) results))) (match-empty subpattern results)))) (define-method match-tail ((pattern ) (expr-pieces ) expr-state results punt) (bind ((keywords (keyword-pattern-keywords pattern))) (match-keywords keywords (check-keywords keywords expt-pieces expr-state punt) results))) (define-method check-keywords (keywords expr-pieces expr-state punt) (iterate repeat ((state expr-state)) (if state (bind ((keyword-expr (current-element expr-pieces state)) (keyword (and (instance? keyword-expr ) (literal-constant-object keyword-expr))) (state (next-state expr-pieces state))) (unless state (punt)) (unless (member? keyword keywords) (punt)) (pair (pair keyword (current-element expr-pieces state)) (repeat (next-state expr-pieces state)))) '()))) (define-method match-keywords (keywords key-value-pairs results) (if (empty? keywords) results (pair (bind ((keyword (first keywords)) (entry (any? (method (x) (= (head x) keyword)) key-value-pairs))) (and entry (tail entry))) (match-keywords (tail keywords) key-value-pairs results)))) (define-method match-empty ((pattern ) results) results) (define-method match-empty ((pattern ) results) results) (define-method match-empty ((pattern ) results) (pair #f results)) (define-method match-empty ((pattern ) results) (bind ((pieces (combination-pattern-pieces pattern))) (match-empty-combination pattern pieces (initial-state pieces) results))) (define-method match-empty-combination ((pattern ) (pieces ) state results) (match-empty (current-element pieces state) (match-empty-combination pattern pieces (next-state pieces state) results))) (define-method match-empty-combination ((pattern ) (pieces ) (state (singleton #f)) results) results) (define-method match-empty-combination ((pattern ) (pieces ) (state (singleton #f)) results) (match-empty (ellipsis-pattern-repeated pattern) results)) (define-method match-empty-combination ((pattern ) (pieces ) (state (singleton #f)) results) (for-each ((keyword (keyword-pattern-keywords pattern))) () (set! results (pair #f results))) results)