aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2025-06-28 03:19:13 -0400
committerGravatar Peter McGoron 2025-06-28 03:19:13 -0400
commit3aa30b3bf919b141888c30a6dcdd7ea7bdc051df (patch)
tree6b93b645b78376d58baa243cd423535165b9f369
parentreenable tests (diff)
first pass at a syntax expander for pure LC
Diffstat (limited to '')
-rw-r--r--multisyntax/examples/untyped-lambda-calculus-prelude.scm282
-rw-r--r--multisyntax/examples/untyped-lambda-calculus.scm326
-rw-r--r--multisyntax/examples/untyped-lambda-calculus.sld23
-rw-r--r--multisyntax/syntax-object.scm63
-rw-r--r--multisyntax/syntax-object.sld13
-rw-r--r--test/run.scm23
6 files changed, 708 insertions, 22 deletions
diff --git a/multisyntax/examples/untyped-lambda-calculus-prelude.scm b/multisyntax/examples/untyped-lambda-calculus-prelude.scm
new file mode 100644
index 0000000..ad9d2a1
--- /dev/null
+++ b/multisyntax/examples/untyped-lambda-calculus-prelude.scm
@@ -0,0 +1,282 @@
+;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Untyped lambda calculus using normal-order evaluation.
+;;;
+;;; The following defines a Scheme-like pure untyped curried lambda
+;;; calculus evaluated in normal order. "Pure" means there are only
+;;; functions: numbers, pairs, booleans, etc. are Church encoded.
+;;; Numbers are expanded into Church numerals by the expander.
+
+;;; ;;;;;;;;;;;;;
+;;; Fundamentals
+
+(define-syntax splicing-begin
+ (syntax-rules ()
+ ((_ body ...) (splicing-let-syntax () body ...))))
+
+(define I (lambda x x))
+
+(splicing-let-syntax ((lambda lambda))
+ ;; This binds `lambda` in the global syntatic environment into a
+ ;; local, immutable syntatic environment.
+ (define-syntax begin
+ (syntax-rules ()
+ ((begin) I)
+ ((begin x y ...)
+ ((lambda dummy (begin y ...)) x)))))
+
+(splicing-let-syntax ((%lambda lambda))
+ ;; This binds `%lambda` because `lambda` will be overridden in the
+ ;; global syntatic environment.
+ (define-syntax lambda
+ (syntax-rules ()
+ ((_ (formal1 formal-rest ...) body ...)
+ (lambda formal1 (lambda (formal-rest ...) body ...)))
+ ((_ (formal) body ...)
+ (lambda formal body ...))
+ ((_ formal body ...)
+ (%lambda formal (begin body ...))))))
+
+(define-syntax λ lambda)
+
+(define-syntax let
+ ;; Regular `let`. Named `let` is defined later.
+ (syntax-rules ()
+ ((let () body ...)
+ ((λ (dummy) body ...) I))
+ ((let ((name value) ...) body ...)
+ ((λ (name ...) body ...) value ...))))
+
+(define-syntax let* let)
+
+(define (Y f)
+ ;; Y combinator.
+ (let ((recursor (λ (x) (f (x x)))))
+ (recursor recursor)))
+
+(define-syntax rec
+ ;; Define a recursive function.
+ (syntax-rules ()
+ ((rec (name . formals) body ...)
+ (Y (λ (name . formals) body ...)))))
+
+(define-syntax υ rec)
+
+(splicing-let-syntax ((%let let))
+ ;; Named `let`.
+ (define-syntax let
+ (syntax-rules ()
+ ((let ((name value) ...) body ...)
+ (%let ((name value) ...) body ...))
+ ((let name ((param first-binding) ...) body ...)
+ ((υ (name param ...) body ...)
+ first-binding ...)))))
+
+(define-syntax letrec
+ (syntax-rules ()
+ ((letrec ((name value) ...) body ...)
+ (let ((name (λ (name ...) value)) ...)
+ (let ((name (name name ...)) ...)
+ body ...)))))
+
+(splicing-let-syntax ((%define define))
+ (define-syntax define
+ (syntax-rules ()
+ ((define (name . args) body ...)
+ (define name (rec (name . args) body ...)))
+ ((define name body ...)
+ (%define name (letrec ((name (begin body ...))) name))))))
+
+(define-syntax binary-to-arbitrary
+ ;; Convert a binary procedure to a syntatic procedure of arbitrary
+ ;; arguments.
+ (syntax-rules ()
+ ((_ name binary)
+ (define-syntax name
+ (syntax-rules ...* ()
+ ((_ x) x)
+ ((_ x y ...*) (binary x (name y ...*))))))))
+
+(define-syntax let-lowered
+ (syntax-rules (define)
+ ((_ ((name value) ...) (define d-name d-value ...))
+ (splicing-begin
+ (define d-name (let ((name value) ...) d-value ...))
+ ...))))
+
+;;; Boolean logic. `#t` maps to this definition of true, and `#f` maps to
+;;; this definition of false.
+(define (true x y) x)
+(define (false x y) y)
+(define (not x) (x false true))
+
+(define (binary-and x y) (x y false))
+(binary-to-arbitrary and binary-and)
+
+(define (binary-or x y) (x true y))
+(binary-to-arbitrary or binary-or)
+
+(define (if predicate truthy falsy)
+ (predicate truthy falsy))
+
+(define-syntax cond
+ ;; Simplified cond.
+ (syntax-rules (else)
+ ((_ (predicate expression ...)
+ rest ...)
+ (if predicate
+ (begin expression ...)
+ (case rest ...)))
+ ((_ (else expression ...))
+ (begin expression ...))))
+
+;;; Church numeral operations
+
+(define (S n)
+ (λ (f x) (f (n f x))))
+
+(define (binary+ x y) (x S n))
+(binary-to-arbitrary + binary+)
+
+(define (binary* x y) (λ (f) (m (n f))))
+(binary-to-arbitary * binary*)
+
+(define (expt b n) (n b))
+
+;;; Pairs and lists
+
+(define (cons car cdr)
+ (lambda (f) (f car cdr)))
+(define null false)
+
+(define-syntax list
+ (syntax-rules ()
+ ((list) null)
+ ((list x y ...)
+ (cons x (list y ...)))))
+
+(define-syntax cons*
+ (syntax-rules ()
+ ((cons* x y) (cons x y))
+ ((cons* x y z ...)
+ (cons x (cons* y z ...)))))
+
+(define (car pair) (pair true))
+(define (cdr pair) (pair false))
+
+(define (null? x) (x (λ (h t d) false) true))
+
+(define (fold kons nil list)
+ ;; This is a left-fold: i.e.
+ ;; (kons (kons (... (kons (car list) nil))))
+ ;;
+ ;; This is the fundamental list iterator that all other list functions
+ ;; are defined with.
+ (if (null? list)
+ nil
+ (fold kons (kons (car list) nil) (cdr list))))
+
+(define (reverse-append list1 list2)
+ (fold cons list2 list1))
+(define (reverse list) (reverse-append list1 null))
+
+(define (fold-right kons nil list)
+ (fold kons nil (reverse list)))
+
+(define (append list1 list2) (fold-right cons list2 list1))
+
+(define (append-n list-of-lists)
+ (fold-right (λ (list acc) (append list acc))
+ null
+ list-of-lists))
+
+(let-lowered ((mapper (λ (car cdr) (cons (f car) cdr))))
+ (define (map-reverse f list) (fold mapper null list))
+ (define (map f list) (fold-right mapper null list)))
+
+(define (any f list)
+ (fold (λ (value previous) (or previous (f value))) false list))
+(define (all f list)
+ (fold (λ (value previous) (and previous (f value))) true list))
+
+(define (length list) (fold (lambda (_ n) (+ n 1)) 0 list))
+
+(define (iota-fold kons nil count start step)
+ ;; `iota` cannot be defined in terms of `fold`s.
+ (if (<= count 0)
+ nil
+ (iota-fold kons
+ (kons start nil)
+ (- count 1)
+ (+ start step)
+ step)))
+
+(define (reverse-iota count start step)
+ (iota-fold cons null count start step))
+(define (iota count start step)
+ (reverse (reverse-iota count start step)))
+
+(define (make-list n fill)
+ (iota-fold (λ (_ cdr) (cons fill cdr)) null n 0 1))
+
+(define (list-tabulate n init-proc)
+ (iota-fold (λ (i cdr) (cons (init-proc i) cdr)) null n 0 1))
+
+(define (list-tail n list failure)
+ (let ((length (length list)))
+ (if (>= n length)
+ (failure n)
+ (iota-fold (lambda (_ list) list) n 0 1))))
+
+(define (list-ref n list failure)
+ (car (list-tail n list (lambda (n)
+ (cons (failure n) nil)))))
+
+(define (list-drop list i)
+ (iota-fold (λ (_ cdr) cdr) list i 0 1))
+
+(define (list-take list i)
+ (reverse (list-drop (reverse list) (- (length list) i))))
+
+(define (list-take-right list i)
+ (list-drop list (- (length list) i)))
+
+(define (list-drop-right list i)
+ (list-take list (- (length list) i)))
+
+(define (last list default)
+ (fold (λ (kar knil) kar) default list))
+
+(define (transverse list-of-lists)
+ (let ((init-list (list-tabluate (length (car list-of-lists)) null))
+ (add-element
+ (λ (el state)
+ (let ((n (list-ref state 0))
+ (acc (list-ref state 1)))
+ (list (+ n 1)
+ (append (list-take acc n)
+ (cons el (list-ref acc n))
+ (list-drop acc (+ n 1))))))))
+ (fold-right (λ (l state)
+ (fold-right add-element l (list 0 state)))
+ init-list
+ list-of-lists)))
+
+(define (foldn f init list-of-lists)
+ (fold (λ (elements init)
+ (letrec ((loop
+ (λ (f elements init)
+ (if (null? elements)
+ (f init)
+ (loop (f (car elements)) (cdr elements) init)))))
+ (loop f elements init)))
+ init
+ (transverse list-of-lists)))
+
+(define (list= = l1 l2)
+ (fold-n (λ (e1 e2 result)
+ (cond
+ ((not result) result)
+ ((= e1 e2) true)
+ (else false)))
+ (list l1 l2)))
+
diff --git a/multisyntax/examples/untyped-lambda-calculus.scm b/multisyntax/examples/untyped-lambda-calculus.scm
new file mode 100644
index 0000000..feea5f5
--- /dev/null
+++ b/multisyntax/examples/untyped-lambda-calculus.scm
@@ -0,0 +1,326 @@
+#| Copyright (c) Peter McGoron 2025
+|
+ | Licensed under the Apache License, Version 2.0 (the "License");
+ | you may not use this file except in compliance with the License.
+ | You may obtain a copy of the License at
+ |
+ | http://www.apache.org/licenses/LICENSE-2.0
+ |
+ | Unless required by applicable law or agreed to in writing, software
+ | distributed under the License is distributed on an "AS IS" BASIS,
+ | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ | See the License for the specific language governing permissions and
+ | limitations under the License.
+ |------------------------------------------------------------------------
+ | Example implementation of macros for an untyped lambda calculus.
+ |
+ | Syntax of the core:
+ |
+ | TERM ::= (lambda ID TERM) | (TERM TERM+) | ID
+ |
+ | where `(TERM1 TERM2 TERM3 TERM4 ...)` is interpreted as
+ | `((TERM1 TERM2) TERM3 TERM4 ...)`.
+ |
+ | Syntax with macros:
+ |
+ | TOPLEVEL ::= (define-syntax ID TFMR)
+ | | (define ID TERM)
+ | | (splicing-let-syntax ((ID TFMR) ...) TOPLEVEL ...)
+ | | (splicing-letrec-syntax ((ID TFMR) ...) TOPLEVEL ...)
+ | | EXPR
+ | EXPR ::= (let-syntax ((ID TFMR) ...) EXPR)
+ | | (letrec-syntax ((ID TFMR) ...) EXPR)
+ | | (lambda ID EXPR)
+ | | (EXPR EXPR EXPR ...)
+ | | ID
+ | TFMR ::= ID
+ | | (let-syntax ((ID TFMR) ...) TFMR)
+ | | (letrec-syntax ((ID TFMR) ...) TFMR)
+ | | (syntax-rules ID? (ID ...) ((ID . pattern) PROD) ... )
+ |
+ | The lexical environment is made up of either the symbol `variable`
+ | (meaning that the identifier is some bound variable), a value
+ | satisfying `transformer?` (a syntax transformer), or a symbol for a
+ | primitive syntatic transformer (`'lambda` for `lambda`, etc).
+ |#
+
+(define-record-type <syntax-rules>
+ ;; `clauses` is a list of cons cells, the car of each cell is the matcher
+ ;; and the cdr of each cell is the producer.
+ (wrap-syntax-rules clauses)
+ transformer?
+ (clauses unwrap-syntax-rules))
+
+(define (empty-map) (hashmap bound-identifier-comparator))
+
+(define initial-environment
+ (hashmap bound-identifier-comparator
+ (empty-wrap 'lambda) 'lambda
+ (empty-wrap 'define) 'define
+ (empty-wrap 'define-syntax) 'define-syntax
+ (empty-wrap 'splicing-let-syntax) 'splicing-let-syntax
+ (empty-wrap 'splicing-letrec-syntax) 'splicing-letrec-syntax
+ (empty-wrap 'let-syntax) 'let-syntax
+ (empty-wrap 'letrec-syntax) 'letrec-syntax
+ (empty-wrap 'syntax-rules) 'syntax-rules))
+
+(define (church-numeral stx)
+ ;; Convert the exact non-negative integer `stx` into a Church numeral.
+ (let ((function (generate-identifier 'f))
+ (argument (generate-identifier 'x)))
+ (list (empty-wrap 'lambda)
+ function
+ (list (empty-wrap 'lambda)
+ argument
+ (let loop ((i stx))
+ (if (zero? i)
+ argument
+ (list function
+ (loop (- i 1)))))))))
+
+(define (on-bindings stx)
+ ;; Given (_ ((name value) ...) body ...), return
+ ;;
+ ;; 1. `name ...`,
+ ;; 2. `tmp ...`, the same length as `name ...`, which are the names with
+ ;; new lexical locations.
+ ;; 3. `value ...`.
+ ;; 4. `body ...`
+ ;;
+ (let* ((stx (unwrap-list stx))
+ (binders (unwrap-list (syntax-cxr '(d a) stx)))
+ (old-names (map syntax-car binders))
+ (new-lls (generate-lexical-locations old-names)))
+ (values old-names
+ (map (lambda (old-name ll)
+ (add-substitution old-name old-name ll))
+ old-names
+ new-lls)
+ (map (lambda (form) (syntax-cxr '(d a) form))
+ binders)
+ (syntax-cxr '(d d) stx))))
+
+(define (union-names env new-names tfmrs)
+ ;; Add `new-names` bound to `tfmrs` in `env`, overriding previous
+ ;; bindings.
+ (hashmap-union (alist->hashmap bound-identifier-comparator
+ (map cons new-names tfmrs))
+ env))
+
+(define (is? env stx id)
+ ;; Return true if `stx` in `env` is `eq?` to `id`.
+ (and (identifier? (syntax-car stx))
+ (let ((resolved (hashmap-ref/default env (syntax-car stx) #f)))
+ (eq? resolved id))))
+
+(define (identifier-is-transformer env stx)
+ ;; Returns transformer if `stx` is a syntax-rules transformer in `env`.
+ (cond
+ ((not (identifier? (syntax-car stx))) #f)
+ ((hashmap-ref/default env (syntax-car stx) #f)
+ => (lambda (return)
+ (and (transformer? return) return)))
+ (else #f)))
+
+(define (let-syntax-expander env stx K)
+ ;; Continuation-passing-style expansion of `let-syntax`. Expands the
+ ;; body of the `let-syntax` form using the continuation `K`, with an
+ ;; environment binding the transformers to names as defined by the
+ ;; `let-syntax` declaration.
+ (let*-values (((old-names new-names tfmrs body) (on-bindings stx))
+ ((tfmrs) (map (lambda (stx) (expand-transformer env stx))
+ tfmrs)))
+ (K (union-names env new-names tfmrs)
+ (add-substitution (syntax-cxr '(a) body)
+ old-names
+ new-names))))
+
+(define (letrec-syntax-expander env stx K)
+ ;; CPS expansion of `letrec-syntax`. See `let-syntax-expander`.
+ (let*-values (((old-names new-names tfmrs body) (on-bindings stx))
+ ((tfmrs)
+ (map (lambda (stx)
+ (expand-transformer env
+ (add-substitution
+ stx
+ old-names
+ new-names)))
+ tfmrs)))
+ (K (union-names env new-names tfmrs)
+ (add-substitution (syntax-cxr '(a) stx)
+ old-names
+ new-names))))
+
+(define (eval-transformer tfmr stx)
+ ;; Try to match each pattern in `tfmr`, and when one matches, call the
+ ;; producer on the matched data.
+ (let loop ((tfmr (unwrap-syntax-rules tfmr)))
+ (if (null? tfmr)
+ (error "no matched pattern" stx tfmr)
+ (let ((matcher (caar tfmr))
+ (producer (cdar tfmr)))
+ (cond
+ ((matcher tfmr) => producer)
+ (else (loop (cdr tfmr))))))))
+
+(define (macro-expand-expander env stx tfmr K)
+ ;; Evaluate the transformer `tfmr` with `stx`, properly adding and
+ ;; removing macro expansion timesteps. Pass the result to `K`, which
+ ;; is a function of one argument (not two like the `let-syntax-expander`
+ ;; procedures).
+ (let ((ts (generate-timestamp)))
+ (K (add-timestamp (eval-transformer tfmr
+ (add-timestamp stx ts))
+ ts))))
+
+(define (expand-expr env stx)
+ ;; TODO: fix function application
+ ;; Expander of expressions (not toplevel statements).
+ (let ((stx (unwrap-syntax stx)))
+ (cond
+ ((and (exact-integer? stx) (positive? stx))
+ (church-numeral stx))
+ ((identifier? stx) stx)
+ ((is? env stx 'lambda)
+ (let* ((bound (syntax-cxr '(d a) stx))
+ (renamed (generate-identifier (syntax->datum bound)))
+ (body (syntax-cxr '(d d a) stx)))
+ (list (empty-wrap 'lambda)
+ renamed
+ (expand-expr
+ (hashmap-adjoin env renamed 'variable)
+ (add-substitution body renamed bound)))))
+ ((is? env stx 'let-syntax)
+ (let-syntax-expander env stx expand-expr))
+ ((is? env stx 'letrec-syntax)
+ (letrec-syntax-expander env stx expand-expr))
+ ((identifier-is-transformer env stx)
+ => (lambda (tfmr)
+ (macro-expand-expander env
+ stx
+ tfmr
+ (lambda (stx)
+ (expand-expr env stx)))))
+ ((pair? stx)
+ (cons (expand-expr env (car stx)) (expand-expr env (cdr stx))))
+ (else (error "invalid syntax" stx)))))
+
+(define (expand-syntax-rules env ellipsis literals clauses)
+ ;; Expand a `syntax-rules` transformer and wrap it as a `syntax-rules`
+ ;; object.
+ (define (operate clause)
+ (let*-values (((clause) (unwrap-list clause))
+ ((matcher bindings _)
+ (compile-pattern literals
+ (list-ref clause 0)
+ ellipsis)))
+ (cons matcher (compile-producer literals
+ (list-ref clause 1)
+ bindings
+ ellipsis))))
+ (wrap-syntax-rules (map operate (unwrap-list clauses))))
+
+(define (expand-transformer env stx)
+ (let ((stx (unwrap-syntax stx)))
+ (cond
+ ((identifier? stx)
+ (hashmap-ref env stx (lambda () (error "transformer not found" stx))))
+ ((identifier-is-transformer env stx)
+ => (lambda (tfmr)
+ (macro-expand-expander env
+ stx
+ tfmr
+ (lambda (stx)
+ (expand-transformer env stx)))))
+ ((is? env stx 'syntax-rules)
+ (if (identifier? (syntax-cxr '(d a) stx))
+ (expand-syntax-rules env
+ (syntax-cxr '(d a) stx)
+ (syntax-cxr '(d d a) stx)
+ (syntax-cxr '(d d d) stx))
+ (expand-syntax-rules env
+ #f
+ (syntax-cxr '(d a) stx)
+ (syntax-cxr '(d d) stx))))
+ ((is? env stx 'let-syntax)
+ (let-syntax-expander env stx expand-transformer))
+ ((is? env stx 'letrec-syntax)
+ (letrec-syntax-expander env stx expand-transformer))
+ (else (error "invalid syntax for transformer" stx)))))
+
+(define (accumulate-splicing globalenv lexenv body)
+ ;; Expand each toplevel declaraion in `body` with the lexical environment
+ ;; `lexenv` with an accumulated global environment `globalenv`.
+ ;;
+ ;; Returns `(values globalenv acc)` which is the expanded body clauses
+ ;; and the accumulated global environment.
+ (let loop ((globalenv globalenv)
+ (iter (unwrap-list body))
+ (acc '()))
+ (if (null? iter)
+ (values globalenv (reverse acc))
+ (let-values (((globalenv next)
+ (expand-toplevel globalenv lexenv (car iter))))
+ (loop globalenv (cdr iter) (append-reverse next acc))))))
+
+(define (expand-toplevel globalenv lexenv stx)
+ ;; Expands toplevel expressions with accumulated global environment
+ ;; `globalenv`.
+ (let ((stx (unwrap-syntax stx))
+ (env (hashmap-union lexenv globalenv)))
+ (cond
+ ((is? env stx 'define-syntax)
+ (let* ((name (syntax-cxr '(d a) stx))
+ (tfmr (expand-transformer env (syntax-cxr '(d d a) stx))))
+ (values (hashmap-adjoin globalenv name tfmr) '())))
+ ((is? env stx 'splicing-let-syntax)
+ (let*-values (((old-names new-names tfmrs body)
+ (on-bindings stx))
+ ((tfmrs) (map (lambda (stx)
+ (expand-transformer env stx))
+ tfmrs)))
+ (accumulate-splicing globalenv
+ (union-names lexenv new-names tfmrs)
+ body)))
+ ((is? env stx 'splicing-letrec-syntax)
+ (let*-values (((old-names new-names tfmrs body) (on-bindings stx))
+ ((tfmrs) (map (lambda (stx)
+ (expand-transformer env
+ (add-substitution
+ stx
+ old-names
+ new-names)))
+ tfmrs)))
+ (accumulate-splicing globalenv
+ (union-names lexenv new-names tfmrs)
+ body)))
+ ((is? env stx 'define)
+ (let* ((name (syntax-cxr '(d a) stx))
+ (expanded-value (expand-expr env (syntax-cxr '(d d a) stx))))
+ (values (hashmap-adjoin globalenv name 'variable)
+ (list (list (empty-wrap 'define)
+ name
+ expanded-value)))))
+ ((identifier-is-transformer env stx)
+ => (lambda (tfmr)
+ (macro-expand-expander env
+ stx
+ tfmr
+ (lambda (stx)
+ (expand-toplevel globalenv lexenv stx)))))
+ (else (values globalenv
+ (list
+ (expand-expr (hashmap-union lexenv globalenv) stx)))))))
+
+(define (expand initenv stx)
+ ;; Expand `stx`, which is a list of syntax forms, into a list of syntax
+ ;; forms, with initial environment `initenv`. Returns the new environment
+ ;; and the list of expanded forms.
+ (define (fold globalenv stxlist acc)
+ (if (null? stxlist)
+ (values globalenv (reverse acc))
+ (let-values (((globalenv next)
+ (expand-toplevel globalenv (empty-map) (car stxlist))))
+ (fold globalenv (cdr stxlist) (append-reverse next acc)))))
+ (fold initenv (unwrap-list stx) '()))
+
diff --git a/multisyntax/examples/untyped-lambda-calculus.sld b/multisyntax/examples/untyped-lambda-calculus.sld
new file mode 100644
index 0000000..2ace753
--- /dev/null
+++ b/multisyntax/examples/untyped-lambda-calculus.sld
@@ -0,0 +1,23 @@
+#| Copyright (c) Peter McGoron 2025
+ |
+ | Licensed under the Apache License, Version 2.0 (the "License");
+ | you may not use this file except in compliance with the License.
+ | You may obtain a copy of the License at
+ |
+ | http://www.apache.org/licenses/LICENSE-2.0
+ |
+ | Unless required by applicable law or agreed to in writing, software
+ | distributed under the License is distributed on an "AS IS" BASIS,
+ | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ | See the License for the specific language governing permissions and
+ | limitations under the License.
+ |#
+
+(define-library (multisyntax examples untyped-lambda-calculus)
+ (import (scheme base) (scheme write)
+ (srfi 1) (srfi 146 hash)
+ (multisyntax syntax-object)
+ (multisyntax pattern matcher)
+ (multisyntax pattern producer))
+ (export expand transformer? initial-environment)
+ (include "untyped-lambda-calculus.scm")) \ No newline at end of file
diff --git a/multisyntax/syntax-object.scm b/multisyntax/syntax-object.scm
index c55b4e1..2bd2060 100644
--- a/multisyntax/syntax-object.scm
+++ b/multisyntax/syntax-object.scm
@@ -66,6 +66,12 @@
(raw-lexical-location symbol
(generate-unique-integer)))
+(define (generate-lexical-locations list)
+ (do ((acc (list-accumulator))
+ (list (unwrap-list list) (cdr list)))
+ ((null? list) (acc (eof-object)))
+ (acc (generate-lexical-location (syntax->datum (car list))))))
+
(define (lexical-location->string ll)
(string-append (symbol->string (lexical-location->symbol ll))
"."
@@ -209,7 +215,6 @@
((mapping-ref/default invenv location-from '())
=> (lambda (lst)
(for-each (lambda (maps-to-location-from)
- (display "a\n")
(set! env (mapping-set
env
maps-to-location-from
@@ -234,6 +239,12 @@
;;
;; Otherwise update `environment` and `inverse-environment` with the
;; new locations.
+ ;;
+ ;; If `location-to` is a list, then `id` must be a list too. Each
+ ;; pair `(id location-to)` is added as a substitution.
+ ;;
+ ;; If `location-to` is an identifier, the location that it resolves to
+ ;; is added as a substitution.
(cond
((pair? stx) (cons (add-substitution (car stx)
id
@@ -245,19 +256,25 @@
stx))
((self-syntax? stx) stx)
(else
- (let ((timestamps (wrap->timestamps stx)))
- (if (not (set=? timestamps (wrap->timestamps id)))
- stx
- (add-timestamps/same-wrap stx id location-to))))))
+ (let operate ((id id)
+ (location-to location-to)
+ (stx stx))
+ (cond
+ ((pair? location-to)
+ (fold operate stx id location-to))
+ ((identifier? location-to)
+ (operate id (resolve location-to) stx))
+ ((not (set=? (wrap->timestamps stx) (wrap->timestamps id)))
+ stx)
+ (else
+ (add-timestamps/same-wrap stx id location-to)))))))
(define (generate-unique-symbol)
;; Tries as best as possible to generate a unique symbol. Not read/write
;; invariant. An actual implementation of this procedure would require
;; implementation support.
(string->symbol
- (string-append "gensym."
- (number->string
- (generate-unique-integer)))))
+ (string-append "gensym." (number->string (generate-unique-integer)))))
(define (identifier-lexically-bound? id)
;; Returns true if `id` was bound by some lexical construct. Returns
@@ -279,11 +296,12 @@
(define (generate-temporaries lst)
;; Generate a list of identifiers from `generate-identifier`.
- (let loop ((lst (unwrap-syntax lst))
- (acc '()))
- (if (null? lst)
- '()
- (loop (unwrap-syntax (cdr list)) (cons (generate-identifier) acc)))))
+ (do ((acc (list-accumulator))
+ (lst (unwrap-list lst) (cdr lst)))
+ ((null? lst) (acc (eof-object)))
+ (if (identifier? (car lst))
+ (acc (generate-identifier (syntax->datum (car lst))))
+ (acc (generate-identifier)))))
(define (symbolic-identifier=? id1 id2)
;; Returns true if the underlying symbol of each identifier is the same.
@@ -415,3 +433,22 @@
((self-syntax? datum) datum)
((if-contains-wrap operate datum) => values)
(else (push-wrap context-id datum))))
+
+(define (syntax-cxr list stx)
+ (if (null? list)
+ stx
+ (let ((stx (unwrap-syntax stx)))
+ (case (car list)
+ ((a) (syntax-cxr (cdr list) (car stx)))
+ ((d) (syntax-cxr (cdr list) (cdr stx)))
+ (else (error "invalid accessor" list stx))))))
+
+(define (syntax-car stx) (syntax-cxr '(a) stx))
+(define (syntax-cdr stx) (syntax-cxr '(d) stx))
+
+(define (unwrap-list stx)
+ (let ((stx (unwrap-syntax stx)))
+ (if (pair? stx)
+ (cons (car stx) (unwrap-syntax (cdr stx)))
+ stx)))
+
diff --git a/multisyntax/syntax-object.sld b/multisyntax/syntax-object.sld
index f013885..31060b4 100644
--- a/multisyntax/syntax-object.sld
+++ b/multisyntax/syntax-object.sld
@@ -18,19 +18,20 @@
(define-library (multisyntax syntax-object)
(import (scheme base) (scheme case-lambda)
(scheme write)
- (srfi 26) (srfi 113) (srfi 128) (srfi 133) (srfi 146) (srfi 228)
+ (srfi 1) (srfi 26) (srfi 113) (srfi 128) (srfi 133) (srfi 146) (srfi 158) (srfi 228)
(multisyntax utils))
- (export generate-lexical-location
- lexical-location->string
- lexical-location-comparator
- environment-key-comparator
- bound-identifier-comparator
+ (export generate-lexical-location generate-lexical-locations
+ lexical-location->string lexical-location-comparator
+ environment-key-comparator bound-identifier-comparator
;; Misc. predicates
self-syntax? syntax?
;; Operations on wraps
generate-timestamp empty-wrap add-timestamp add-substitution
wrap->timestamps resolve
identifier-lexically-bound?
+ ;; Non-standard procedures that can be defined in terms of
+ ;; Macrological Fascile procedures
+ syntax-cxr syntax-car syntax-cdr unwrap-list
;; Standard operations
symbolic-identifier=? free-identifier=? bound-identifier=?
identifier?
diff --git a/test/run.scm b/test/run.scm
index b0b03bc..23d3af2 100644
--- a/test/run.scm
+++ b/test/run.scm
@@ -14,15 +14,32 @@
(import (rename (multisyntax syntax-object test)
(test test-syntax-object)))
-(test-syntax-object)
+#;(test-syntax-object)
(load "../multisyntax/pattern/internal.sld")
(load "../multisyntax/pattern/matcher.sld")
(load "pattern/matcher.sld")
(import (multisyntax pattern matcher test))
-(test-patterns)
+#;(test-patterns)
(load "../multisyntax/pattern/producer.sld")
(load "pattern/producer.sld")
(import (multisyntax pattern producer test))
-(test-producers)
+#;(test-producers)
+
+(load "../multisyntax/examples/untyped-lambda-calculus.sld")
+(import (multisyntax examples untyped-lambda-calculus)
+ (multisyntax syntax-object))
+
+#;(let-values (((global-map expanded-list)
+ (expand initial-environment (list (empty-wrap '(lambda x x)))))))
+
+(define-values (global-map expanded-list)
+ (expand initial-environment
+ (list (empty-wrap '(let-syntax ((λ lambda))
+ (λ x x))))))
+
+#;(begin
+ (load "examples/untyped-lambda-calculus.sld")
+ (import (multisyntax examples untyped-lambda-calculus test))
+ (test-untyped-lambda-calculus))