;;;; -*-scheme-*- ;;;; ;;;; Copyright (C) 1997-1998,2000-2003,2005-2006,2008-2013,2015-2022,2024 ;;;; Free Software Foundation, Inc. ;;;; ;;;; This library is free software: you can redistribute it and/or modify ;;;; it under the terms of the GNU Lesser General Public License as ;;;; published by the Free Software Foundation, either version 3 of the ;;;; License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, but ;;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this program. If not, see ;;;; . ;;; Originally extracted from Chez Scheme Version 5.9f ;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman ;;; Copyright (c) 1992-1997 Cadence Research Systems ;;; Permission to copy this software, in whole or in part, to use this ;;; software for any lawful purpose, and to redistribute this software ;;; is granted subject to the restriction that all copies made of this ;;; software must include this copyright notice in full. This software ;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, ;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY ;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE ;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY ;;; NATURE WHATSOEVER. ;;; This code is based on "Syntax Abstraction in Scheme" ;;; by R. Kent Dybvig, Robert Hieb, and Carl Bruggeman. ;;; Lisp and Symbolic Computation 5:4, 295-326, 1992. ;;; ;;; This file defines Guile's syntax expander and a set of associated ;;; syntactic forms and procedures. For more documentation, see The ;;; Scheme Programming Language, Fourth Edition (R. Kent Dybvig, MIT ;;; Press, 2009), or the R6RS. ;;; This file is shipped along with an expanded version of itself, ;;; psyntax-pp.scm, which is loaded when psyntax.scm has not yet been ;;; compiled. In this way, psyntax bootstraps off of an expanded ;;; version of itself. ;;; Implementation notes: ;;; Objects with no standard print syntax, including objects containing ;;; cycles and syntax object, are allowed in quoted data as long as they ;;; are contained within a syntax form or produced by datum->syntax. ;;; Such objects are never copied. ;;; All identifiers that don't have macro definitions and are not bound ;;; lexically are assumed to be global variables. ;;; Top-level definitions of macro-introduced identifiers are allowed. ;;; When changing syntax representations, it is necessary to support ;;; both old and new syntax representations in id-var-name. It ;;; should be sufficient to recognize old representations and treat ;;; them as not lexically bound. (eval-when (compile) (set-current-module (resolve-module '(guile)))) (let ((syntax? (module-ref (current-module) 'syntax?)) (make-syntax (module-ref (current-module) 'make-syntax)) (syntax-expression (module-ref (current-module) 'syntax-expression)) (syntax-wrap (module-ref (current-module) 'syntax-wrap)) (syntax-module (module-ref (current-module) 'syntax-module)) (syntax-sourcev (module-ref (current-module) 'syntax-sourcev))) (define-syntax define-expansion-constructors (lambda (x) (syntax-case x () ((_) (let lp ((n 0) (out '())) (if (< n (vector-length %expanded-vtables)) (lp (1+ n) (let* ((vtable (vector-ref %expanded-vtables n)) (stem (struct-ref vtable (+ vtable-offset-user 0))) (fields (struct-ref vtable (+ vtable-offset-user 2))) (sfields (map (lambda (f) (datum->syntax x f)) fields)) (ctor (datum->syntax x (symbol-append 'make- stem)))) (cons #`(define (#,ctor #,@sfields) (make-struct/simple (vector-ref %expanded-vtables #,n) #,@sfields)) out))) #`(begin #,@(reverse out)))))))) (define-syntax define-expansion-accessors (lambda (x) (syntax-case x () ((_ stem field ...) (let ((stem (syntax->datum #'stem)) (fields (map syntax->datum #'(field ...)))) (let lp ((n 0)) (let ((vtable (vector-ref %expanded-vtables n))) (if (eq? (struct-ref vtable (+ vtable-offset-user 0)) stem) (let ((pred (datum->syntax x (symbol-append stem '?))) (all-fields (struct-ref vtable (+ vtable-offset-user 2)))) #`(begin (define (#,pred x) (and (struct? x) (eq? (struct-vtable x) (vector-ref %expanded-vtables #,n)))) #,@(map (lambda (f) (define get (datum->syntax x (symbol-append stem '- f))) (define idx (list-index all-fields f)) #`(define (#,get x) (struct-ref x #,idx))) fields))) (lp (1+ n)))))))))) (define-expansion-constructors) (define-expansion-accessors lambda src meta body) ;; A simple pattern matcher based on Oleg Kiselyov's pmatch. (define-syntax-rule (simple-match e cs ...) (let ((v e)) (simple-match-1 v cs ...))) (define-syntax simple-match-1 (syntax-rules () ((_ v) (error "value failed to match" v)) ((_ v (pat e0 e ...) cs ...) (let ((fk (lambda () (simple-match-1 v cs ...)))) (simple-match-pat v pat (let () e0 e ...) (fk)))))) (define-syntax simple-match-patv (syntax-rules () ((_ v idx () kt kf) kt) ((_ v idx (x . y) kt kf) (simple-match-pat (vector-ref v idx) x (simple-match-patv v (1+ idx) y kt kf) kf)))) (define-syntax simple-match-pat (syntax-rules (_ quote unquote ? and or not) ((_ v _ kt kf) kt) ((_ v () kt kf) (if (null? v) kt kf)) ((_ v #t kt kf) (if (eq? v #t) kt kf)) ((_ v #f kt kf) (if (eq? v #f) kt kf)) ((_ v (and) kt kf) kt) ((_ v (and x . y) kt kf) (simple-match-pat v x (simple-match-pat v (and . y) kt kf) kf)) ((_ v (or) kt kf) kf) ((_ v (or x . y) kt kf) (let ((tk (lambda () kt))) (simple-match-pat v x (tk) (simple-match-pat v (or . y) (tk) kf)))) ((_ v (not pat) kt kf) (simple-match-pat v pat kf kt)) ((_ v (quote lit) kt kf) (if (eq? v (quote lit)) kt kf)) ((_ v (? proc) kt kf) (simple-match-pat v (? proc _) kt kf)) ((_ v (? proc pat) kt kf) (if (proc v) (simple-match-pat v pat kt kf) kf)) ((_ v (x . y) kt kf) (if (pair? v) (let ((vx (car v)) (vy (cdr v))) (simple-match-pat vx x (simple-match-pat vy y kt kf) kf)) kf)) ((_ v #(x ...) kt kf) (if (and (vector? v) (eq? (vector-length v) (length '(x ...)))) (simple-match-patv v 0 (x ...) kt kf) kf)) ((_ v var kt kf) (let ((var v)) kt)))) (define-syntax-rule (match e cs ...) (simple-match e cs ...)) (define (top-level-eval x mod) (primitive-eval x)) (define (local-eval x mod) (primitive-eval x)) (define (global-extend type sym val) (module-define! (current-module) sym (make-syntax-transformer sym type val))) (define (sourcev-filename s) (vector-ref s 0)) (define (sourcev-line s) (vector-ref s 1)) (define (sourcev-column s) (vector-ref s 2)) (define (sourcev->alist sourcev) (define (maybe-acons k v tail) (if v (acons k v tail) tail)) (and sourcev (maybe-acons 'filename (sourcev-filename sourcev) `((line . ,(sourcev-line sourcev)) (column . ,(sourcev-column sourcev)))))) (define (maybe-name-value name val) (if (lambda? val) (let ((meta (lambda-meta val))) (if (assq 'name meta) val (make-lambda (lambda-src val) (acons 'name name meta) (lambda-body val)))) val)) ;; output constructors (define build-void make-void) (define build-call make-call) (define build-conditional make-conditional) (define build-lexical-reference make-lexical-ref) (define (build-lexical-assignment src name var exp) (make-lexical-set src name var (maybe-name-value name exp))) (define (analyze-variable mod var modref-cont bare-cont) (match mod (#f (bare-cont #f var)) (('public . mod) (modref-cont mod var #t)) (((or 'private 'hygiene) . mod) (if (equal? mod (module-name (current-module))) (bare-cont mod var) (modref-cont mod var #f))) (('primitive . _) (syntax-violation #f "primitive not in operator position" var)))) (define (build-global-reference src var mod) (analyze-variable mod var (lambda (mod var public?) (make-module-ref src mod var public?)) (lambda (mod var) (make-toplevel-ref src mod var)))) (define (build-global-assignment src var exp mod) (let ((exp (maybe-name-value var exp))) (analyze-variable mod var (lambda (mod var public?) (make-module-set src mod var public? exp)) (lambda (mod var) (make-toplevel-set src mod var exp))))) (define (build-global-definition src mod var exp) (make-toplevel-define src (and mod (cdr mod)) var (maybe-name-value var exp))) (define (build-simple-lambda src req rest vars meta exp) (make-lambda src meta (make-lambda-case ;; src req opt rest kw inits vars body else src req #f rest #f '() vars exp #f))) (define build-case-lambda make-lambda) (define build-lambda-case make-lambda-case) (define build-primcall make-primcall) (define build-primref make-primitive-ref) (define build-data make-const) (define (build-sequence src exps) (match exps ((tail) tail) ((head . tail) (make-seq src head (build-sequence #f tail))))) (define (build-let src ids vars val-exps body-exp) (match (map maybe-name-value ids val-exps) (() body-exp) (val-exps (make-let src ids vars val-exps body-exp)))) (define (build-named-let src ids vars val-exps body-exp) (match vars ((f . vars) (match ids ((f-name . ids) (let ((proc (build-simple-lambda src ids #f vars '() body-exp))) (make-letrec src #f (list f-name) (list f) (list (maybe-name-value f-name proc)) (build-call src (build-lexical-reference src f-name f) (map maybe-name-value ids val-exps))))))))) (define (build-letrec src in-order? ids vars val-exps body-exp) (match (map maybe-name-value ids val-exps) (() body-exp) (val-exps (make-letrec src in-order? ids vars val-exps body-exp)))) (define (gen-lexical id) ;; Generate a unique symbol for a lexical variable. These need to ;; be symbols as they are embedded in Tree-IL. Lexicals from ;; different separately compiled modules can coexist, for example ;; if a macro defined in module A is used in a separately-compiled ;; module B, so they do need to be unique. However we assume that ;; generally a module corresponds to a compilation unit, so there ;; is no need to be unique across separately-compiled instances of ;; the same module, and that therefore we can use a deterministic ;; per-module counter instead of the global counter of 'gensym' so ;; that the generated identifier is reproducible. (module-gensym (symbol->string id))) (define no-source #f) (define (datum-sourcev datum) (let ((props (source-properties datum))) (and (pair? props) (vector (assq-ref props 'filename) (assq-ref props 'line) (assq-ref props 'column))))) (define (source-annotation x) (if (syntax? x) (syntax-sourcev x) (datum-sourcev x))) (define-syntax-rule (arg-check pred? e who) (let ((x e)) (unless (pred? x) (syntax-violation who "invalid argument" x)))) ;; compile-time environments ;; wrap and environment comprise two level mapping. ;; wrap : id --> label ;; env : label --> ;; environments are represented in two parts: a lexical part and a ;; global part. The lexical part is a simple list of associations ;; from labels to bindings. The global part is implemented by ;; Guile's module system and associates symbols with bindings. ;; global (assumed global variable) and displaced-lexical (see below) ;; do not show up in any environment; instead, they are fabricated by ;; resolve-identifier when it finds no other bindings. ;; ::= ((