fix: README -> README.md
This commit is contained in:
parent
43e68af625
commit
99b0a6292c
756 changed files with 323753 additions and 71 deletions
28
ta6ob/examples/Makefile
Normal file
28
ta6ob/examples/Makefile
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Unix make file to compile the examples.
|
||||
# Compilation is not necessary since the examples may be loaded from
|
||||
# source, but this gives an example of how to use make for Scheme.
|
||||
# * To compile files not already compiled, type "make". Only those
|
||||
# files in the object list below and not yet compiled will be compiled.
|
||||
# * To compile all files, type "make all". Only those files in the object
|
||||
# list below will be compiled.
|
||||
# * To compile one file, say "fumble.ss", type "make fumble.so". The
|
||||
# file need not be in the object list below.
|
||||
# * To remove the object files, type "make clean".
|
||||
# * To print the examples, type "make print".
|
||||
|
||||
src = def.ss edit.ss fact.ss fatfib.ss fft.ss fib.ss freq.ss interpret.ss\
|
||||
m4.ss macro.ss matrix.ss object.ss power.ss queue.ss rabbit.ss rsa.ss\
|
||||
scons.ss setof.ss socket.ss unify.ss compat.ss ez-grammar-test.ss
|
||||
obj = ${src:%.ss=%.so}
|
||||
|
||||
Scheme = ../bin/scheme -q
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .ss .so
|
||||
.ss.so: ; echo '(time (compile-file "$*"))' | ${Scheme}
|
||||
|
||||
needed: ${obj}
|
||||
|
||||
all: ; echo "(time (for-each compile-file (map symbol->string '(${src}))))" | ${Scheme}
|
||||
|
||||
clean: ; rm -f $(obj) expr.md
|
291
ta6ob/examples/compat.ss
Normal file
291
ta6ob/examples/compat.ss
Normal file
|
@ -0,0 +1,291 @@
|
|||
;;; compat.ss
|
||||
;;; Copyright 1984-2017 Cisco Systems, Inc.
|
||||
;;;
|
||||
;;; 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.
|
||||
|
||||
;;; miscellaneous definitions to make this version compatible
|
||||
;;; (where possible) with previous versions...and to a small extent with
|
||||
;;; other versions of scheme and other dialects of lisp as well
|
||||
|
||||
;;; use only those items that you need to avoid introducing accidental
|
||||
;;; dependencies on other items.
|
||||
|
||||
(define-syntax define!
|
||||
(syntax-rules ()
|
||||
((_ x v) (begin (set! x v) 'x))))
|
||||
|
||||
(define-syntax defrec!
|
||||
(syntax-rules ()
|
||||
((_ x v) (define! x (rec x v)))))
|
||||
|
||||
(define-syntax begin0
|
||||
(syntax-rules ()
|
||||
((_ x y ...) (let ((t x)) y ... t))))
|
||||
|
||||
(define-syntax recur
|
||||
(syntax-rules ()
|
||||
((_ f ((i v) ...) e1 e2 ...)
|
||||
(let f ((i v) ...) e1 e2 ...))))
|
||||
|
||||
(define-syntax trace-recur
|
||||
(syntax-rules ()
|
||||
((_ f ((x v) ...) e1 e2 ...)
|
||||
(trace-let f ((x v) ...) e1 e2 ...))))
|
||||
|
||||
(define swap-box!
|
||||
(lambda (b v)
|
||||
(if (box? b)
|
||||
(let ((x (unbox b))) (set-box! b v) x)
|
||||
(error 'swap-box! "~s is not a box" b))))
|
||||
|
||||
(define cull
|
||||
(lambda (pred? ls)
|
||||
(unless (procedure? pred?)
|
||||
(error 'cull "~s is not a procedure" pred?))
|
||||
(let f ([l ls])
|
||||
(cond
|
||||
[(pair? l)
|
||||
(if (pred? (car l))
|
||||
(cons (car l) (f (cdr l)))
|
||||
(f (cdr l)))]
|
||||
[(null? l) '()]
|
||||
[else (error 'cull "~s is not a proper list" ls)]))))
|
||||
|
||||
(define cull! cull)
|
||||
|
||||
(define mem
|
||||
(lambda (pred? ls)
|
||||
(unless (procedure? pred?)
|
||||
(error 'mem "~s is not a procedure" pred?))
|
||||
(let f ([l ls])
|
||||
(cond
|
||||
[(pair? l) (if (pred? (car l)) l (f (cdr l)))]
|
||||
[(null? l) #f]
|
||||
[else (error 'mem "~s is not a proper list" ls)]))))
|
||||
|
||||
(define rem
|
||||
(lambda (pred? ls)
|
||||
(unless (procedure? pred?)
|
||||
(error 'rem "~s is not a procedure" pred?))
|
||||
(let f ([l ls])
|
||||
(cond
|
||||
[(pair? l)
|
||||
(if (pred? (car l))
|
||||
(f (cdr l))
|
||||
(cons (car l) (f (cdr l))))]
|
||||
[(null? l) '()]
|
||||
[else (error 'rem "~s is not a proper list" ls)]))))
|
||||
|
||||
(define rem!
|
||||
(lambda (pred? ls)
|
||||
(unless (procedure? pred?)
|
||||
(error 'rem! "~s is not a procedure" pred?))
|
||||
(let f ([l ls])
|
||||
(cond
|
||||
[(pair? l)
|
||||
(if (pred? (car l))
|
||||
(f (cdr l))
|
||||
(begin
|
||||
(set-cdr! l (f (cdr l)))
|
||||
l))]
|
||||
[(null? l) '()]
|
||||
[else (error 'rem! "~s is not a proper list" ls)]))))
|
||||
|
||||
(define ass
|
||||
(lambda (pred? alist)
|
||||
(unless (procedure? pred?)
|
||||
(error 'ass "~s is not a procedure" pred?))
|
||||
(let loop ([l alist])
|
||||
(cond
|
||||
[(and (pair? l) (pair? (car l)))
|
||||
(if (pred? (caar l))
|
||||
(car l)
|
||||
(loop (cdr l)))]
|
||||
[(null? l) #f]
|
||||
[else (error 'ass "improperly formed alist ~s" alist)]))))
|
||||
|
||||
(define prompt-read
|
||||
(lambda (fmt . args)
|
||||
(apply printf fmt args)
|
||||
(read)))
|
||||
|
||||
(define tree-copy
|
||||
(rec tree-copy
|
||||
(lambda (x)
|
||||
(if (pair? x)
|
||||
(cons (tree-copy (car x)) (tree-copy (cdr x)))
|
||||
x))))
|
||||
|
||||
(define ferror error)
|
||||
|
||||
(define *most-negative-short-integer* (most-negative-fixnum))
|
||||
(define *most-positive-short-integer* (most-positive-fixnum))
|
||||
|
||||
(define *most-negative-fixnum* (most-negative-fixnum))
|
||||
(define *most-positive-fixnum* (most-positive-fixnum))
|
||||
|
||||
(define *eof* (read-char (open-input-string "")))
|
||||
|
||||
(define short-integer? fixnum?)
|
||||
(define big-integer? bignum?)
|
||||
(define ratio? ratnum?)
|
||||
(define float? flonum?)
|
||||
|
||||
(define bound? top-level-bound?)
|
||||
(define global-value top-level-value)
|
||||
(define set-global-value! set-top-level-value!)
|
||||
(define define-global-value define-top-level-value)
|
||||
(define symbol-value top-level-value)
|
||||
(define set-symbol-value! set-top-level-value!)
|
||||
|
||||
(define put putprop)
|
||||
(define get getprop)
|
||||
|
||||
(define copy-list list-copy)
|
||||
(define copy-tree tree-copy)
|
||||
(define copy-string string-copy)
|
||||
(define copy-vector vector-copy)
|
||||
|
||||
(define intern string->symbol)
|
||||
(define symbol-name symbol->string)
|
||||
(define string->uninterned-symbol gensym)
|
||||
(define make-temp-symbol string->uninterned-symbol)
|
||||
(define uninterned-symbol? gensym?)
|
||||
(define temp-symbol? uninterned-symbol?)
|
||||
|
||||
(define compile-eval compile)
|
||||
|
||||
(define closure? procedure?)
|
||||
|
||||
(define =? =)
|
||||
(define <? <)
|
||||
(define >? >)
|
||||
(define <=? <=)
|
||||
(define >=? >=)
|
||||
|
||||
(define float exact->inexact)
|
||||
(define rational inexact->exact)
|
||||
|
||||
(define char-equal? char=?)
|
||||
(define char-less? char<?)
|
||||
(define string-equal? string=?)
|
||||
(define string-less? string<?)
|
||||
|
||||
; following defn conflicts with new r6rs mod
|
||||
#;(define mod modulo)
|
||||
|
||||
(define flush-output flush-output-port)
|
||||
(define clear-output clear-output-port)
|
||||
(define clear-input clear-input-port)
|
||||
|
||||
(define mapcar map)
|
||||
(define mapc for-each)
|
||||
(define true #t)
|
||||
(define false #f)
|
||||
(define t #t)
|
||||
(define nil '())
|
||||
|
||||
(define macro-expand expand)
|
||||
|
||||
;;; old macro and structure definition
|
||||
|
||||
;;; thanks to Michael Lenaghan (MichaelL@frogware.com) for suggesting
|
||||
;;; various improvements.
|
||||
(define-syntax define-macro!
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
[(k (name arg1 ... . args)
|
||||
form1
|
||||
form2
|
||||
...)
|
||||
#'(k name (arg1 ... . args)
|
||||
form1
|
||||
form2
|
||||
...)]
|
||||
[(k (name arg1 arg2 ...)
|
||||
form1
|
||||
form2
|
||||
...)
|
||||
#'(k name (arg1 arg2 ...)
|
||||
form1
|
||||
form2
|
||||
...)]
|
||||
[(k name args . forms)
|
||||
(identifier? #'name)
|
||||
(letrec ((add-car
|
||||
(lambda (access)
|
||||
(case (car access)
|
||||
((cdr) `(cadr ,@(cdr access)))
|
||||
((cadr) `(caadr ,@(cdr access)))
|
||||
((cddr) `(caddr ,@(cdr access)))
|
||||
((cdddr) `(cadddr ,@(cdr access)))
|
||||
(else `(car ,access)))))
|
||||
(add-cdr
|
||||
(lambda (access)
|
||||
(case (car access)
|
||||
((cdr) `(cddr ,@(cdr access)))
|
||||
((cadr) `(cdadr ,@(cdr access)))
|
||||
((cddr) `(cdddr ,@(cdr access)))
|
||||
((cdddr) `(cddddr ,@(cdr access)))
|
||||
(else `(cdr ,access)))))
|
||||
(parse
|
||||
(lambda (l access)
|
||||
(cond
|
||||
((null? l) '())
|
||||
((symbol? l) `((,l ,access)))
|
||||
((pair? l)
|
||||
(append!
|
||||
(parse (car l) (add-car access))
|
||||
(parse (cdr l) (add-cdr access))))
|
||||
(else
|
||||
(syntax-error #'args
|
||||
(format "invalid ~s parameter syntax" (datum k))))))))
|
||||
(with-syntax ((proc (datum->syntax-object #'k
|
||||
(let ((g (gensym)))
|
||||
`(lambda (,g)
|
||||
(let ,(parse (datum args) `(cdr ,g))
|
||||
,@(datum forms)))))))
|
||||
#'(define-syntax name
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
((k1 . r)
|
||||
(datum->syntax-object #'k1
|
||||
(proc (syntax-object->datum x)))))))))])))
|
||||
|
||||
(alias define-macro define-macro!)
|
||||
(alias defmacro define-macro!)
|
||||
|
||||
(define-macro! define-struct! (name . slots)
|
||||
`(begin
|
||||
(define ,name
|
||||
(lambda ,slots
|
||||
(vector ',name ,@slots)))
|
||||
(define ,(string->symbol (format "~a?" name))
|
||||
(lambda (x)
|
||||
(and (vector? x)
|
||||
(= (vector-length x) (1+ ,(length slots)))
|
||||
(eq? ',name (vector-ref x 0)))))
|
||||
,@(\#make-accessors name slots)
|
||||
',name))
|
||||
|
||||
(define \#make-accessors
|
||||
(lambda (name slots)
|
||||
(recur f ((n 1) (slots slots))
|
||||
(if (not (null? slots))
|
||||
(let*
|
||||
((afn (string->symbol (format "~a-~a" name (car slots))))
|
||||
(sfn (string->symbol (format "~a!" afn))))
|
||||
`((define-macro! ,afn (x) `(vector-ref ,x ,,n))
|
||||
(define-macro! ,sfn (x v) `(vector-set! ,x ,,n ,v))
|
||||
,@(f (1+ n) (cdr slots))))
|
||||
'()))))
|
86
ta6ob/examples/crepl.c
Normal file
86
ta6ob/examples/crepl.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* crepl.c
|
||||
* Copyright 1984-2017 Cisco Systems, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
This is a variant of main.c that implements a Scheme repl in C.
|
||||
It's not at all useful, but it highlights how to invoke Scheme
|
||||
without going through Sscheme_start.
|
||||
|
||||
Test in a workarea's examples subdirectory with:
|
||||
|
||||
( cd ../c ; ln -sf ../examples/crepl.c . )
|
||||
( cd ../c ; make mainsrc=crepl.c )
|
||||
sh -c 'SCHEMEHEAPDIRS=../boot/%m ../bin/scheme'
|
||||
*/
|
||||
|
||||
#include "scheme.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define CALL0(who) Scall0(Stop_level_value(Sstring_to_symbol(who)))
|
||||
#define CALL1(who, arg) Scall1(Stop_level_value(Sstring_to_symbol(who)), arg)
|
||||
|
||||
static void custom_init(void) {}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int n, new_argc = 1, ignoreflags = 0;
|
||||
ptr p;
|
||||
|
||||
Sscheme_init(NULL);
|
||||
|
||||
/* process command-line arguments, registering boot and heap files */
|
||||
for (n = 1; n < argc; n += 1) {
|
||||
if (!ignoreflags && *argv[n] == '-') {
|
||||
switch (*(argv[n]+1)) {
|
||||
case '-': /* pass through remaining options */
|
||||
if (*(argv[n]+2) != 0) break;
|
||||
ignoreflags = 1;
|
||||
continue;
|
||||
case 'b': /* boot option, expects boot file pathname */
|
||||
if (*(argv[n]+2) != 0) break;
|
||||
if (++n == argc) {
|
||||
(void) fprintf(stderr,"\n-b option requires argument\n");
|
||||
exit(1);
|
||||
}
|
||||
Sregister_boot_file(argv[n]);
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
argv[new_argc++] = argv[n];
|
||||
}
|
||||
|
||||
/* must call Sscheme_heap after registering boot and heap files
|
||||
* Sscheme_heap() completes the initialization of the Scheme system
|
||||
* and loads the boot or heap files. Before loading boot files,
|
||||
* it calls custom_init(). */
|
||||
Sbuild_heap(argv[0], custom_init);
|
||||
|
||||
for (;;) {
|
||||
CALL1("display", Sstring("* "));
|
||||
p = CALL0("read");
|
||||
if (Seof_objectp(p)) break;
|
||||
p = CALL1("eval", p);
|
||||
if (p != Svoid) CALL1("pretty-print", p);
|
||||
}
|
||||
CALL0("newline");
|
||||
|
||||
/* must call Scheme_deinit after saving the heap and before exiting */
|
||||
Sscheme_deinit();
|
||||
|
||||
exit(0);
|
||||
}
|
103
ta6ob/examples/csocket.c
Normal file
103
ta6ob/examples/csocket.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*/ csocket.c
|
||||
R. Kent Dybvig May 1998
|
||||
Updated by Jamie Taylor, Sept 2016
|
||||
Public Domain
|
||||
/*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* c_write attempts to write the entire buffer, pushing through
|
||||
interrupts, socket delays, and partial-buffer writes */
|
||||
int c_write(int fd, char *buf, ssize_t start, ssize_t n) {
|
||||
ssize_t i, m;
|
||||
|
||||
buf += start;
|
||||
m = n;
|
||||
while (m > 0) {
|
||||
if ((i = write(fd, buf, m)) < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR)
|
||||
return i;
|
||||
} else {
|
||||
m -= i;
|
||||
buf += i;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* c_read pushes through interrupts and socket delays */
|
||||
int c_read(int fd, char *buf, size_t start, size_t n) {
|
||||
int i;
|
||||
|
||||
buf += start;
|
||||
for (;;) {
|
||||
i = read(fd, buf, n);
|
||||
if (i >= 0) return i;
|
||||
if (errno != EAGAIN && errno != EINTR) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* bytes_ready(fd) returns true if there are bytes available
|
||||
to be read from the socket identified by fd */
|
||||
int bytes_ready(int fd) {
|
||||
int n;
|
||||
|
||||
(void) ioctl(fd, FIONREAD, &n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* socket support */
|
||||
|
||||
/* do_socket() creates a new AF_UNIX socket */
|
||||
int do_socket(void) {
|
||||
|
||||
return socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
/* do_bind(s, name) binds name to the socket s */
|
||||
int do_bind(int s, char *name) {
|
||||
struct sockaddr_un sun;
|
||||
int length;
|
||||
|
||||
sun.sun_family = AF_UNIX;
|
||||
(void) strcpy(sun.sun_path, name);
|
||||
length = sizeof(sun.sun_family) + sizeof(sun.sun_path);
|
||||
|
||||
return bind(s, (struct sockaddr*)(&sun), length);
|
||||
}
|
||||
|
||||
/* do_accept accepts a connection on socket s */
|
||||
int do_accept(int s) {
|
||||
struct sockaddr_un sun;
|
||||
socklen_t length;
|
||||
|
||||
length = sizeof(sun.sun_family) + sizeof(sun.sun_path);
|
||||
|
||||
return accept(s, (struct sockaddr*)(&sun), &length);
|
||||
}
|
||||
|
||||
/* do_connect initiates a socket connection */
|
||||
int do_connect(int s, char *name) {
|
||||
struct sockaddr_un sun;
|
||||
int length;
|
||||
|
||||
sun.sun_family = AF_UNIX;
|
||||
(void) strcpy(sun.sun_path, name);
|
||||
length = sizeof(sun.sun_family) + sizeof(sun.sun_path);
|
||||
|
||||
return connect(s, (struct sockaddr*)(&sun), length);
|
||||
}
|
||||
|
||||
/* get_error returns the operating system's error status */
|
||||
char* get_error(void) {
|
||||
extern int errno;
|
||||
return strerror(errno);
|
||||
}
|
125
ta6ob/examples/def.ss
Normal file
125
ta6ob/examples/def.ss
Normal file
|
@ -0,0 +1,125 @@
|
|||
;;; def.ss
|
||||
;;; Copyright (C) 1987 R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; Prototype code for definition facility that remembers definitions and
|
||||
;;; allows you to pretty-print or edit them (using the structure editor
|
||||
;;; defined in the file "edit.ss").
|
||||
|
||||
;;; def can be in place of define at top level (i.e., not within a lambda,
|
||||
;;; let, let*, or letrec body). It saves the source for the definition
|
||||
;;; as well as performing the defintion. Type (ls-def) for a list of
|
||||
;;; variables defined this session, and (pp-def variable) to return the
|
||||
;;; definition of a particular variable.
|
||||
|
||||
;;; Possible exercises/enhancements:
|
||||
;;;
|
||||
;;; 1) Write a "dskout" function that pretty-prints the definitions of
|
||||
;;; all or selected variables defined this session to a file.
|
||||
;;;
|
||||
;;; 2) In place of "def", write a modified "load" that remembers where
|
||||
;;; (that is, in which file) it saw the definition for each variable
|
||||
;;; defined in a particular session. This would be used instead of
|
||||
;;; the "def" form. "ls-def" would be similar to what it is now.
|
||||
;;; "pp-def" could be similar to what it is now, or it could involve
|
||||
;;; rereading the corresponding file. "ed-def" could invoke the
|
||||
;;; structure editor and (as an option) print the modified definition
|
||||
;;; back to the corresponding file, or "ed-def" could invoke a host
|
||||
;;; editor (such as Unix "vi" or VMS "edit") on the corresponding
|
||||
;;; source file, with an option to reload. If this tool is smart
|
||||
;;; enough, it could get around the limitation that definitions use
|
||||
;;; define at top-level, i.e., (let ([x #f]) (set! foo (lambda () x)))
|
||||
;;; could be recognized as a definition for foo.
|
||||
|
||||
(define-syntax def
|
||||
;; only makes sense for "top level" definitions
|
||||
(syntax-rules ()
|
||||
[(_ (var . formals) . body)
|
||||
(begin (define (var . formals) . body)
|
||||
(insert-def! 'var '(def (var . formals) . body) var)
|
||||
'var)]
|
||||
[(_ var exp)
|
||||
(begin (define var exp)
|
||||
(insert-def! 'var '(def var exp) var)
|
||||
'var)]))
|
||||
|
||||
(define-syntax pp-def
|
||||
(syntax-rules (quote)
|
||||
; allow var to be unquoted or quoted
|
||||
[(_ var) (pp-def-help 'var var)]
|
||||
[(_ 'var) (pp-def-help 'var var)]))
|
||||
|
||||
(define-syntax ed-def
|
||||
(syntax-rules (quote)
|
||||
; allow var to be unquoted or quoted
|
||||
[(_ var) (ed-def-help 'var var)]
|
||||
[(_ 'var) (ed-def-help 'var var)]))
|
||||
|
||||
|
||||
(define insert-def! #f) ; assigned within the let below
|
||||
(define ls-def #f) ; assigned within the let below
|
||||
(define pp-def-help #f) ; assigned within the let below
|
||||
(define ed-def-help #f) ; assigned within the let below
|
||||
(let ([defs '()])
|
||||
(define tree-copy
|
||||
(rec tree-copy
|
||||
(lambda (x)
|
||||
(if (pair? x)
|
||||
(cons (tree-copy (car x)) (tree-copy (cdr x)))
|
||||
x))))
|
||||
(set! insert-def!
|
||||
(lambda (var defn val)
|
||||
(unless (symbol? var)
|
||||
(error 'insert-def! "~s is not a symbol" var))
|
||||
(let ([a (assq var defs)])
|
||||
(if a
|
||||
(set-cdr! a (cons defn val))
|
||||
(set! defs (cons (cons var (cons defn val)) defs))))))
|
||||
(set! ls-def
|
||||
(lambda ()
|
||||
(map car defs)))
|
||||
(set! pp-def-help
|
||||
(lambda (var val)
|
||||
(unless (symbol? var)
|
||||
(error 'pp-def "~s is not a symbol" var))
|
||||
(let ([a (assq var defs)])
|
||||
(unless a
|
||||
(error 'pp-def
|
||||
"~s has not been defined during this session"
|
||||
var))
|
||||
(unless (eq? (cddr a) val)
|
||||
(printf "Warning: ~s has been reassigned since definition"
|
||||
var))
|
||||
(cadr a))))
|
||||
(set! ed-def-help
|
||||
(lambda (var val)
|
||||
(unless (symbol? var)
|
||||
(error 'ed-def "~s is not a symbol" var))
|
||||
(let ([a (assq var defs)])
|
||||
(unless a
|
||||
(error 'ed-def
|
||||
"~s has not been defined during this session"
|
||||
var))
|
||||
(unless (eq? (cddr a) val)
|
||||
(printf "Warning: ~s reassigned since last definition"
|
||||
var))
|
||||
; edit is destructive; the copy allows the defined name to
|
||||
; be changed without affecting the old name's definition
|
||||
(eval (edit (tree-copy (cadr a))))))))
|
464
ta6ob/examples/edit.ss
Normal file
464
ta6ob/examples/edit.ss
Normal file
|
@ -0,0 +1,464 @@
|
|||
;;; edit.ss
|
||||
;;; Copyright (C) 1987 R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; This file contains an implementation of a simple interactive structure
|
||||
;;; editor for Scheme. The editor is invoked with an expression as it's
|
||||
;;; single argument. It prompts for, reads, and processes editor commands.
|
||||
|
||||
;;; The editor commands recognized are those documented in the Texas
|
||||
;;; Instruments' PC Scheme manual. They are summarized below.
|
||||
|
||||
;;; Command syntax Action
|
||||
;;;
|
||||
;;; q or <eof> Quit the editor, returning edited expression.
|
||||
;;;
|
||||
;;; p Write the current expression.
|
||||
;;;
|
||||
;;; ? Write to level 2, length 10.
|
||||
;;;
|
||||
;;; pp Pretty print the current expression.
|
||||
;;;
|
||||
;;; ?? Pretty print to level 2, length 10.
|
||||
;;;
|
||||
;;; <pos> Move to subexpression of current expression
|
||||
;;; <pos> = 0 is the current expression, <pos> > 0
|
||||
;;; is the numbered subexpression (1 for first, 2
|
||||
;;; for second, ...), <pos> < 0 is the numbered
|
||||
;;; subexpression from the right (-1 for last, -2
|
||||
;;; for second to last, ...), and <pos> = * is the
|
||||
;;; "last cdr" of the current expression. If <pos>
|
||||
;;; is not 0, the current expression must be a list.
|
||||
;;;
|
||||
;;; b Move back to parent expression.
|
||||
;;;
|
||||
;;; t Move to top-level expression.
|
||||
;;;
|
||||
;;; pr Move to expression on the left (previous).
|
||||
;;;
|
||||
;;; n Move to expression on the right (next).
|
||||
;;;
|
||||
;;; (f <obj>) Find <obj> within or to the right of the current
|
||||
;;; expression using equal?.
|
||||
;;;
|
||||
;;; f or (f) Find <obj> of last (f <obj>) command.
|
||||
;;;
|
||||
;;; (d <pos>) Delete the expression at position <pos>.
|
||||
;;;
|
||||
;;; (r <pos> <obj>) Replace the expression at position <pos> with
|
||||
;;; <obj>.
|
||||
;;;
|
||||
;;; (s <obj1> <obj2>) Replace all occurrences of <obj1> by <obj2>
|
||||
;;; within the current expression.
|
||||
;;;
|
||||
;;; (dp <pos>) Remove parens from around expression at position
|
||||
;;; <pos>.
|
||||
;;;
|
||||
;;; (ap <pos1> <pos2>) Insert parens around expressions from position
|
||||
;;; <pos1> through <pos2> (inclusive). If <pos1> is
|
||||
;;; 0 or *, <pos2> is ignored and may be omitted.
|
||||
;;;
|
||||
;;; (ib <pos> <obj>) Insert <obj> before expression at position <pos>.
|
||||
;;;
|
||||
;;; (ia <pos> <obj>) Insert <obj> after expression at position <pos>.
|
||||
;;;
|
||||
;;; (sb <pos> <obj>) Splice <obj> before expression at position <pos>.
|
||||
;;;
|
||||
;;; (sa <pos> <obj>) Splice <obj> after expression at position <pos>.
|
||||
|
||||
;;; Possible exercises/enhancements:
|
||||
;;;
|
||||
;;; 1) Implement an infinite undo ("u") command in the editor. This
|
||||
;;; can be done by creating an "inverse" function for each operation
|
||||
;;; that causes a side-effect, i.e, a closure that "remembers" the
|
||||
;;; list cells involved and knows how to put them back the way they
|
||||
;;; were. An undo (u) variable could then be added to the editor's
|
||||
;;; main loop; it would be bound to a list containing the set of
|
||||
;;; registers at the point of the last side-effect (similarly to the
|
||||
;;; "back" (b) variable) and the undo function for the side-effect.
|
||||
;;;
|
||||
;;; 2) Implement an infinite redo ("r") command in the editor. This
|
||||
;;; can be done by remembering the undo functions and registers for
|
||||
;;; the undo's since the last non-undo command.
|
||||
;;;
|
||||
;;; 3) Handle circular structures better in the editor. Specifically,
|
||||
;;; modify the find ("f") command so that it always terminates, and
|
||||
;;; devise a method for printing circular structures with the "p"
|
||||
;;; and "pp" commands. Cure the bug mentioned in the overview of
|
||||
;;; the code given later in the file.
|
||||
;;;
|
||||
;;; 4) Add a help ("h") command to the editor. This could be as simple
|
||||
;;; as listing the available commands.
|
||||
;;;
|
||||
;;; 5) Make the editor "extensible" via user-defined macros or editor
|
||||
;;; commands written in Scheme.
|
||||
;;;
|
||||
;;; 6) Modify the editor to provide more descriptive error messages that
|
||||
;;; diagnose the problem and attempt to give some help. For example,
|
||||
;;; if the editor receives "(r 1)" it might respond with:
|
||||
;;; "Two few arguments:
|
||||
;;; Type (r pos exp) to replace the expression at position pos
|
||||
;;; with the expression exp."
|
||||
;;; This should be implemented in conjunction with the help command.
|
||||
;;; Should it be possible to disable such verbose error messages?
|
||||
|
||||
;;; Implementation:
|
||||
;;;
|
||||
;;; The main editor loop and many of the help functions operate on a
|
||||
;;; set of "registers". These registers are described below:
|
||||
;;;
|
||||
;;; s The current find object. s is initially #f, and is bound to a
|
||||
;;; pair containing the find object when the first (f <obj>) command
|
||||
;;; is seen. The identical f and (f) commands use the saved object.
|
||||
;;;
|
||||
;;; p The parent of the current expression. This is initially a list
|
||||
;;; of one element, the argument to edit. It is updated by various
|
||||
;;; movement commands.
|
||||
;;;
|
||||
;;; i The index of the current expression in the parent (p). This is
|
||||
;;; initially 0. It is updated by various movement commands.
|
||||
;;;
|
||||
;;; b The "back" chain; actually a list containing the registers p, i
|
||||
;;; and b for the parent of the current expression. It is initially
|
||||
;;; (). It is updated by various movement commands.
|
||||
;;;
|
||||
;;; Bugs:
|
||||
;;;
|
||||
;;; When editing a circular structure, it is possible for the editor to
|
||||
;;; get lost. That is, when the parent node of the current expression
|
||||
;;; is changed by a command operating on a subexpression of the current
|
||||
;;; expression, the index for the current expression may become incorrect.
|
||||
;;; This can result in abnormal termination of the editor. It would be
|
||||
;;; fairly simple to check for this (in list-ref) and reset the editor,
|
||||
;;; and it may be possible to use a different set of registers to avoid
|
||||
;;; the problem altogether.
|
||||
|
||||
(define edit #f) ; assigned within the let expression below
|
||||
(let ()
|
||||
(define cmdeq?
|
||||
;; used to check command syntax
|
||||
(lambda (cmd pat)
|
||||
(and (pair? cmd)
|
||||
(eq? (car cmd) (car pat))
|
||||
(let okargs? ([cmd (cdr cmd)] [pat (cdr pat)])
|
||||
(if (null? pat)
|
||||
(null? cmd)
|
||||
(and (not (null? cmd))
|
||||
(okargs? (cdr cmd) (cdr pat))))))))
|
||||
(define find
|
||||
;; find expression within or to right of current expression
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(define check
|
||||
(lambda (p i b)
|
||||
(if (equal? (list-ref p i) (car s0))
|
||||
(wrlev s0 p i b)
|
||||
(continue p i b))))
|
||||
(define continue
|
||||
(lambda (p i b)
|
||||
(let ([e (list-ref p i)])
|
||||
(if (atom? e)
|
||||
(let next ([p p] [i i] [b b])
|
||||
(let ([n (maxref p)])
|
||||
(if (or (not n) (< i n))
|
||||
(check p (+ i 1) b)
|
||||
(if (null? b)
|
||||
(search-failed s0 p0 i0 b0)
|
||||
(apply next b)))))
|
||||
(check e 0 (list p i b))))))
|
||||
(continue p0 i0 b0)))
|
||||
(define maxref
|
||||
;; use "hare and tortoise" algorithm to check for circular lists.
|
||||
;; return maximum reference index (zero-based) for a list x. return
|
||||
;; -1 for atoms and #f for circular lists.
|
||||
(lambda (x)
|
||||
(let f ([hare x] [tortoise x] [n -1])
|
||||
(cond
|
||||
[(atom? hare) n]
|
||||
[(atom? (cdr hare)) (+ n 1)]
|
||||
[(eq? (cdr hare) tortoise) #f]
|
||||
[else (f (cddr hare) (cdr tortoise) (+ n 2))]))))
|
||||
(define move
|
||||
;; move to subexpression specified by x and pass current state to k.
|
||||
(lambda (x s p i b k)
|
||||
(cond
|
||||
[(eqv? x 0) (k s p i b)]
|
||||
[(eq? x '*)
|
||||
(let ([m (maxref (list-ref p i))])
|
||||
(if m
|
||||
(k s (list-ref p i) '* (list p i b))
|
||||
(invalid-movement s p i b)))]
|
||||
[(> x 0)
|
||||
(let ([m (maxref (list-ref p i))] [x (- x 1)])
|
||||
(if (or (not m) (>= m x))
|
||||
(k s (list-ref p i) x (list p i b))
|
||||
(invalid-movement s p i b)))]
|
||||
[else
|
||||
(let ([m (maxref (list-ref p i))] [x (- -1 x)])
|
||||
(if (and m (>= m x))
|
||||
(let ([x (- m x)])
|
||||
(k s (list-ref p i) x (list p i b)))
|
||||
(invalid-movement s p i b)))])))
|
||||
(define proper-list?
|
||||
;; return #t if x is a proper list.
|
||||
(lambda (x)
|
||||
(and (maxref x)
|
||||
(or (null? x) (null? (cdr (last-pair x)))))))
|
||||
(define list-ref
|
||||
;; reference list ls element i. i may be *, in which case return
|
||||
;; the last pair of ls.
|
||||
(lambda (ls i)
|
||||
(if (eq? i '*)
|
||||
(cdr (last-pair ls))
|
||||
(car (list-tail ls i)))))
|
||||
(define list-set!
|
||||
;; change element i of ls to x.
|
||||
(lambda (ls i x)
|
||||
(if (eq? i '*)
|
||||
(set-cdr! (last-pair ls) x)
|
||||
(set-car! (list-tail ls i) x))))
|
||||
(define list-cut!
|
||||
;; remove element i from ls.
|
||||
(lambda (ls i)
|
||||
(let ([a (cons '() ls)])
|
||||
(set-cdr! (list-tail a i) (list-tail a (+ i 2)))
|
||||
(cdr a))))
|
||||
(define list-splice!
|
||||
;; insert ls2 into ls1 in place of element i.
|
||||
(lambda (ls1 i ls2)
|
||||
(let ([a (list-tail ls1 i)])
|
||||
(unless (null? (cdr a))
|
||||
(set-cdr! (last-pair ls2) (cdr a)))
|
||||
(set-car! a (car ls2))
|
||||
(set-cdr! a (cdr ls2)))
|
||||
ls1))
|
||||
(define list-ap*!
|
||||
;; place parens from element i through last pair of ls.
|
||||
(lambda (ls i)
|
||||
(let ([a (list-tail ls i)])
|
||||
(let ([c (cons (car a) (cdr a))])
|
||||
(set-car! a c)
|
||||
(set-cdr! a '())))
|
||||
ls))
|
||||
(define list-ap!
|
||||
;; place parens from element i0 through element i1.
|
||||
(lambda (ls i0 i1)
|
||||
(let ([a (list-tail ls i0)] [b (list-tail ls i1)])
|
||||
(let ([c (cons (car a) (cdr a))])
|
||||
(set-car! a c)
|
||||
(if (eq? a b)
|
||||
(set-cdr! c '())
|
||||
(begin (set-cdr! a (cdr b))
|
||||
(set-cdr! b '())))))
|
||||
ls))
|
||||
(define wrlev
|
||||
;; write current expression to level 2, length 10 and continue.
|
||||
(lambda (s p i b)
|
||||
(parameterize ([print-level 2] [print-length 10])
|
||||
(printf "~s~%" (list-ref p i)))
|
||||
(edit-loop s p i b)))
|
||||
(define wr
|
||||
;; write current expression and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "~s~%" (list-ref p i))
|
||||
(edit-loop s p i b)))
|
||||
(define pplev
|
||||
;; pretty print current expression to level 2, length 10 and continue.
|
||||
(lambda (s p i b)
|
||||
(parameterize ([print-level 2] [print-length 10])
|
||||
(pretty-print (list-ref p i)))
|
||||
(edit-loop s p i b)))
|
||||
(define pp
|
||||
;; pretty print current expression and continue.
|
||||
(lambda (s p i b)
|
||||
(pretty-print (list-ref p i))
|
||||
(edit-loop s p i b)))
|
||||
(define not-a-proper-list
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "structure is not a proper list~%")
|
||||
(edit-loop s p i b)))
|
||||
(define cannot-dp-zero
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "cannot remove parens from current expression~%")
|
||||
(edit-loop s p i b)))
|
||||
(define pos2-before-pos1
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "second position before first~%")
|
||||
(edit-loop s p i b)))
|
||||
(define invalid-movement
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "no such position~%")
|
||||
(edit-loop s p i b)))
|
||||
(define unrecognized-command-syntax
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "unrecognized command syntax~%")
|
||||
(edit-loop s p i b)))
|
||||
(define search-failed
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "search failed~%")
|
||||
(edit-loop s p i b)))
|
||||
(define no-previous-find
|
||||
;; complain and continue.
|
||||
(lambda (s p i b)
|
||||
(printf "no previous find command~%")
|
||||
(edit-loop s p i b)))
|
||||
(define edit-loop
|
||||
;; read command and process.
|
||||
(lambda (s p i b)
|
||||
(let ([x (begin (printf "edit> ") (read))])
|
||||
(cond
|
||||
[(eof-object? x) (newline)] ; need newline after eof
|
||||
[(eq? x 'q)] ; do not need newline after q
|
||||
[(eq? x 'p) (wr s p i b)]
|
||||
[(eq? x '?) (wrlev s p i b)]
|
||||
[(eq? x 'pp) (pp s p i b)]
|
||||
[(eq? x '??) (pplev s p i b)]
|
||||
[(or (integer? x) (eqv? x '*)) (move x s p i b wrlev)]
|
||||
[(eq? x 't)
|
||||
(let f ([p p] [i i] [b b])
|
||||
(if (null? b)
|
||||
(wrlev s p i b)
|
||||
(apply f b)))]
|
||||
[(eq? x 'b)
|
||||
(if (pair? b)
|
||||
(apply wrlev s b)
|
||||
(invalid-movement s p i b))]
|
||||
[(eq? x 'n)
|
||||
(let ([n (maxref p)])
|
||||
(if (and (not (eq? i '*)) (or (not n) (< i n)))
|
||||
(wrlev s p (+ i 1) b)
|
||||
(invalid-movement s p i b)))]
|
||||
[(eq? x 'pr)
|
||||
(if (and (not (eq? i '*)) (> i 0))
|
||||
(wrlev s p (- i 1) b)
|
||||
(invalid-movement s p i b))]
|
||||
[(or (eq? x 'f) (cmdeq? x '(f)))
|
||||
(if s
|
||||
(find s p i b)
|
||||
(no-previous-find s p i b))]
|
||||
[(cmdeq? x '(f x))
|
||||
(find (cons (cadr x) '()) p i b)]
|
||||
[(and (cmdeq? x '(r x x))
|
||||
(or (integer? (cadr x)) (eq? (cadr x) '*)))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-set! p0 i0 (caddr x))))
|
||||
(wrlev s p i b)]
|
||||
[(cmdeq? x '(s x x))
|
||||
(list-set! p i (subst! (caddr x) (cadr x) (list-ref p i)))
|
||||
(wrlev s p i b)]
|
||||
[(and (cmdeq? x '(d x)) (eqv? (cadr x) 0))
|
||||
(list-set! p i '())
|
||||
(wrlev s p i b)]
|
||||
[(and (cmdeq? x '(d x)) (eq? (cadr x) '*))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(set-cdr! (last-pair p0) '())
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(d x)) (integer? (cadr x)))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-set! p i (list-cut! p0 i0))
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(dp x)) (eqv? (cadr x) 0))
|
||||
(let ([e (list-ref p i)])
|
||||
(if (and (pair? e) (null? (cdr e)))
|
||||
(begin (list-set! p i (car e))
|
||||
(wrlev s p i b))
|
||||
(cannot-dp-zero s p i b)))]
|
||||
[(and (cmdeq? x '(dp x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(let ([e0 (list-ref p0 i0)])
|
||||
(if (or (proper-list? e0)
|
||||
(and (pair? e0) (eqv? i0 (maxref p0))))
|
||||
(begin (if (null? e0)
|
||||
(list-set! p i (list-cut! p0 i0))
|
||||
(list-splice! p0 i0 e0))
|
||||
(wrlev s p i b))
|
||||
(not-a-proper-list s p i b)))))]
|
||||
[(and (or (cmdeq? x '(ap x)) (cmdeq? x '(ap x x)))
|
||||
(memv (cadr x) '(0 *)))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-set! p0 i0 (list (list-ref p0 i0)))
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(ap x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0)))
|
||||
(eq? (caddr x) '*))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-ap*! p0 i0)
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(ap x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0)))
|
||||
(and (integer? (caddr x)) (not (= (caddr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(move (caddr x) s p i b
|
||||
(lambda (s1 p1 i1 b1)
|
||||
(if (>= i1 i0)
|
||||
(begin (list-ap! p0 i0 i1)
|
||||
(wrlev s p i b))
|
||||
(pos2-before-pos1 s p i b))))))]
|
||||
[(and (cmdeq? x '(ib x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-splice! p0 i0 (list (caddr x) (list-ref p0 i0)))
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(ia x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-splice! p0 i0 (list (list-ref p0 i0) (caddr x)))
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(sb x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-splice! p0 i0
|
||||
(append (caddr x) (list (list-ref p0 i0))))
|
||||
(wrlev s p i b)))]
|
||||
[(and (cmdeq? x '(sa x x))
|
||||
(and (integer? (cadr x)) (not (= (cadr x) 0))))
|
||||
(move (cadr x) s p i b
|
||||
(lambda (s0 p0 i0 b0)
|
||||
(list-splice! p0 i0 (cons (list-ref p0 i0) (caddr x)))
|
||||
(wrlev s p i b)))]
|
||||
[else
|
||||
(unrecognized-command-syntax s p i b)]))))
|
||||
(set! edit
|
||||
;; set up keyboard interrupt handler and go.
|
||||
(lambda (e)
|
||||
(let ([p (cons e '())])
|
||||
(let ([k (call/cc (lambda (k) k))]) ; return here on interrupt
|
||||
(parameterize ([keyboard-interrupt-handler
|
||||
(lambda ()
|
||||
(printf "reset~%")
|
||||
(k k))])
|
||||
(wrlev #f p 0 '())
|
||||
(car p)))))))
|
570
ta6ob/examples/ez-grammar-test.ss
Normal file
570
ta6ob/examples/ez-grammar-test.ss
Normal file
|
@ -0,0 +1,570 @@
|
|||
;;; Copyright 2017 Cisco Systems, Inc.
|
||||
;;;
|
||||
;;; 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.
|
||||
|
||||
;;; This file contains a sample parser defined via the ez-grammar system
|
||||
;;; and a simple test of the parser.
|
||||
|
||||
;;; This file is organized as follows:
|
||||
;;;
|
||||
;;; - (streams) library providing the required exports for ez-grammar and
|
||||
;;; the parser.
|
||||
;;;
|
||||
;;; - (state-case) library exporting the state-case macro, copped from
|
||||
;;; cmacros.ss, for use by the lexer.
|
||||
;;;
|
||||
;;; - (lexer) library providing a simple lexer that reads characters
|
||||
;;; from a port and produces a corresponding stream of tokens.
|
||||
;;;
|
||||
;;; - (parser) library providing the sample parser.
|
||||
;;;
|
||||
;;; - ez-grammar-test procedure that tests the sample parser.
|
||||
;;;
|
||||
;;; Instructions for running the test are at the end of this file.
|
||||
|
||||
(library (streams)
|
||||
(export stream-cons stream-car stream-cdr stream-nil stream-null?
|
||||
stream-map stream stream-append2 stream-append-all stream-last-forced)
|
||||
(import (chezscheme))
|
||||
|
||||
(define stream-cons
|
||||
(lambda (x thunk)
|
||||
(cons x thunk)))
|
||||
|
||||
(define stream-car
|
||||
(lambda (x)
|
||||
(car x)))
|
||||
|
||||
(define stream-cdr
|
||||
(lambda (x)
|
||||
(when (procedure? (cdr x)) (set-cdr! x ((cdr x))))
|
||||
(cdr x)))
|
||||
|
||||
(define stream-nil '())
|
||||
|
||||
(define stream-null?
|
||||
(lambda (x)
|
||||
(null? x)))
|
||||
|
||||
(define stream-map
|
||||
(lambda (f x)
|
||||
(if (stream-null? x)
|
||||
'()
|
||||
(stream-cons (f (stream-car x))
|
||||
(lambda ()
|
||||
(stream-map f (stream-cdr x)))))))
|
||||
|
||||
(define stream
|
||||
(lambda xs
|
||||
xs))
|
||||
|
||||
(define stream-append2
|
||||
(lambda (xs thunk)
|
||||
(if (null? xs)
|
||||
(thunk)
|
||||
(stream-cons (stream-car xs)
|
||||
(lambda ()
|
||||
(stream-append2 (stream-cdr xs) thunk))))))
|
||||
|
||||
(define stream-append-all
|
||||
(lambda (stream$) ;; stream of streams
|
||||
(if (stream-null? stream$)
|
||||
stream$
|
||||
(stream-append2 (stream-car stream$)
|
||||
(lambda () (stream-append-all (stream-cdr stream$)))))))
|
||||
|
||||
(define stream-last-forced
|
||||
(lambda (x)
|
||||
(and (not (null? x))
|
||||
(let loop ([x x])
|
||||
(let ([next (cdr x)])
|
||||
(if (pair? next)
|
||||
(loop next)
|
||||
(car x)))))))
|
||||
)
|
||||
|
||||
(library (state-case)
|
||||
(export state-case eof)
|
||||
(import (chezscheme))
|
||||
|
||||
;;; from Chez Scheme Version 9.5.1 cmacros.ss
|
||||
(define-syntax state-case
|
||||
(lambda (x)
|
||||
(define state-case-test
|
||||
(lambda (cvar k)
|
||||
(with-syntax ((cvar cvar))
|
||||
(syntax-case k (-)
|
||||
(char
|
||||
(char? (datum char))
|
||||
#'(char=? cvar char))
|
||||
((char1 - char2)
|
||||
(and (char? (datum char1)) (char? (datum char2)))
|
||||
#'(char<=? char1 cvar char2))
|
||||
(predicate
|
||||
(identifier? #'predicate)
|
||||
#'(predicate cvar))))))
|
||||
(define state-case-help
|
||||
(lambda (cvar clauses)
|
||||
(syntax-case clauses (else)
|
||||
(((else exp1 exp2 ...))
|
||||
#'(begin exp1 exp2 ...))
|
||||
((((k ...) exp1 exp2 ...) . more)
|
||||
(with-syntax (((test ...)
|
||||
(map (lambda (k) (state-case-test cvar k))
|
||||
#'(k ...)))
|
||||
(rest (state-case-help cvar #'more)))
|
||||
#'(if (or test ...) (begin exp1 exp2 ...) rest)))
|
||||
(((k exp1 exp2 ...) . more)
|
||||
(with-syntax ((test (state-case-test cvar #'k))
|
||||
(rest (state-case-help cvar #'more)))
|
||||
#'(if test (begin exp1 exp2 ...) rest))))))
|
||||
(syntax-case x (eof)
|
||||
((_ cvar (eof exp1 exp2 ...) more ...)
|
||||
(identifier? #'cvar)
|
||||
(with-syntax ((rest (state-case-help #'cvar #'(more ...))))
|
||||
#'(if (eof-object? cvar)
|
||||
(begin exp1 exp2 ...)
|
||||
rest))))))
|
||||
|
||||
(define-syntax eof
|
||||
(lambda (x)
|
||||
(syntax-error x "misplaced aux keyword")))
|
||||
)
|
||||
|
||||
(library (lexer)
|
||||
(export token? token-type token-value token-bfp token-efp lexer)
|
||||
(import (chezscheme) (state-case) (streams))
|
||||
|
||||
(define-record-type token
|
||||
(nongenerative)
|
||||
(fields type value bfp efp))
|
||||
|
||||
;; test lexer
|
||||
(define lexer
|
||||
(lambda (fn ip)
|
||||
(define $prev-pos 0)
|
||||
(define $pos 0)
|
||||
(define ($get-char)
|
||||
(set! $pos (+ $pos 1))
|
||||
(get-char ip))
|
||||
(define ($unread-char c)
|
||||
(set! $pos (- $pos 1))
|
||||
(unread-char c ip))
|
||||
(define ($ws!) (set! $prev-pos $pos))
|
||||
(define ($make-token type value)
|
||||
(let ([tok (make-token type value $prev-pos $pos)])
|
||||
(set! $prev-pos $pos)
|
||||
tok))
|
||||
(define ($lex-error c)
|
||||
(errorf #f "unexpected ~a at character ~s of ~a"
|
||||
(if (eof-object? c)
|
||||
"eof"
|
||||
(format "character '~c'" c))
|
||||
$pos fn))
|
||||
(define-syntax lex-error
|
||||
(syntax-rules ()
|
||||
[(_ ?c)
|
||||
(let ([c ?c])
|
||||
($lex-error c)
|
||||
(void))]))
|
||||
(let-values ([(sp get-buf) (open-string-output-port)])
|
||||
(define (return-token type value)
|
||||
(stream-cons ($make-token type value) lex))
|
||||
(module (identifier-initial? identifier-subsequent?)
|
||||
(define identifier-initial?
|
||||
(lambda (c)
|
||||
(char-alphabetic? c)))
|
||||
(define identifier-subsequent?
|
||||
(lambda (c)
|
||||
(or (char-alphabetic? c)
|
||||
(char-numeric? c)))))
|
||||
(define-syntax define-state-case
|
||||
(syntax-rules ()
|
||||
[(_ ?def-id ?char-id clause ...)
|
||||
(define (?def-id)
|
||||
(let ([?char-id ($get-char)])
|
||||
(state-case ?char-id clause ...)))]))
|
||||
(define-state-case lex c
|
||||
[eof stream-nil]
|
||||
[char-whitespace? ($ws!) (lex)]
|
||||
[char-numeric? (lex-number c)]
|
||||
[#\/ (seen-slash)]
|
||||
[identifier-initial? (put-char sp c) (lex-identifier)]
|
||||
[#\( (return-token 'lparen #\()]
|
||||
[#\) (return-token 'rparen #\))]
|
||||
[#\! (return-token 'bang #\!)]
|
||||
[#\+ (seen-plus)]
|
||||
[#\- (seen-minus)]
|
||||
[#\= (seen-equals)]
|
||||
[#\* (return-token 'binop '*)]
|
||||
[#\, (return-token 'sep #\,)]
|
||||
[#\; (return-token 'sep #\;)]
|
||||
[else (lex-error c)])
|
||||
(module (lex-identifier)
|
||||
(define (id) (return-token 'id (string->symbol (get-buf))))
|
||||
(define-state-case next c
|
||||
[eof (id)]
|
||||
[identifier-subsequent? (put-char sp c) (next)]
|
||||
[else ($unread-char c) (id)])
|
||||
(define (lex-identifier) (next)))
|
||||
(define-state-case seen-plus c
|
||||
[eof (return-token 'binop '+)]
|
||||
[char-numeric? (lex-signed-number #\+ c)]
|
||||
[else (return-token 'binop '+)])
|
||||
(define-state-case seen-minus c
|
||||
[eof (return-token 'binop '-)]
|
||||
[char-numeric? (lex-signed-number #\- c)]
|
||||
[else (return-token 'binop '-)])
|
||||
(define-state-case seen-equals c
|
||||
[eof (return-token 'binop '=)]
|
||||
[#\> (return-token 'big-arrow #f)]
|
||||
[else (return-token 'binop '=)])
|
||||
(module (lex-number lex-signed-number)
|
||||
(define (finish-number)
|
||||
(let ([str (get-buf)])
|
||||
(let ([n (string->number str 10)])
|
||||
(unless n (errorf 'lexer "unexpected number literal ~a" str))
|
||||
(return-token 'integer n))))
|
||||
(define (num)
|
||||
(let ([c ($get-char)])
|
||||
(state-case c
|
||||
[eof (finish-number)]
|
||||
[char-numeric? (put-char sp c) (num)]
|
||||
[else ($unread-char c) (finish-number)])))
|
||||
(define (lex-signed-number s c)
|
||||
(put-char sp s)
|
||||
(lex-number c))
|
||||
(define (lex-number c)
|
||||
(state-case c
|
||||
[eof (assert #f)]
|
||||
[char-numeric? (put-char sp c) (num)]
|
||||
[else (assert #f)])))
|
||||
(define-state-case seen-slash c
|
||||
[eof (return-token 'binop '/)]
|
||||
[#\* (lex-block-comment)]
|
||||
[#\/ (lex-comment)]
|
||||
[else (return-token 'binop '/)])
|
||||
(define-state-case lex-comment c
|
||||
[eof (lex)]
|
||||
[#\newline ($ws!) (lex)]
|
||||
[else (lex-comment)])
|
||||
(define (lex-block-comment)
|
||||
(define-state-case maybe-end-comment c
|
||||
[eof (lex-error c)]
|
||||
[#\/ ($ws!) (lex)]
|
||||
[else (lex-block-comment)])
|
||||
(let ([c ($get-char)])
|
||||
(state-case c
|
||||
[eof (lex-error c)]
|
||||
[#\* (maybe-end-comment)]
|
||||
[else (lex-block-comment)])))
|
||||
(lex))))
|
||||
|
||||
(record-writer (record-type-descriptor token)
|
||||
(lambda (x p wr)
|
||||
(put-char p #\[)
|
||||
(wr (token-type x) p)
|
||||
(put-char p #\,)
|
||||
(put-char p #\space)
|
||||
(wr (token-value x) p)
|
||||
(put-char p #\])
|
||||
(put-char p #\:)
|
||||
(wr (token-bfp x) p)
|
||||
(put-char p #\-)
|
||||
(wr (token-efp x) p)))
|
||||
)
|
||||
|
||||
(module parser ()
|
||||
(export parse *sfd*)
|
||||
(import (chezscheme) (streams) (lexer))
|
||||
(define *sfd*)
|
||||
(module (define-grammar is sat parse-consumed-all? parse-result-value grammar-trace make-src)
|
||||
(define (sep->parser sep)
|
||||
(cond
|
||||
[(char? sep) (sat (lambda (x) (and (eq? (token-type x) 'sep) (eq? (token-value x) sep))))]
|
||||
[(symbol? sep) (sat (lambda (x) (eq? (token-type x) sep)))]
|
||||
[else (errorf "don't know how to parse separator: ~s" sep)]))
|
||||
(meta define (constant? x) (let ([x (syntax->datum x)]) (or (string? x) (char? x))))
|
||||
(define constant->parser
|
||||
(lambda (const)
|
||||
(define (token-sat type val)
|
||||
(sat (lambda (x)
|
||||
(let ([ans (and (token? x) (eqv? (token-type x) type) (eqv? (token-value x) val))])
|
||||
(when (grammar-trace) (printf " ~s is [~s, ~a]? => ~s~%" x type val ans))
|
||||
ans))))
|
||||
(if (string? const)
|
||||
(case const
|
||||
[else (token-sat 'id (string->symbol const))])
|
||||
(case const
|
||||
[#\( (token-sat 'lparen const)]
|
||||
[#\) (token-sat 'rparen const)]
|
||||
[#\! (token-sat 'bang const)]
|
||||
[else (errorf 'constant->parser "don't know how to construct a parser for ~a" const)]))))
|
||||
(meta define (constant->markdown k)
|
||||
(format "~a" k))
|
||||
(define binop->parser
|
||||
(lambda (binop)
|
||||
(define (binop-sat type val)
|
||||
(is val
|
||||
(where [x <- item] (and (token? x) (eq? (token-type x) type) (eq? (token-value x) val)))))
|
||||
(define (unexpected) (errorf 'binop->parser "don't know how to construct a parser for ~a" binop))
|
||||
(if (string? binop)
|
||||
(binop-sat 'binop
|
||||
(case binop
|
||||
["=" '=]
|
||||
["+" '+]
|
||||
["-" '-]
|
||||
["*" '*]
|
||||
["/" '/]
|
||||
[else (unexpected)]))
|
||||
(unexpected))))
|
||||
(define make-src
|
||||
(lambda (bfp efp)
|
||||
(make-source-object *sfd* bfp efp)))
|
||||
(include "ez-grammar.ss"))
|
||||
|
||||
(define token
|
||||
(case-lambda
|
||||
[(type)
|
||||
(is (token-value x)
|
||||
(where
|
||||
[x <- (sat (lambda (x)
|
||||
(let ([ans (eq? (token-type x) type)])
|
||||
(when (grammar-trace) (printf " ~s is ~s? => ~s~%" x type ans))
|
||||
ans)))]))]
|
||||
[(type val)
|
||||
(is (token-value x)
|
||||
(where
|
||||
[x <- (sat (lambda (x)
|
||||
(let ([ans (and
|
||||
(eq? (token-type x) type)
|
||||
(eqv? (token-value x) val))])
|
||||
(when (grammar-trace) (printf " ~s is [~s, ~s]? => ~s~%" x type val ans))
|
||||
ans)))]))]))
|
||||
|
||||
(define identifier (token 'id))
|
||||
|
||||
(define integer (token 'integer))
|
||||
|
||||
(define-grammar expr (markdown-directory ".")
|
||||
(TERMINALS
|
||||
(identifier (x y) (DESCRIPTION ("An identifier is ...")))
|
||||
(integer (i) (DESCRIPTION ("An integer literal is ..."))))
|
||||
(expr (e)
|
||||
(BINOP src ((RIGHT "=") (LEFT "+" "-") (LEFT "*" "/")) t) =>
|
||||
(lambda (src op x y)
|
||||
(make-annotation `(,op ,x ,y) src `(,op ,(annotation-stripped x) ,(annotation-stripped y)))))
|
||||
(term (t)
|
||||
[test-SEP+ :: src "sepplus" #\( (SEP+ e #\;) #\) =>
|
||||
(lambda (src e+)
|
||||
(make-annotation `(SEP+ ,@e+) src `(SEP+ ,@(map annotation-stripped e+))))]
|
||||
[test-SEP* :: src "sepstar" #\( (SEP* e #\,) #\) =>
|
||||
(lambda (src e*)
|
||||
(make-annotation `(SEP* ,@e*) src `(SEP* ,@(map annotation-stripped e*))))]
|
||||
[test-OPT :: src "opt" #\( (OPT e #f) #\) =>
|
||||
(lambda (src maybe-e)
|
||||
(if maybe-e
|
||||
(make-annotation `(OPT ,maybe-e) src `(OPT ,(annotation-stripped maybe-e)))
|
||||
(make-annotation `(OPT) src `(OPT))))]
|
||||
[test-K+ :: src "kplus" #\( (K+ e) #\) =>
|
||||
(lambda (src e+)
|
||||
(make-annotation `(K+ ,@e+) src `(K+ ,@(map annotation-stripped e+))))]
|
||||
[test-K* :: src "kstar" #\( (K* e) #\) =>
|
||||
(lambda (src e*)
|
||||
(make-annotation `(K* ,@e*) src `(K* ,@(map annotation-stripped e*))))]
|
||||
[varref :: src x =>
|
||||
(lambda (src id)
|
||||
(make-annotation `(id ,id) src `(id ,id)))]
|
||||
[intref :: src i =>
|
||||
(lambda (src n)
|
||||
(make-annotation `(int ,n) src `(int ,n)))]
|
||||
[group :: src #\( e #\) =>
|
||||
(lambda (src e)
|
||||
`(group ,src ,e))]))
|
||||
|
||||
(define parse
|
||||
(lambda (fn ip)
|
||||
(let ([token-stream (lexer fn ip)])
|
||||
(define (oops)
|
||||
(let ([last-token (stream-last-forced token-stream)])
|
||||
(if last-token
|
||||
(errorf 'parse "parse error at or before character ~s of ~a" (token-bfp last-token) fn)
|
||||
(errorf 'parse "no expressions found in ~a" fn))))
|
||||
;;; return the first result, if any, for which the input stream was entirely consumed.
|
||||
(let loop ([res* (expr token-stream)])
|
||||
(if (null? res*)
|
||||
(oops)
|
||||
(let ([res (car res*)])
|
||||
(if (parse-consumed-all? res)
|
||||
(parse-result-value res)
|
||||
(loop (cdr res*))))))))))
|
||||
|
||||
(define run
|
||||
(lambda (fn)
|
||||
(import parser)
|
||||
(let* ([ip (open-file-input-port fn)]
|
||||
[sfd (make-source-file-descriptor fn ip #t)]
|
||||
[ip (transcoded-port ip (native-transcoder))])
|
||||
(fluid-let ([*sfd* sfd])
|
||||
(eval
|
||||
`(let ()
|
||||
(define-syntax define-ops
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
[(_ op ...)
|
||||
#`(begin
|
||||
(define-syntax op
|
||||
(lambda (x)
|
||||
(let ([src (annotation-source (syntax->annotation x))])
|
||||
(with-syntax ([bfp (source-object-bfp src)] [efp (source-object-efp src)])
|
||||
(syntax-case x ()
|
||||
[(_ e (... ...)) #'`(op (bfp . efp) ,e (... ...))])))))
|
||||
...)])))
|
||||
(define-ops SEP+ SEP* OPT K+ K* id int group)
|
||||
(define-ops = + - * /)
|
||||
(define x 'x)
|
||||
(define y 'y)
|
||||
(define z 'z)
|
||||
,(dynamic-wind
|
||||
void
|
||||
(lambda () (parse fn ip))
|
||||
(lambda () (close-input-port ip)))))))))
|
||||
|
||||
(define (ez-grammar-test)
|
||||
(define n 0)
|
||||
(define test
|
||||
(lambda (line* okay?)
|
||||
(set! n (+ n 1))
|
||||
(let ([fn (format "testfile~s" n)])
|
||||
(with-output-to-file fn
|
||||
(lambda () (for-each (lambda (line) (printf "~a\n" line)) line*))
|
||||
'replace)
|
||||
(let ([result (parameterize ([compile-profile #t] [compile-interpret-simple #f])
|
||||
(guard (c [else c]) (run fn)))])
|
||||
(guard (c [else #f]) (profile-dump-html))
|
||||
(delete-file fn)
|
||||
(delete-file "profile.html")
|
||||
(delete-file (format "~a.html" fn))
|
||||
(unless (okay? result)
|
||||
(printf "test ~s failed\n" n)
|
||||
(printf " test code:")
|
||||
(for-each (lambda (line) (printf " ~a\n" line)) line*)
|
||||
(printf " result:\n ")
|
||||
(if (condition? result)
|
||||
(begin (display-condition result) (newline))
|
||||
(parameterize ([pretty-initial-indent 4])
|
||||
(pretty-print result)))
|
||||
(newline))))))
|
||||
|
||||
(define-syntax returns
|
||||
(syntax-rules ()
|
||||
[(_ k) (lambda (x) (equal? x 'k))]))
|
||||
|
||||
(define-syntax oops
|
||||
(syntax-rules ()
|
||||
[(_ (c) e1 e2 ...)
|
||||
(lambda (c) (and (condition? c) e1 e2 ...))]))
|
||||
|
||||
(test
|
||||
'(
|
||||
"1347"
|
||||
)
|
||||
(returns
|
||||
(int (0 . 4) 1347)))
|
||||
|
||||
(test
|
||||
'(
|
||||
"3 /*"
|
||||
)
|
||||
(oops (c)
|
||||
(equal? (condition-message c) "unexpected ~a at character ~s of ~a")
|
||||
(equal? (condition-irritants c) '("eof" 6 "testfile2"))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"3 / 4 + 5 opt(6)"
|
||||
)
|
||||
(oops (c)
|
||||
(equal? (condition-message c) "parse error at or before character ~s of ~a")
|
||||
(equal? (condition-irritants c) '(10 "testfile3"))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"x = y = 5"
|
||||
)
|
||||
(returns
|
||||
(=
|
||||
(0 . 9)
|
||||
(id (0 . 1) x)
|
||||
(= (4 . 9) (id (4 . 5) y) (int (8 . 9) 5)))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"x = y = x + 5 - z * 7 + 8 / z"
|
||||
)
|
||||
(returns
|
||||
(=
|
||||
(0 . 29)
|
||||
(id (0 . 1) x)
|
||||
(=
|
||||
(4 . 29)
|
||||
(id (4 . 5) y)
|
||||
(+
|
||||
(8 . 29)
|
||||
(-
|
||||
(8 . 21)
|
||||
(+ (8 . 13) (id (8 . 9) x) (int (12 . 13) 5))
|
||||
(* (16 . 21) (id (16 . 17) z) (int (20 . 21) 7)))
|
||||
(/ (24 . 29) (int (24 . 25) 8) (id (28 . 29) z)))))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"opt(opt(opt()))"
|
||||
)
|
||||
(returns
|
||||
(OPT (0 . 15) (OPT (4 . 14) (OPT (8 . 13))))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"kstar(3 4 kplus(1 2 3 kstar()))"
|
||||
)
|
||||
(returns
|
||||
(K* (0 . 31)
|
||||
(int (6 . 7) 3)
|
||||
(int (8 . 9) 4)
|
||||
(K+ (10 . 30)
|
||||
(int (16 . 17) 1)
|
||||
(int (18 . 19) 2)
|
||||
(int (20 . 21) 3)
|
||||
(K* (22 . 29))))))
|
||||
|
||||
(test
|
||||
'(
|
||||
"sepplus( opt() ; opt(5) ; sepstar(17, 34) ; sepstar())"
|
||||
)
|
||||
(returns
|
||||
(SEP+ (0 . 54)
|
||||
(OPT (9 . 14))
|
||||
(OPT (17 . 23) (int (21 . 22) 5))
|
||||
(SEP* (26 . 41) (int (34 . 36) 17) (int (38 . 40) 34))
|
||||
(SEP* (44 . 53)))))
|
||||
|
||||
(delete-file "expr.md")
|
||||
(printf "~s tests ran\n" n)
|
||||
)
|
||||
|
||||
#!eof
|
||||
|
||||
The following should print only "<n> tests ran".
|
||||
|
||||
echo '(ez-grammar-test)' | ../bin/scheme -q ez-grammar-test.ss
|
759
ta6ob/examples/ez-grammar.ss
Normal file
759
ta6ob/examples/ez-grammar.ss
Normal file
|
@ -0,0 +1,759 @@
|
|||
;;; Copyright 2017 Cisco Systems, Inc.
|
||||
;;;
|
||||
;;; 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.
|
||||
|
||||
;;; See http://www.cs.nott.ac.uk/~pszgmh/monparsing.pdf for origins of
|
||||
;;; some of the monadic combinators.
|
||||
|
||||
;;; Authors: Jon Rossie, Kent Dybvig
|
||||
|
||||
;;; The define-grammar form produces a parser:
|
||||
;;;
|
||||
;;; parser : token-stream -> ((Tree token-stream) ...)
|
||||
;;;
|
||||
;;; If the return value is the empty list, a parse error occurred.
|
||||
;;; If the return value has multiple elements, the parse was ambiguous.
|
||||
;;; The token-stream in each (Tree token-stream) is the tail of the
|
||||
;;; input stream that begins with the last token consumed by the parse.
|
||||
;;; This gives the consumer access to both the first and last token,
|
||||
;;; allowing it to determine cheaply the extent of the parse, including
|
||||
;;; source locations if source information is attached to the tokens.
|
||||
|
||||
;;; Internally, backtracking occurs whenever a parser return value
|
||||
;;; has multiple elements.
|
||||
|
||||
;;; This code should be included into a lexical context that supplies:
|
||||
;;;
|
||||
;;; token-bfp : token -> token's beginning file position
|
||||
;;; token-efp : token -> token's ending file position
|
||||
;;; meta constant? : syntax-object -> boolean
|
||||
;;; sep->parser : sep -> parser
|
||||
;;; constant->parser : constant -> parser
|
||||
;;; make-src : bfp x efp -> src. bfp > efp => no tokens consumed.
|
||||
;;;
|
||||
;;; See ez-grammar-test.ss for an example.
|
||||
|
||||
(module (define-grammar
|
||||
is sat item peek seq ++ +++ many many+ ?
|
||||
parse-consumed-all? parse-result-value parse-result-unused
|
||||
grammar-trace
|
||||
)
|
||||
(import (streams))
|
||||
|
||||
(define grammar-trace (make-parameter #f))
|
||||
|
||||
(define-record-type parse-result
|
||||
(nongenerative parse-result)
|
||||
(sealed #t)
|
||||
(fields value unused))
|
||||
|
||||
;; to enable $trace-is to determine the ending file position (efp) of a parse
|
||||
;; form, the input stream actually points to the preceding token rather than
|
||||
;; to the current token. the next few routines establish, maintain, and deal
|
||||
;; with that invariant.
|
||||
(define make-top-level-parser
|
||||
(lambda (parser)
|
||||
(lambda (inp)
|
||||
(parser (stream-cons 'dummy-token inp)))))
|
||||
|
||||
(define preceding-token
|
||||
(lambda (inp)
|
||||
(stream-car inp)))
|
||||
|
||||
(define current-token
|
||||
(lambda (inp)
|
||||
(stream-car (stream-cdr inp))))
|
||||
|
||||
(define remaining-tokens
|
||||
(lambda (inp)
|
||||
(stream-cdr inp)))
|
||||
|
||||
(define no-more-tokens?
|
||||
(lambda (inp)
|
||||
(stream-null? (stream-cdr inp))))
|
||||
|
||||
(define parse-consumed-all?
|
||||
(lambda (res)
|
||||
(no-more-tokens? (parse-result-unused res))))
|
||||
|
||||
;; A parser generator
|
||||
(define result
|
||||
(lambda (v)
|
||||
;; this is a parser that ignores its input and produces v
|
||||
(lambda (inp)
|
||||
(stream (make-parse-result v inp)))))
|
||||
|
||||
;; A parse that always generates a parse error
|
||||
(define zero
|
||||
(lambda (inp)
|
||||
stream-nil))
|
||||
|
||||
;; For a non-empty stream, successfully consume the first element
|
||||
(define item
|
||||
(lambda (inp)
|
||||
(cond
|
||||
[(no-more-tokens? inp) '()]
|
||||
[else
|
||||
(stream (make-parse-result (current-token inp) (remaining-tokens inp)))])))
|
||||
|
||||
(define (peek p)
|
||||
(lambda (inp)
|
||||
(stream-map (lambda (pr)
|
||||
(make-parse-result (parse-result-value pr) inp))
|
||||
(p inp))))
|
||||
|
||||
;;------------------------------------------
|
||||
|
||||
(define bind
|
||||
(lambda (parser receiver)
|
||||
(lambda (inp)
|
||||
(let ([res* (parser inp)])
|
||||
(stream-append-all
|
||||
(stream-map (lambda (res)
|
||||
((receiver (parse-result-value res))
|
||||
(parse-result-unused res)))
|
||||
res*))))))
|
||||
|
||||
;; monad comprehensions
|
||||
(define-syntax is-where ; used by is and trace-is
|
||||
(lambda (x)
|
||||
(syntax-case x (where <-)
|
||||
[(_ expr (where)) #'expr]
|
||||
[(_ expr (where [x <- p] clauses ...))
|
||||
#'(bind p (lambda (x) (is-where expr (where clauses ...))))]
|
||||
[(_ expr (where pred clauses ...))
|
||||
#'(if pred (is-where expr (where clauses ...)) zero)]
|
||||
[(_ expr where-clause) (syntax-error #'where-clause)])))
|
||||
(indirect-export is-where bind)
|
||||
|
||||
(define-syntax is
|
||||
(syntax-rules ()
|
||||
[(_ expr where-clause) (is-where (result expr) where-clause)]))
|
||||
(indirect-export is is-where)
|
||||
|
||||
(module (trace-is)
|
||||
(define ($trace-is name proc head)
|
||||
(lambda (unused)
|
||||
(let ([res (proc (token-bfp (current-token head)) (token-efp (preceding-token unused)))])
|
||||
(when (and name (grammar-trace)) (printf "<<~s = ~s~%" name res))
|
||||
(stream (make-parse-result res unused)))))
|
||||
|
||||
(define-syntax trace-is
|
||||
(syntax-rules ()
|
||||
[(_ name proc-expr where-clause)
|
||||
(lambda (inp) ((is-where ($trace-is 'name proc-expr inp) where-clause) inp))]))
|
||||
(indirect-export trace-is $trace-is))
|
||||
|
||||
(define (seq2 p q) (is (cons x y) (where [x <- p] [y <- q])))
|
||||
|
||||
(define seq
|
||||
(lambda p*
|
||||
(let loop ([p* p*])
|
||||
(cond
|
||||
[(null? p*) (result '())]
|
||||
[else (seq2 (car p*) (loop (cdr p*)))]))))
|
||||
|
||||
(define (sat pred) (is x (where [x <- item] (pred x))))
|
||||
|
||||
(define ++ ;; introduce ambiguity
|
||||
(lambda (p q)
|
||||
(lambda (inp)
|
||||
(stream-append2 (p inp)
|
||||
(lambda ()
|
||||
(q inp))))))
|
||||
|
||||
(define (many+ p) (is (cons x xs) (where [x <- p] [xs <- (many p)])))
|
||||
|
||||
(define (many p) (++ (many+ p) (result '())))
|
||||
|
||||
(define (? p) (++ (sat p) (result #f)))
|
||||
|
||||
(define (sepby1 p sep)
|
||||
(is (cons x xs)
|
||||
(where
|
||||
[x <- p]
|
||||
[xs <- (many (is y (where [_ <- sep] [y <- p])))])))
|
||||
|
||||
(define (sepby p sep) (++ (sepby1 p sep) (result '())))
|
||||
|
||||
(define (bracket open p close) (is x (where [_ <- open] [x <- p] [_ <- close])))
|
||||
|
||||
(define (optional p default)
|
||||
(lambda (inp)
|
||||
(let ([res (p inp)])
|
||||
(if (stream-null? res)
|
||||
(stream (make-parse-result default inp))
|
||||
res))))
|
||||
|
||||
(define (first p)
|
||||
(lambda (inp)
|
||||
(let ([res (p inp)])
|
||||
(if (stream-null? res)
|
||||
res
|
||||
(stream (stream-car res))))))
|
||||
|
||||
(define (+++ p q) (first (++ p q))) ;; choose first match, cut backtracking
|
||||
|
||||
(define-syntax infix-expression-parser
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
[(_ ((L/R ?op-parser) ...) ?term-parser ?receiver)
|
||||
(with-syntax ([(op-parser ...) (generate-temporaries #'(?op-parser ...))])
|
||||
#`(let ([op-parser ?op-parser] ... [term-parser (lambda (inp) (?term-parser inp))] [receiver ?receiver])
|
||||
#,(let f ([ls #'((L/R op-parser) ...)])
|
||||
(if (null? ls)
|
||||
#'term-parser
|
||||
#`(let ([next #,(f (cdr ls))])
|
||||
#,(syntax-case (car ls) (LEFT RIGHT)
|
||||
[(LEFT op-parser)
|
||||
#'(let ()
|
||||
(define-record-type frob (nongenerative) (sealed #t) (fields op y efp))
|
||||
(trace-is binop-left (lambda (bfp ignore-this-efp)
|
||||
(fold-left
|
||||
(lambda (x f) (receiver bfp (frob-efp f) (frob-op f) x (frob-y f)))
|
||||
x f*))
|
||||
(where
|
||||
[x <- next]
|
||||
[f* <- (rec this
|
||||
(optional
|
||||
(is (cons f f*)
|
||||
(where
|
||||
[f <- (trace-is binop-left-tail (lambda (bfp efp) (make-frob op y efp))
|
||||
(where
|
||||
[op <- op-parser]
|
||||
[y <- next]))]
|
||||
[f* <- this]))
|
||||
'()))])))]
|
||||
[(RIGHT op-parser)
|
||||
#'(rec this
|
||||
(+++
|
||||
(trace-is binop-right (lambda (bfp efp) (receiver bfp efp op x y))
|
||||
(where
|
||||
[x <- next]
|
||||
[op <- op-parser]
|
||||
[y <- this]))
|
||||
next))]))))))])))
|
||||
|
||||
(define (format-inp inp)
|
||||
(if (no-more-tokens? inp)
|
||||
"#<null-stream>"
|
||||
(format "(~s ...)" (current-token inp))))
|
||||
|
||||
(define-syntax define-grammar
|
||||
(lambda (x)
|
||||
(define-record-type grammar
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(fields title paragraph* section*))
|
||||
(define-record-type section
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(fields title paragraph* suppressed? clause*))
|
||||
(define-record-type clause
|
||||
(nongenerative)
|
||||
(fields id alias* before-paragraph* after-paragraph*))
|
||||
(define-record-type regular-clause
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent clause)
|
||||
(fields prod*))
|
||||
(define-record-type binop-clause
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent clause)
|
||||
(fields level* term receiver)
|
||||
(protocol
|
||||
(lambda (pargs->new)
|
||||
(lambda (nt alias* before-paragraph* after-paragraph* level* term src? receiver)
|
||||
((pargs->new nt alias* before-paragraph* after-paragraph*) level* term
|
||||
#`(lambda (bfp efp op x y)
|
||||
#,(if src?
|
||||
#`(#,receiver (make-src bfp efp) op x y)
|
||||
#`(#,receiver op x y))))))))
|
||||
(define-record-type terminal-clause
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(fields term*))
|
||||
(define-record-type terminal
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(fields parser alias* paragraph*))
|
||||
(define-record-type production
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(fields name paragraph* elt* receiver)
|
||||
(protocol
|
||||
(let ()
|
||||
(define (check-elts elt*)
|
||||
(for-each (lambda (elt) (unless (elt? elt) (errorf 'make-production "~s is not an elt" elt))) elt*))
|
||||
(lambda (new)
|
||||
(case-lambda
|
||||
[(name elt* receiver)
|
||||
(check-elts elt*)
|
||||
(new name #f elt* receiver)]
|
||||
[(name paragraph* elt* receiver)
|
||||
(check-elts elt*)
|
||||
(new name paragraph* elt* receiver)])))))
|
||||
(define-record-type elt
|
||||
(nongenerative))
|
||||
(define-record-type sep-elt
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent elt)
|
||||
(fields +? elt sep))
|
||||
(define-record-type opt-elt
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent elt)
|
||||
(fields elt default))
|
||||
(define-record-type kleene-elt
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent elt)
|
||||
(fields +? elt))
|
||||
(define-record-type constant-elt
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent elt)
|
||||
(fields k))
|
||||
(define-record-type id-elt
|
||||
(nongenerative)
|
||||
(sealed #t)
|
||||
(parent elt)
|
||||
(fields id))
|
||||
(define paragraph?
|
||||
(lambda (x)
|
||||
(syntax-case x (include)
|
||||
[(include filename) (string? (datum filename))]
|
||||
[(str ...) (andmap string? (datum (str ...)))])))
|
||||
(define (gentemp) (datum->syntax #'* (gensym)))
|
||||
(define (elt-temps elt*)
|
||||
(for-each (lambda (elt) (unless (elt? elt) (errorf 'elt-temps "~s is not an elt" elt))) elt*)
|
||||
(fold-left
|
||||
(lambda (t* elt)
|
||||
(if (constant-elt? elt) t* (cons (gentemp) t*)))
|
||||
'()
|
||||
elt*))
|
||||
(define (left-factor clause*)
|
||||
(define syntax-equal?
|
||||
(lambda (x y)
|
||||
(equal? (syntax->datum x) (syntax->datum y))))
|
||||
(define (elt-equal? x y)
|
||||
(cond
|
||||
[(sep-elt? x)
|
||||
(and (sep-elt? y)
|
||||
(eq? (sep-elt-+? x) (sep-elt-+? y))
|
||||
(elt-equal? (sep-elt-elt x) (sep-elt-elt y))
|
||||
(syntax-equal? (sep-elt-sep x) (sep-elt-sep y)))]
|
||||
[(opt-elt? x)
|
||||
(and (opt-elt? y)
|
||||
(elt-equal? (opt-elt-elt x) (opt-elt-elt y))
|
||||
(syntax-equal? (opt-elt-default x) (opt-elt-default y)))]
|
||||
[(kleene-elt? x)
|
||||
(and (kleene-elt? y)
|
||||
(eq? (kleene-elt-+? x) (kleene-elt-+? y))
|
||||
(elt-equal? (kleene-elt-elt x) (kleene-elt-elt y)))]
|
||||
[(constant-elt? x)
|
||||
(and (constant-elt? y)
|
||||
(syntax-equal? (constant-elt-k x) (constant-elt-k y)))]
|
||||
[(id-elt? x)
|
||||
(and (id-elt? y)
|
||||
(syntax-equal? (id-elt-id x) (id-elt-id y)))]
|
||||
[else #f]))
|
||||
(let lp1 ([clause* clause*] [new-clause* '()])
|
||||
(if (null? clause*)
|
||||
(reverse new-clause*)
|
||||
(let ([clause (car clause*)])
|
||||
(let lp2 ([prod* (regular-clause-prod* clause)] [new-prod* '()] [clause* (cdr clause*)])
|
||||
(if (null? prod*)
|
||||
(lp1 clause* (cons (make-regular-clause (clause-id clause) (clause-alias* clause) '() '() (reverse new-prod*)) new-clause*))
|
||||
(let ([prod (car prod*)] [prod* (cdr prod*)])
|
||||
(let ([elt* (production-elt* prod)])
|
||||
(if (null? elt*)
|
||||
(lp2 prod* (cons prod new-prod*) clause*)
|
||||
(let ([elt (car elt*)])
|
||||
(let-values ([(haves have-nots) (partition
|
||||
(lambda (prod)
|
||||
(let ([elt* (production-elt* prod)])
|
||||
(and (not (null? elt*))
|
||||
(elt-equal? (car elt*) elt))))
|
||||
prod*)])
|
||||
(if (null? haves)
|
||||
(lp2 prod* (cons prod new-prod*) clause*)
|
||||
(let ([haves (cons prod haves)])
|
||||
; "haves" start with the same elt. to cut down on the number of new
|
||||
; nonterminals and receiver overhead, find the largest common prefix
|
||||
(let ([prefix (cons elt
|
||||
(let f ([elt** (map production-elt* haves)])
|
||||
(let ([elt** (map cdr elt**)])
|
||||
(if (ormap null? elt**)
|
||||
'()
|
||||
(let ([elt (caar elt**)])
|
||||
(if (andmap (lambda (elt*) (elt-equal? (car elt*) elt)) (cdr elt**))
|
||||
(cons elt (f elt**))
|
||||
'()))))))])
|
||||
(let ([t (gentemp)] [n (length prefix)] [t* (elt-temps prefix)])
|
||||
(lp2 have-nots
|
||||
(cons (make-production #f (append prefix (list (make-id-elt t)))
|
||||
#`(lambda (bfp efp #,@t* p) (p bfp #,@t*)))
|
||||
new-prod*)
|
||||
(cons (make-regular-clause t '() '() '()
|
||||
(map (lambda (prod)
|
||||
(let ([elt* (list-tail (production-elt* prod) n)])
|
||||
(make-production (production-name prod) elt*
|
||||
(let ([u* (elt-temps elt*)])
|
||||
#`(lambda (bfp efp #,@u*)
|
||||
(lambda (bfp #,@t*)
|
||||
(#,(production-receiver prod) bfp efp #,@t* #,@u*)))))))
|
||||
haves))
|
||||
clause*)))))))))))))))))
|
||||
(define (make-env tclause* clause*)
|
||||
(let ([env (make-hashtable (lambda (x) (symbol-hash (syntax->datum x))) free-identifier=?)])
|
||||
(define (insert parser)
|
||||
(lambda (name)
|
||||
(let ([a (hashtable-cell env name #f)])
|
||||
(when (cdr a) (syntax-error name "duplicate terminal/non-terminal name"))
|
||||
(set-cdr! a parser))))
|
||||
(for-each
|
||||
(lambda (tclause)
|
||||
(for-each
|
||||
(lambda (term)
|
||||
(let ([parser (terminal-parser term)])
|
||||
(for-each (insert parser) (cons parser (terminal-alias* term)))))
|
||||
(terminal-clause-term* tclause)))
|
||||
tclause*)
|
||||
(for-each
|
||||
(lambda (clause)
|
||||
(let ([id (clause-id clause)])
|
||||
(for-each (insert id) (cons id (clause-alias* clause)))))
|
||||
clause*)
|
||||
env))
|
||||
(define (lookup id env)
|
||||
(or (hashtable-ref env id #f)
|
||||
(syntax-error id "unrecognized terminal or nonterminal")))
|
||||
(define (render-markdown name grammar mdfn env)
|
||||
(define (separators sep ls)
|
||||
(if (null? ls)
|
||||
""
|
||||
(apply string-append
|
||||
(cons (car ls)
|
||||
(map (lambda (s) (format "~a~a" sep s)) (cdr ls))))))
|
||||
(define (render-paragraph hard-leading-newline?)
|
||||
(lambda (paragraph)
|
||||
(define (md-text s)
|
||||
(list->string
|
||||
(fold-right
|
||||
(lambda (c ls)
|
||||
(case c
|
||||
[(#\\) (cons* c c ls)]
|
||||
[else (cons c ls)]))
|
||||
'()
|
||||
(string->list s))))
|
||||
(syntax-case paragraph (include)
|
||||
[(include filename)
|
||||
(string? (datum filename))
|
||||
(let ([text (call-with-port (open-input-file (datum filename)) get-string-all)])
|
||||
(unless (equal? text "")
|
||||
(if hard-leading-newline? (printf "\\\n") (newline))
|
||||
(display-string text)))]
|
||||
[(sentence ...)
|
||||
(andmap string? (datum (sentence ...)))
|
||||
(let ([sentence* (datum (sentence ...))])
|
||||
(unless (null? sentence*)
|
||||
(if hard-leading-newline? (printf "\\\n") (newline))
|
||||
(printf "~a\n" (separators " " (map md-text sentence*)))))])))
|
||||
(define (format-elt x)
|
||||
(cond
|
||||
[(sep-elt? x)
|
||||
(let* ([one (format-elt (sep-elt-elt x))]
|
||||
[sep (constant->markdown (syntax->datum (sep-elt-sep x)))]
|
||||
[seq (format "~a ~a `...`" one sep)])
|
||||
(if (sep-elt-+? x)
|
||||
seq
|
||||
(format "OPT(~a)" seq)))]
|
||||
[(opt-elt? x)
|
||||
(format "~a~~opt~~" (format-elt (opt-elt-elt x)))]
|
||||
[(kleene-elt? x)
|
||||
(let ([one (format-elt (kleene-elt-elt x))])
|
||||
(if (kleene-elt-+? x)
|
||||
(format "~a `...`" one)
|
||||
(format "OPT(~a)" one)))]
|
||||
[(constant-elt? x) (constant->markdown (syntax->datum (constant-elt-k x)))]
|
||||
[(id-elt? x) (format "[*~s*](#~s)"
|
||||
(syntax->datum (id-elt-id x))
|
||||
(syntax->datum (lookup (id-elt-id x) env)))]
|
||||
[else (errorf 'format-elt "unexpected elt ~s" x)]))
|
||||
(define (render-elt x)
|
||||
(printf " ~a" (format-elt x)))
|
||||
(define (render-production prod)
|
||||
(unless (null? (production-elt* prod))
|
||||
(printf " : ")
|
||||
(for-each render-elt (production-elt* prod))
|
||||
(printf "\n"))
|
||||
(when (and (null? (production-elt* prod))
|
||||
(not (null? (production-paragraph* prod))))
|
||||
(errorf 'render-production "empty production must not have description: ~a" (production-paragraph* prod)))
|
||||
(for-each (render-paragraph #t) (production-paragraph* prod)))
|
||||
(define (render-clause clause)
|
||||
(define (render-aliases alias*)
|
||||
(unless (null? alias*)
|
||||
(printf " \naliases: ~{*~a*~^, ~}\n" (map syntax->datum alias*))))
|
||||
(if (terminal-clause? clause)
|
||||
(for-each
|
||||
(lambda (term)
|
||||
(printf "\n#### *~a* {#~:*~a}\n" (syntax->datum (terminal-parser term)))
|
||||
(render-aliases (terminal-alias* term))
|
||||
(for-each (render-paragraph #f) (terminal-paragraph* term)))
|
||||
(terminal-clause-term* clause))
|
||||
(let ([id (syntax->datum (clause-id clause))])
|
||||
(printf "\n#### *~a* {#~:*~a}\n" id)
|
||||
(render-aliases (clause-alias* clause))
|
||||
(for-each (render-paragraph #f) (clause-before-paragraph* clause))
|
||||
(printf "\nsyntax:\n")
|
||||
(if (binop-clause? clause)
|
||||
(let ([level* (binop-clause-level* clause)])
|
||||
(let loop ([level* level*] [first? #t])
|
||||
(unless (null? level*)
|
||||
(let ([level (syntax->datum (car level*))] [level* (cdr level*)])
|
||||
(let ([L/R (car level)] [op* (cdr level)])
|
||||
(printf " : _~(~a~)-associative" L/R)
|
||||
(if first?
|
||||
(if (null? level*)
|
||||
(printf ":_\n")
|
||||
(printf ", highest precedence:_\n"))
|
||||
(if (null? level*)
|
||||
(printf ", lowest precedence:_\n")
|
||||
(printf ":_\n")))
|
||||
(for-each
|
||||
(lambda (op) (printf " : ~s ~a ~s\n" id (constant->markdown op) id))
|
||||
op*))
|
||||
(loop level* #f))))
|
||||
(printf " : _leaves:_\n")
|
||||
(printf " : ")
|
||||
(render-elt (binop-clause-term clause))
|
||||
(printf "\n"))
|
||||
(for-each render-production (or (regular-clause-prod* clause) '())))
|
||||
(for-each (render-paragraph #f) (clause-after-paragraph* clause)))))
|
||||
(define (render-section section)
|
||||
(unless (section-suppressed? section)
|
||||
(printf "\n## ~a\n" (or (section-title section) "The section"))
|
||||
(for-each (render-paragraph #f) (section-paragraph* section))
|
||||
(for-each render-clause (section-clause* section))))
|
||||
(with-output-to-file mdfn
|
||||
(lambda ()
|
||||
(printf "# ~a\n" (or (grammar-title grammar) "The grammar"))
|
||||
(for-each (render-paragraph #f) (grammar-paragraph* grammar))
|
||||
(for-each render-section (grammar-section* grammar)))
|
||||
'replace))
|
||||
(module (parse-grammar)
|
||||
(define parse-elt
|
||||
(lambda (elt)
|
||||
(syntax-case elt (SEP+ SEP* OPT K* K+)
|
||||
[(SEP+ p sep) (make-sep-elt #t (parse-elt #'p) #'sep)]
|
||||
[(SEP* p sep) (make-sep-elt #f (parse-elt #'p) #'sep)]
|
||||
[(OPT p default) (make-opt-elt (parse-elt #'p) #'default)]
|
||||
[(K+ p) (make-kleene-elt #t (parse-elt #'p))]
|
||||
[(K* p) (make-kleene-elt #f (parse-elt #'p))]
|
||||
[k (constant? #'k) (make-constant-elt #'k)]
|
||||
[id (identifier? #'id) (make-id-elt #'id)]
|
||||
[_ (syntax-error elt "invalid production element")])))
|
||||
(define parse-production
|
||||
(lambda (prod)
|
||||
(define (finish name src? paragraph* elt* receiver)
|
||||
(let ([elt* (map parse-elt elt*)])
|
||||
(make-production name paragraph* elt*
|
||||
(with-syntax ([(t ...) (elt-temps elt*)])
|
||||
#`(lambda (bfp efp t ...)
|
||||
#,(if src?
|
||||
#`(#,receiver (make-src bfp efp) t ...)
|
||||
#`(#,receiver t ...)))))))
|
||||
(syntax-case prod (:: src =>)
|
||||
[[name :: src elt ... => receiver]
|
||||
(finish #'name #t '() #'(elt ...) #'receiver)]
|
||||
[[name :: elt ... => receiver]
|
||||
(finish #'name #f '() #'(elt ...) #'receiver)])))
|
||||
(define (parse-terminal term)
|
||||
(syntax-case term (DESCRIPTION)
|
||||
[(parser (alias ...) (DESCRIPTION paragraph ...))
|
||||
(and (identifier? #'parser) (andmap identifier? #'(alias ...)) (andmap paragraph? #'(paragraph ...)))
|
||||
(make-terminal #'parser #'(alias ...) #'(paragraph ...))]
|
||||
[(parser (alias ...))
|
||||
(and (identifier? #'parser) (andmap identifier? #'(alias ...)))
|
||||
(make-terminal #'parser #'(alias ...) '())]))
|
||||
(define (parse-clause clause nt alias* before-paragraph* after-paragraph* stuff*)
|
||||
(syntax-case stuff* (BINOP :: src =>)
|
||||
[((BINOP src (level ...) term) => receiver)
|
||||
(make-binop-clause nt alias* before-paragraph* after-paragraph* #'(level ...) (parse-elt #'term) #t #'receiver)]
|
||||
[((BINOP (level ...) term) => receiver)
|
||||
(make-binop-clause nt alias* before-paragraph* after-paragraph* #'(level ...) (parse-elt #'term) #f #'receiver)]
|
||||
[(prod prods ...)
|
||||
(make-regular-clause nt alias* before-paragraph* after-paragraph* (map parse-production #'(prod prods ...)))]
|
||||
[else (syntax-error clause)]))
|
||||
(define (parse-top top* knull kgrammar ksection kclause)
|
||||
(if (null? top*)
|
||||
(knull)
|
||||
(let ([top (car top*)] [top* (cdr top*)])
|
||||
(syntax-case top (GRAMMAR SECTION SUPPRESSED DESCRIPTION BINOP TERMINALS src =>)
|
||||
[(GRAMMAR title paragraph ...)
|
||||
(andmap paragraph? #'(paragraph ...))
|
||||
(kgrammar top* (datum title) #'(paragraph ...))]
|
||||
[(SECTION SUPPRESSED title paragraph ...)
|
||||
(andmap paragraph? #'(paragraph ...))
|
||||
(ksection top* (datum title) #'(paragraph ...) #t)]
|
||||
[(SECTION title paragraph ...)
|
||||
(andmap paragraph? #'(paragraph ...))
|
||||
(ksection top* (datum title) #'(paragraph ...) #f)]
|
||||
[(TERMINALS term ...)
|
||||
(kclause top* (make-terminal-clause (map parse-terminal #'(term ...))))]
|
||||
[(TERMINALS term ...)
|
||||
(kclause top* (make-terminal-clause (map parse-terminal #'(term ...))))]
|
||||
[(nt (alias ...) (DESCRIPTION paragraph1 ...) stuff ... (DESCRIPTION paragraph2 ...))
|
||||
(and (identifier? #'nt) (andmap identifier? #'(alias ...)) (andmap paragraph? #'(paragraph1 ...)) (andmap paragraph? #'(paragraph2 ...)))
|
||||
(kclause top* (parse-clause top #'nt #'(alias ...) #'(paragraph1 ...) #'(paragraph2 ...) #'(stuff ...)))]
|
||||
[(nt (alias ...) (DESCRIPTION paragraph ...) stuff ...)
|
||||
(and (identifier? #'nt) (andmap identifier? #'(alias ...)) (andmap paragraph? #'(paragraph ...)))
|
||||
(kclause top* (parse-clause top #'nt #'(alias ...) #'(paragraph ...) '() #'(stuff ...)))]
|
||||
[(nt (alias ...) stuff ... (DESCRIPTION paragraph ...))
|
||||
(and (identifier? #'nt) (andmap identifier? #'(alias ...)) (andmap paragraph? #'(paragraph ...)))
|
||||
(kclause top* (parse-clause top #'nt #'(alias ...) '() #'(paragraph ...) #'(stuff ...)))]
|
||||
[(nt (alias ...) stuff ...)
|
||||
(and (identifier? #'nt) (andmap identifier? #'(alias ...)))
|
||||
(kclause top* (parse-clause top #'nt #'(alias ...) '() '() #'(stuff ...)))]))))
|
||||
(define (parse-grammar top*)
|
||||
(define (misplaced-grammar-error top)
|
||||
(syntax-error top "unexpected GRAMMAR element after other elements"))
|
||||
(define (s1 top*) ; looking for GRAMMAR form, first SECTION form, or clause
|
||||
(parse-top top*
|
||||
(lambda () (make-grammar #f '() '()))
|
||||
(lambda (top* title paragraph*)
|
||||
(make-grammar title paragraph* (s2 top*)))
|
||||
(lambda (top* title paragraph* suppressed?)
|
||||
(make-grammar #f '()
|
||||
(s3 top* title paragraph* suppressed? '() '())))
|
||||
(lambda (top* clause)
|
||||
(make-grammar #f '()
|
||||
(s3 top* #f '() #f (list clause) '())))))
|
||||
(define (s2 top*) ; looking for first SECTION form or clause
|
||||
(parse-top top*
|
||||
(lambda () '())
|
||||
(lambda (title paragraph*) (misplaced-grammar-error (car top*)))
|
||||
(lambda (top* title paragraph* suppressed?)
|
||||
(s3 top* title paragraph* suppressed? '() '()))
|
||||
(lambda (top* clause)
|
||||
(s3 top* #f '() #f (list clause) '()))))
|
||||
(define (s3 top* title paragraph* suppressed? rclause* rsection*) ; steady state: looking for remaining SECTION forms and clauses
|
||||
(define (finish-section)
|
||||
(cons (make-section title paragraph* suppressed? (reverse rclause*)) rsection*))
|
||||
(parse-top top*
|
||||
(lambda () (reverse (finish-section)))
|
||||
(lambda (title paragraph*) (misplaced-grammar-error (car top*)))
|
||||
(lambda (top* title paragraph* suppressed?)
|
||||
(s3 top* title paragraph* suppressed? '() (finish-section)))
|
||||
(lambda (top* clause)
|
||||
(s3 top* title paragraph* suppressed? (cons clause rclause*) rsection*))))
|
||||
(s1 top*)))
|
||||
(define (go init-nts top* mddir)
|
||||
(let ([grammar (parse-grammar top*)])
|
||||
(let* ([clause* (apply append (map section-clause* (grammar-section* grammar)))]
|
||||
[terminal-clause* (filter terminal-clause? clause*)]
|
||||
[binop-clause* (filter binop-clause? clause*)]
|
||||
[regular-clause* (left-factor (filter regular-clause? clause*))]
|
||||
[env (make-env terminal-clause* (append binop-clause* regular-clause*))])
|
||||
(define (elt-helper x)
|
||||
(cond
|
||||
[(sep-elt? x) #`(#,(if (sep-elt-+? x) #'sepby1 #'sepby) #,(elt-helper (sep-elt-elt x)) (sep->parser #,(sep-elt-sep x)))]
|
||||
[(opt-elt? x) #`(optional #,(elt-helper (opt-elt-elt x)) #,(opt-elt-default x))]
|
||||
[(kleene-elt? x) #`(#,(if (kleene-elt-+? x) #'many+ #'many) #,(elt-helper (kleene-elt-elt x)))]
|
||||
[(constant-elt? x) #`(constant->parser '#,(constant-elt-k x))]
|
||||
[(id-elt? x) (lookup (id-elt-id x) env)]
|
||||
[else (errorf 'elt-helper "unhandled elt ~s\n" x)]))
|
||||
(define (binop-helper clause)
|
||||
#`[#,(clause-id clause)
|
||||
(infix-expression-parser
|
||||
#,(map (lambda (level)
|
||||
(syntax-case level ()
|
||||
[(L/R op1 ... op2)
|
||||
(or (free-identifier=? #'L/R #'LEFT) (free-identifier=? #'L/R #'RIGHT))
|
||||
#`(L/R #,(fold-right (lambda (op next) #`(++ (binop->parser '#,op) #,next)) #'(binop->parser 'op2) #'(op1 ...)))]))
|
||||
(binop-clause-level* clause))
|
||||
#,(elt-helper (binop-clause-term clause))
|
||||
#,(binop-clause-receiver clause))])
|
||||
(define (nt-helper clause)
|
||||
#`[#,(clause-id clause)
|
||||
#,(let f ([prod* (regular-clause-prod* clause)])
|
||||
(if (null? prod*)
|
||||
#'zero
|
||||
(let ([elt* (production-elt* (car prod*))])
|
||||
(with-syntax ([name (production-name (car prod*))]
|
||||
[(elt ...) elt*]
|
||||
[receiver (production-receiver (car prod*))])
|
||||
(with-syntax ([(x ...) (generate-temporaries elt*)])
|
||||
(with-syntax ([([y _] ...) (filter (lambda (pr) (not (constant-elt? (cadr pr)))) #'([x elt] ...))])
|
||||
(with-syntax ([(where-nt ...) (map elt-helper elt*)])
|
||||
#`(+++ ;; use +++ if you don't ever need to backtrack to a previous production for the same non-terminal
|
||||
(lambda (inp)
|
||||
(when (and 'name (grammar-trace)) (printf ">>~s(~a)~%" 'name (format-inp inp)))
|
||||
(let ([res ((trace-is name (lambda (bfp efp) (receiver bfp efp y ...)) (where [x <- where-nt] ...)) inp)])
|
||||
(when (and 'name (grammar-trace))
|
||||
(if (stream-null? res)
|
||||
(printf "<<~s(~a) failed~%" 'name (format-inp inp))
|
||||
(printf "<<~s(~a) succeeded~%" 'name (format-inp inp))))
|
||||
res))
|
||||
#,(f (cdr prod*))))))))))])
|
||||
(with-syntax ([(init-nt ...)
|
||||
(syntax-case init-nts ()
|
||||
[(id1 id2 ...) (andmap identifier? #'(id1 id2 ...)) #'(id1 id2 ...)]
|
||||
[id (identifier? #'id) (list #'id)])])
|
||||
(when mddir
|
||||
(for-each
|
||||
(lambda (init-nt)
|
||||
(let ([mdfn (format "~a/~a.md" mddir (syntax->datum init-nt))])
|
||||
(render-markdown init-nt grammar mdfn env)))
|
||||
#'(init-nt ...)))
|
||||
(with-syntax ([((lhs rhs) ...)
|
||||
(append
|
||||
(map binop-helper binop-clause*)
|
||||
(map nt-helper regular-clause*))])
|
||||
#'(module (init-nt ...)
|
||||
(module M (init-nt ...) (define lhs rhs) ...)
|
||||
(define init-nt
|
||||
(let ()
|
||||
(import M)
|
||||
(make-top-level-parser init-nt)))
|
||||
...))))))
|
||||
(syntax-case x (markdown-directory)
|
||||
[(_ init-nts (markdown-directory mddir) top ...)
|
||||
(string? (datum mddir))
|
||||
(go #'init-nts #'(top ...) (datum mddir))]
|
||||
[(_ init-nts top ...) (go #'init-nts #'(top ...) #f)])))
|
||||
|
||||
(indirect-export define-grammar
|
||||
result
|
||||
zero
|
||||
is
|
||||
trace-is
|
||||
sepby1
|
||||
sepby
|
||||
optional
|
||||
many
|
||||
many+
|
||||
+++
|
||||
infix-expression-parser
|
||||
|
||||
grammar-trace
|
||||
format-inp
|
||||
trace-is
|
||||
|
||||
make-top-level-parser
|
||||
)
|
||||
)
|
11
ta6ob/examples/fact.ss
Normal file
11
ta6ob/examples/fact.ss
Normal file
|
@ -0,0 +1,11 @@
|
|||
;;; simple factorial function
|
||||
|
||||
;;; it is interesting to change the 'lambda' into 'trace-lambda'
|
||||
;;; or simply type (trace fact) before running fact to observe
|
||||
;;; the nesting of recursive calls.
|
||||
|
||||
(define fact
|
||||
(lambda (x)
|
||||
(if (zero? x)
|
||||
1
|
||||
(* x (fact (1- x))))))
|
19
ta6ob/examples/fatfib.ss
Normal file
19
ta6ob/examples/fatfib.ss
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; fat fibonacci function
|
||||
|
||||
;;; this is "fat" because it uses only increments and decrements
|
||||
;;; for addition and subtraction (i.e., peano arithmetic).
|
||||
|
||||
;;; note that fat+ is tail-recursive; this is how all looping is
|
||||
;;; performed in Scheme.
|
||||
|
||||
(define fat+
|
||||
(lambda (x y)
|
||||
(if (zero? y)
|
||||
x
|
||||
(fat+ (1+ x) (1- y)))))
|
||||
|
||||
(define fatfib
|
||||
(lambda (x)
|
||||
(if (< x 2)
|
||||
1
|
||||
(fat+ (fatfib (1- x)) (fatfib (1- (1- x)))))))
|
63
ta6ob/examples/fft.ss
Normal file
63
ta6ob/examples/fft.ss
Normal file
|
@ -0,0 +1,63 @@
|
|||
;;; fft.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(define (dft x)
|
||||
(define (w-powers n)
|
||||
(let ((pi (* (acos 0.0) 2)))
|
||||
(let ((delta (/ (* -2.0i pi) n)))
|
||||
(let f ((n n) (x 0.0))
|
||||
(if (= n 0)
|
||||
'()
|
||||
(cons (exp x) (f (- n 2) (+ x delta))))))))
|
||||
(define (evens w)
|
||||
(if (null? w)
|
||||
'()
|
||||
(cons (car w) (evens (cddr w)))))
|
||||
(define (interlace x y)
|
||||
(if (null? x)
|
||||
'()
|
||||
(cons (car x) (cons (car y) (interlace (cdr x) (cdr y))))))
|
||||
(define (split ls)
|
||||
(let split ((fast ls) (slow ls))
|
||||
(if (null? fast)
|
||||
(values '() slow)
|
||||
(call-with-values
|
||||
(lambda () (split (cddr fast) (cdr slow)))
|
||||
(lambda (front back)
|
||||
(values (cons (car slow) front) back))))))
|
||||
(define (butterfly x w)
|
||||
(call-with-values
|
||||
(lambda () (split x))
|
||||
(lambda (front back)
|
||||
(values
|
||||
(map + front back)
|
||||
(map * (map - front back) w)))))
|
||||
(define (rfft x w)
|
||||
(if (null? (cddr x))
|
||||
(let ((x0 (car x)) (x1 (cadr x)))
|
||||
(list (+ x0 x1) (- x0 x1)))
|
||||
(call-with-values
|
||||
(lambda () (butterfly x w))
|
||||
(lambda (front back)
|
||||
(let ((w (evens w)))
|
||||
(interlace (rfft front w) (rfft back w)))))))
|
||||
(rfft x (w-powers (length x))))
|
9
ta6ob/examples/fib.ss
Normal file
9
ta6ob/examples/fib.ss
Normal file
|
@ -0,0 +1,9 @@
|
|||
;;; simple fibonacci function
|
||||
|
||||
;;; uses trace-lambda to show the nesting
|
||||
|
||||
(define fib
|
||||
(trace-lambda fib (x)
|
||||
(if (<= x 1)
|
||||
1
|
||||
(+ (fib (- x 1)) (fib (- x 2))))))
|
179
ta6ob/examples/foreign.ss
Normal file
179
ta6ob/examples/foreign.ss
Normal file
|
@ -0,0 +1,179 @@
|
|||
;;; foreign.ss
|
||||
;;; Copyright (c) 1997 R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; Prototype code for converting ``foreign-callable'' declarations into
|
||||
;;; C interface routines to support C calls to Scheme procedures with
|
||||
;;; automatic datatype conversion analogous to that provided for Scheme
|
||||
;;; calls to C procedures via foreign-procedure.
|
||||
|
||||
;;; Todo
|
||||
;;; - support for foreign-pointer and foreign-object
|
||||
;;; - similar support for foreign-procedure declarations
|
||||
|
||||
(define spec->decl
|
||||
(lambda (spec)
|
||||
(case spec
|
||||
[(integer-32 boolean) "int"]
|
||||
[(unsigned-32) "unsigned int"]
|
||||
[(char) "char"]
|
||||
[(string) "char *"]
|
||||
[(fixnum) "int"]
|
||||
[(double-float) "double"]
|
||||
[(single-float) "float"]
|
||||
[(void) "void"]
|
||||
[(scheme-object) "ptr"]
|
||||
[else
|
||||
(record-case spec
|
||||
[(foreign-pointer foreign-object) ()
|
||||
(error 'spec->decl "unsupported specifier ~s" spec)]
|
||||
[else (error 'spec->decl "unexpected specifier ~s" spec)])])))
|
||||
|
||||
(define C->Scheme
|
||||
(lambda (spec id)
|
||||
(case spec
|
||||
[(boolean) (format "Sboolean(~a)" id)]
|
||||
[(char) (format "Schar(~a)" id)]
|
||||
[(fixnum) (format "Sfixnum(~a)" id)]
|
||||
[(integer-32) (format "Sinteger(~a)" id)]
|
||||
[(unsigned-32) (format "Sunsigned(~a)" id)]
|
||||
[(single-float) (format "Sflonum((double)~a)" id)]
|
||||
[(double-float) (format "Sflonum(~a)" id)]
|
||||
[(scheme-object) id]
|
||||
[(string) (format "Sstring(~a)" id)]
|
||||
[else
|
||||
(record-case spec
|
||||
[(foreign-pointer foreign-object) ()
|
||||
(error 'C->Scheme "unsupported specifier ~s" spec)]
|
||||
[else (error 'C->Scheme "unexpected specifier ~s" spec)])])))
|
||||
|
||||
(define Scheme->C
|
||||
(lambda (op spec src)
|
||||
(case spec
|
||||
[(boolean) (fprintf op "Sboolean_value(~a)" src)]
|
||||
[(char) (fprintf op "Schar_value(~a)" src)]
|
||||
[(fixnum) (fprintf op "Sfixnum_value(~a)" src)]
|
||||
[(integer-32) (fprintf op "Sinteger_value(~a)" src)]
|
||||
[(unsigned-32) (fprintf op "Sunsigned_value(~a)" src)]
|
||||
[(single-float) (fprintf op "(float)Sflonum_value(~a)" src)]
|
||||
[(double-float) (fprintf op "Sflonum_value(~a)" src)]
|
||||
[(scheme-object) (display src op)]
|
||||
[(string) (fprintf op "Sstring_value(~a)" src)]
|
||||
[else
|
||||
(record-case spec
|
||||
[(foreign-pointer foreign-object) ()
|
||||
(error 'Scheme->C "unsupported specifier ~s" spec)]
|
||||
[else (error 'Scheme->C "unexpected specifier ~s" spec)])])))
|
||||
|
||||
(define gen-fcallable
|
||||
(case-lambda
|
||||
[(cname arg-specs res-spec)
|
||||
(gen-fcallable (current-output-port) cname arg-specs res-spec)]
|
||||
[(op cname arg-specs res-spec)
|
||||
(let ((names (let loop ((ls arg-specs) (i 1))
|
||||
(if (null? ls)
|
||||
'()
|
||||
(cons (format "x~d" i) (loop (cdr ls) (+ i 1))))))
|
||||
(count (length arg-specs)))
|
||||
(newline op)
|
||||
(fprintf op "~a ~a(ptr proc" (spec->decl res-spec) cname) ;)
|
||||
(let loop ((arg-specs arg-specs) (names names))
|
||||
(unless (null? arg-specs)
|
||||
(fprintf op ", ~a ~a" (spec->decl (car arg-specs)) (car names))
|
||||
(loop (cdr arg-specs) (cdr names)))) ;(
|
||||
(fprintf op ") {~%")
|
||||
(if (<= 0 count 3)
|
||||
(begin
|
||||
(display " return " op)
|
||||
(Scheme->C op res-spec
|
||||
(let ((op (open-output-string)))
|
||||
(fprintf op "Scall~d(proc" count) ;)
|
||||
(let loop ((arg-specs arg-specs) (names names))
|
||||
(unless (null? arg-specs)
|
||||
(display ", " op)
|
||||
(display (C->Scheme (car arg-specs) (car names)) op)
|
||||
(loop (cdr arg-specs) (cdr names)))) ;(
|
||||
(fprintf op ")")
|
||||
(get-output-string op))))
|
||||
(begin
|
||||
(fprintf op " Sinitframe(~d);~%" count)
|
||||
(let loop ([arg-specs arg-specs] [names names] [num 1])
|
||||
(unless (null? arg-specs)
|
||||
(fprintf op " Sput_arg(~d, ~a);~%"
|
||||
num (C->Scheme (car arg-specs) (car names)))
|
||||
(loop (cdr arg-specs) (cdr names) (+ num 1))))
|
||||
(fprintf op " return ")
|
||||
(Scheme->C op res-spec
|
||||
(format "Scall(proc, ~d)" count))))
|
||||
(fprintf op ";~%}~%"))]))
|
||||
|
||||
(define-syntax foreign-callable
|
||||
(syntax-rules ()
|
||||
((_ n args res)
|
||||
(gen-fcallable n 'args 'res))))
|
||||
|
||||
(define gen-file
|
||||
(lambda (fnroot)
|
||||
(let ((ifn (format "~a.ss" fnroot))
|
||||
(ofn (format "~a.xx" fnroot)))
|
||||
(with-output-to-file ofn
|
||||
(lambda () (load ifn))
|
||||
'replace))))
|
||||
|
||||
#!eof ; cut off the input here so we can give examples w/o comment chars
|
||||
|
||||
Example input file:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
(foreign-callable "foo"
|
||||
(boolean single-float double-float)
|
||||
scheme-object)
|
||||
|
||||
(foreign-callable "bar"
|
||||
(boolean char integer-32 unsigned-32 single-float
|
||||
double-float scheme-object)
|
||||
string)
|
||||
|
||||
(foreign-callable "baz" () fixnum)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Generated output file:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
ptr foo(ptr proc, int x1, float x2, double x3) {
|
||||
return Scall3(proc, Sboolean(x1), Sflonum((double)x2), Sflonum(x3));
|
||||
}
|
||||
|
||||
char * bar(ptr proc, int x1, char x2, int x3, unsigned int x4, float x5, double x6, ptr x7) {
|
||||
Sinitframe(7);
|
||||
Sput_arg(1, Sboolean(x1));
|
||||
Sput_arg(2, Schar(x2));
|
||||
Sput_arg(3, Sinteger(x3));
|
||||
Sput_arg(4, Sunsigned(x4));
|
||||
Sput_arg(5, Sflonum((double)x5));
|
||||
Sput_arg(6, Sflonum(x6));
|
||||
Sput_arg(7, x7);
|
||||
return Sstring_value(Scall(proc, 7));
|
||||
}
|
||||
|
||||
int baz(ptr proc) {
|
||||
return Sfixnum_value(Scall0(proc));
|
||||
}
|
||||
------------------------------------------------------------------------
|
123
ta6ob/examples/freq.ss
Normal file
123
ta6ob/examples/freq.ss
Normal file
|
@ -0,0 +1,123 @@
|
|||
;;; freq.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; If the next character on p is a letter, get-word reads a word
|
||||
;;; from p and returns it in a string. If the character is not a
|
||||
;;; letter, get-word returns the character (on eof, the eof-object).
|
||||
(define get-word
|
||||
(lambda (p)
|
||||
(let ((c (read-char p)))
|
||||
(if (eq? (char-type c) 'letter)
|
||||
(list->string
|
||||
(let loop ((c c))
|
||||
(cons c
|
||||
(if (memq (char-type (peek-char p)) '(letter digit))
|
||||
(loop (read-char p))
|
||||
'()))))
|
||||
c))))
|
||||
|
||||
;;; char-type tests for the eof-object first, since the eof-object
|
||||
;;; may not be a valid argument to char-alphabetic? or char-numeric?
|
||||
;;; It returns the eof-object, the symbol letter, the symbol digit,
|
||||
;;; or the argument itself if it is not a letter or digit.
|
||||
(define char-type
|
||||
(lambda (c)
|
||||
(cond
|
||||
((eof-object? c) c)
|
||||
((char-alphabetic? c) 'letter)
|
||||
((char-numeric? c) 'digit)
|
||||
(else c))))
|
||||
|
||||
;;; Trees are represented as vectors with four fields: word, left,
|
||||
;;; right, and count. Only one field, word, is initialized by an
|
||||
;;; argument to the constructor procedure make-tree. The remaining
|
||||
;;; fields are explicitly initialized and changed by subsequent
|
||||
;;; operations. Most Scheme systems provide structure definition
|
||||
;;; facilities that automate creation of structure manipulation
|
||||
;;; procedures, but we simply define the procedures by hand here.
|
||||
(define make-tree
|
||||
(lambda (word)
|
||||
(vector word '() '() 1)))
|
||||
|
||||
(define tree-word (lambda (tree) (vector-ref tree 0)))
|
||||
|
||||
(define tree-left (lambda (tree) (vector-ref tree 1)))
|
||||
(define set-tree-left!
|
||||
(lambda (tree new-left)
|
||||
(vector-set! tree 1 new-left)))
|
||||
|
||||
(define tree-right (lambda (tree) (vector-ref tree 2)))
|
||||
(define set-tree-right!
|
||||
(lambda (tree new-right)
|
||||
(vector-set! tree 2 new-right)))
|
||||
|
||||
(define tree-count (lambda (tree) (vector-ref tree 3)))
|
||||
(define set-tree-count!
|
||||
(lambda (tree new-count)
|
||||
(vector-set! tree 3 new-count)))
|
||||
|
||||
;;; If the word already exists in the tree, tree increments its
|
||||
;;; count. Otherwise, a new tree node is created and put into the
|
||||
;;; tree. In any case, the new or modified tree is returned.
|
||||
(define tree
|
||||
(lambda (node word)
|
||||
(cond
|
||||
((null? node) (make-tree word))
|
||||
((string=? word (tree-word node))
|
||||
(set-tree-count! node (+ (tree-count node) 1))
|
||||
node)
|
||||
((string<? word (tree-word node))
|
||||
(set-tree-left! node (tree (tree-left node) word))
|
||||
node)
|
||||
(else
|
||||
(set-tree-right! node (tree (tree-right node) word))
|
||||
node))))
|
||||
|
||||
;;; tree-print prints the tree in "in-order," i.e., left subtree,
|
||||
;;; then node, then right subtree. For each word, the count and the
|
||||
;;; word are printed on a single line.
|
||||
(define tree-print
|
||||
(lambda (node p)
|
||||
(if (not (null? node))
|
||||
(begin
|
||||
(tree-print (tree-left node) p)
|
||||
(write (tree-count node) p)
|
||||
(write-char #\space p)
|
||||
(display (tree-word node) p)
|
||||
(newline p)
|
||||
(tree-print (tree-right node) p)))))
|
||||
|
||||
;;; frequency is the driver routine. It opens the files, reads the
|
||||
;;; words, and enters them into the tree. When the input port
|
||||
;;; reaches end-of-file, it prints the tree and closes the ports.
|
||||
(define frequency
|
||||
(lambda (infn outfn)
|
||||
(let ((ip (open-input-file infn))
|
||||
(op (open-output-file outfn)))
|
||||
(let loop ((root '()))
|
||||
(let ((w (get-word ip)))
|
||||
(cond
|
||||
((eof-object? w) (tree-print root op))
|
||||
((string? w) (loop (tree root w)))
|
||||
(else (loop root)))))
|
||||
(close-input-port ip)
|
||||
(close-output-port op))))
|
111
ta6ob/examples/interpret.ss
Normal file
111
ta6ob/examples/interpret.ss
Normal file
|
@ -0,0 +1,111 @@
|
|||
;;; interpret.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(define interpret #f)
|
||||
(let ()
|
||||
;; primitive-environment is an environment containing a small
|
||||
;; number of primitive procedures; it can be extended easily
|
||||
;; to include additional primitives.
|
||||
(define primitive-environment
|
||||
(list (cons 'apply apply)
|
||||
(cons 'assq assq)
|
||||
(cons 'call/cc call/cc)
|
||||
(cons 'car car)
|
||||
(cons 'cadr cadr)
|
||||
(cons 'caddr caddr)
|
||||
(cons 'cadddr cadddr)
|
||||
(cons 'cddr cddr)
|
||||
(cons 'cdr cdr)
|
||||
(cons 'cons cons)
|
||||
(cons 'eq? eq?)
|
||||
(cons 'list list)
|
||||
(cons 'map map)
|
||||
(cons 'memv memv)
|
||||
(cons 'null? null?)
|
||||
(cons 'pair? pair?)
|
||||
(cons 'read read)
|
||||
(cons 'set-car! set-car!)
|
||||
(cons 'set-cdr! set-cdr!)
|
||||
(cons 'symbol? symbol?)))
|
||||
|
||||
;; new-env returns a new environment from a formal parameter
|
||||
;; specification, a list of actual parameters, and an outer
|
||||
;; environment. The symbol? test identifies "improper"
|
||||
;; argument lists. Environments are association lists,
|
||||
;; associating variables with values.
|
||||
(define new-env
|
||||
(lambda (formals actuals env)
|
||||
(cond
|
||||
((null? formals) env)
|
||||
((symbol? formals) (cons (cons formals actuals) env))
|
||||
(else
|
||||
(cons (cons (car formals) (car actuals))
|
||||
(new-env (cdr formals) (cdr actuals) env))))))
|
||||
|
||||
;; lookup finds the value of the variable var in the environment
|
||||
;; env, using assq. Assumes var is bound in env.
|
||||
(define lookup
|
||||
(lambda (var env)
|
||||
(cdr (assq var env))))
|
||||
|
||||
;; assign is similar to lookup but alters the binding of the
|
||||
;; variable var in the environment env by changing the cdr of
|
||||
;; association pair
|
||||
(define assign
|
||||
(lambda (var val env)
|
||||
(set-cdr! (assq var env) val)))
|
||||
|
||||
;; exec evaluates the expression, recognizing all core forms.
|
||||
(define exec
|
||||
(lambda (exp env)
|
||||
(cond
|
||||
((symbol? exp) (lookup exp env))
|
||||
((pair? exp)
|
||||
(case (car exp)
|
||||
((quote) (cadr exp))
|
||||
((lambda)
|
||||
(lambda vals
|
||||
(let ((env (new-env (cadr exp) vals env)))
|
||||
(let loop ((exps (cddr exp)))
|
||||
(if (null? (cdr exps))
|
||||
(exec (car exps) env)
|
||||
(begin
|
||||
(exec (car exps) env)
|
||||
(loop (cdr exps))))))))
|
||||
((if)
|
||||
(if (exec (cadr exp) env)
|
||||
(exec (caddr exp) env)
|
||||
(exec (cadddr exp) env)))
|
||||
((set!)
|
||||
(assign (cadr exp)
|
||||
(exec (caddr exp) env)
|
||||
env))
|
||||
(else
|
||||
(apply (exec (car exp) env)
|
||||
(map (lambda (x) (exec x env))
|
||||
(cdr exp))))))
|
||||
(else exp))))
|
||||
|
||||
;; interpret starts execution with the primitive environment.
|
||||
(set! interpret
|
||||
(lambda (exp)
|
||||
(exec exp primitive-environment))))
|
641
ta6ob/examples/m4.ss
Normal file
641
ta6ob/examples/m4.ss
Normal file
|
@ -0,0 +1,641 @@
|
|||
;;; m4.ss
|
||||
;;; Copyright (C) 1988 R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; speed improvement ideas:
|
||||
;;; use hash table rather than assoc for macro lookup
|
||||
;;; use extensible string buffer in place of lists
|
||||
;;; collect multiple characters when scanning text, arguments
|
||||
;;; use fixnum arithmetic where appropriate
|
||||
|
||||
(eval-when (compile) (optimize-level 3))
|
||||
|
||||
(define lexeme-type car)
|
||||
(define lexeme-value cdr)
|
||||
|
||||
(define-structure (ibuf ip) ([pb '()]))
|
||||
|
||||
(define oparen #\()
|
||||
(define cparen #\))
|
||||
|
||||
(define m4-get-char
|
||||
(lambda (ib)
|
||||
(let ([pb (ibuf-pb ib)])
|
||||
(if (null? pb)
|
||||
(read-char (ibuf-ip ib))
|
||||
(begin (set-ibuf-pb! ib (cdr pb))
|
||||
(car pb))))))
|
||||
|
||||
(define m4-unget-char
|
||||
(lambda (c ib)
|
||||
(set-ibuf-pb! ib (cons c (ibuf-pb ib)))))
|
||||
|
||||
(define unget-string
|
||||
(lambda (s ib)
|
||||
(set-ibuf-pb! ib (append (string->list s) (ibuf-pb ib)))))
|
||||
|
||||
(define int->str
|
||||
(lambda (num)
|
||||
(format "~s" num)))
|
||||
|
||||
(define char->digit
|
||||
(let ([zero (char->integer #\0)])
|
||||
(lambda (c)
|
||||
(- (char->integer c) zero))))
|
||||
|
||||
(define str->int
|
||||
(let ([ustr->int
|
||||
(lambda (s i n)
|
||||
(let f ([a 0] [i i])
|
||||
(if (= i n)
|
||||
a
|
||||
(f (+ (* a 10) (char->digit (string-ref s i)))
|
||||
(+ i 1)))))])
|
||||
(lambda (s)
|
||||
(let ([n (string-length s)])
|
||||
(if (= n 0)
|
||||
0
|
||||
(if (char=? (string-ref s 0) #\-)
|
||||
(- (ustr->int s 1 n))
|
||||
(ustr->int s 0 n)))))))
|
||||
|
||||
(define eval-string
|
||||
(let ([str #f] [port #f] [token #f] [value #f])
|
||||
(define eval-error
|
||||
(lambda ()
|
||||
(error 'm4 "invalid arithmetic expression ~s" str)))
|
||||
(define next-token!
|
||||
(lambda ()
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (set! token 'eof)]
|
||||
[(char-whitespace? c) (next-token!)]
|
||||
[(char-numeric? c)
|
||||
(let loop ([a (char->digit c)])
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c)
|
||||
(set! token 'integer)
|
||||
(set! value a)]
|
||||
[(char-numeric? c)
|
||||
(loop (+ (* a 10) (char->digit c)))]
|
||||
[else
|
||||
(unread-char c port)
|
||||
(set! token 'integer)
|
||||
(set! value a)])))]
|
||||
[(char=? c oparen) (set! token 'oparen)]
|
||||
[(char=? c cparen) (set! token 'cparen)]
|
||||
[(char=? c #\-) (set! token '-)]
|
||||
[(char=? c #\*)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (set! token '*)]
|
||||
[(char=? c #\*) (set! token '**)]
|
||||
[else (unread-char c port) (set! token '*)]))]
|
||||
[(char=? c #\+) (set! token '+)]
|
||||
[(char=? c #\-) (set! token '+)]
|
||||
[(char=? c #\/) (set! token '/)]
|
||||
[(char=? c #\%) (set! token '%)]
|
||||
[(char=? c #\!)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (set! token '!)]
|
||||
[(char=? c #\=) (set! token '!=)]
|
||||
[else (unread-char c port) (set! token '!)]))]
|
||||
[(char=? c #\|)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (eval-error)]
|
||||
[(char=? c #\|) (set! token 'or)]
|
||||
[else (unread-char c port) (eval-error)]))]
|
||||
[(char=? c #\&)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (eval-error)]
|
||||
[(char=? c #\&) (set! token 'and)]
|
||||
[else (unread-char c port) (eval-error)]))]
|
||||
[(char=? c #\=)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (eval-error)]
|
||||
[(char=? c #\=) (set! token '==)]
|
||||
[else (unread-char c port) (eval-error)]))]
|
||||
[(char=? c #\<)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (set! token '<)]
|
||||
[(char=? c #\=) (set! token '<=)]
|
||||
[else (unread-char c port) (set! token '<)]))]
|
||||
[(char=? c #\>)
|
||||
(let ([c (read-char port)])
|
||||
(cond
|
||||
[(eof-object? c) (set! token '>)]
|
||||
[(char=? c #\=) (set! token '>=)]
|
||||
[else (unread-char c port) (set! token '>)]))]))))
|
||||
(define E0 ; or
|
||||
(lambda ()
|
||||
(E0* (E1))))
|
||||
(define E0*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[or (next-token!) (E0* (if (= (+ v (E1)) 0) 0 1))]
|
||||
[else v])))
|
||||
(define E1 ; and
|
||||
(lambda ()
|
||||
(E1* (E2))))
|
||||
(define E1*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[and (next-token!) (E1* (if (= (* v (E2)) 0) 0 1))]
|
||||
[else v])))
|
||||
(define E2 ; ==, !=
|
||||
(lambda ()
|
||||
(E2* (E3))))
|
||||
(define E2*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[== (next-token!) (E2* (if (= v (E3)) 1 0))]
|
||||
[!= (next-token!) (E2* (if (= v (E3)) 0 1))]
|
||||
[else v])))
|
||||
(define E3 ; <, <=, >, >=
|
||||
(lambda ()
|
||||
(E3* (E4))))
|
||||
(define E3*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[< (next-token!) (E3* (if (< v (E4)) 1 0))]
|
||||
[<= (next-token!) (E3* (if (<= v (E4)) 1 0))]
|
||||
[> (next-token!) (E3* (if (> v (E4)) 1 0))]
|
||||
[>= (next-token!) (E3* (if (>= v (E4)) 1 0))]
|
||||
[else v])))
|
||||
(define E4 ; +, -
|
||||
(lambda ()
|
||||
(E4* (E5))))
|
||||
(define E4*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[+ (next-token!) (E4* (+ v (E5)))]
|
||||
[- (next-token!) (E4* (- v (E5)))]
|
||||
[else v])))
|
||||
(define E5 ; *, /, %
|
||||
(lambda ()
|
||||
(E5* (E6))))
|
||||
(define E5*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[* (next-token!) (E5* (* v (E6)))]
|
||||
[/ (next-token!) (E5* (quotient v (E6)))]
|
||||
[% (next-token!) (E5* (modulo v (E6)))]
|
||||
[else v])))
|
||||
(define E6 ; **
|
||||
(lambda ()
|
||||
(E6* (E7))))
|
||||
(define E6*
|
||||
(lambda (v)
|
||||
(case token
|
||||
[** (next-token!) (E6* (expt v (E7)))]
|
||||
[else v])))
|
||||
(define E7 ; -, integer, paren
|
||||
(lambda ()
|
||||
(case token
|
||||
[- (next-token!) (- (E7))]
|
||||
[! (next-token!) (if (= (E7) 0) 1 0)]
|
||||
[oparen
|
||||
(next-token!)
|
||||
(let ([v (E0)])
|
||||
(unless (eq? token 'cparen) (eval-error))
|
||||
(next-token!)
|
||||
v)]
|
||||
[integer (next-token!) value]
|
||||
[else (eval-error)])))
|
||||
(lambda (s)
|
||||
(fluid-let ([str s] [port (open-input-string s)] [token #f] [value #f])
|
||||
(next-token!)
|
||||
(let ([v (E0)])
|
||||
(unless (eq? token 'eof) (eval-error))
|
||||
v)))))
|
||||
|
||||
(define *divnum* #f)
|
||||
(define *diversions* #f)
|
||||
|
||||
(define m4-put-string
|
||||
(lambda (s)
|
||||
(unless (= *divnum* -1)
|
||||
(display s (vector-ref *diversions* *divnum*)))))
|
||||
|
||||
(define *open-quote* #f)
|
||||
(define *close-quote* #f)
|
||||
|
||||
(define *macros* #f)
|
||||
(define builtin-macros '())
|
||||
|
||||
(define *translit-table* #f)
|
||||
|
||||
(define define-builtin-macro
|
||||
(lambda (name proc)
|
||||
(set! builtin-macros (cons (cons name proc) builtin-macros))))
|
||||
|
||||
(define m4
|
||||
(lambda (ofn ifn . rest)
|
||||
(let ([op (open-output-file ofn 'replace)])
|
||||
(fluid-let ([*macros* builtin-macros]
|
||||
[*open-quote* #\`]
|
||||
[*close-quote* #\']
|
||||
[*translit-table* #f]
|
||||
[*divnum* 0]
|
||||
[*diversions* (vector op #f #f #f #f #f #f #f #f #f)])
|
||||
(let loop ([ip (open-input-file ifn)] [rest rest])
|
||||
(m4-process (make-ibuf ip) op)
|
||||
(close-input-port ip)
|
||||
(unless (null? rest)
|
||||
(loop (open-input-file (car rest)) (cdr rest))))
|
||||
(for-each undivert '(1 2 3 4 5 6 7 8 9)))
|
||||
(close-output-port op))))
|
||||
|
||||
(define m4-process
|
||||
(lambda (ib op)
|
||||
(let ([lexeme (read-lexeme ib)])
|
||||
(case (lexeme-type lexeme)
|
||||
[(comment literal)
|
||||
(m4-put-string (lexeme-value lexeme))
|
||||
(m4-process ib op)]
|
||||
[macro
|
||||
((cdr (lexeme-value lexeme)) (read-args ib) ib)
|
||||
(m4-process ib op)]
|
||||
[eof #t]
|
||||
[else (error 'm4-internal "unexpected lexeme ~s" lexeme)]))))
|
||||
|
||||
(define name-start-char?
|
||||
(lambda (c)
|
||||
(or (char-alphabetic? c)
|
||||
(char=? c #\_))))
|
||||
|
||||
(define name-char?
|
||||
(lambda (c)
|
||||
(or (name-start-char? c)
|
||||
(char-numeric? c))))
|
||||
|
||||
(define read-lexeme
|
||||
(lambda (ib)
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) (cons 'eof c)]
|
||||
[(char=? c #\#) (cons 'comment (read-comment ib))]
|
||||
[(char=? c *open-quote*) (cons 'literal (read-quoted ib))]
|
||||
[(name-start-char? c) (lookup-macro (cons c (read-alpha ib)))]
|
||||
[else (cons 'literal (string c))]))))
|
||||
|
||||
(define read-comment
|
||||
(lambda (ib)
|
||||
(let loop ([ls '(#\#)])
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) (list->string (reverse ls))]
|
||||
[(char=? c #\newline) (list->string (reverse (cons c ls)))]
|
||||
[else (loop (cons c ls))])))))
|
||||
|
||||
(define read-quoted
|
||||
(lambda (ib)
|
||||
(let loop ([ls '()] [n 0])
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c)
|
||||
(error 'm4 "end-of-file detected at quote level ~s" n)]
|
||||
[(char=? c *close-quote*)
|
||||
(if (= n 0)
|
||||
(list->string (reverse ls))
|
||||
(loop (cons c ls) (- n 1)))]
|
||||
[(char=? c *open-quote*) (loop (cons c ls) (+ n 1))]
|
||||
[else (loop (cons c ls) n)])))))
|
||||
|
||||
(define read-alpha
|
||||
(lambda (ib)
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) '()]
|
||||
[(name-char? c) (cons c (read-alpha ib))]
|
||||
[else (m4-unget-char c ib) '()]))))
|
||||
|
||||
(define lookup-macro
|
||||
(lambda (ls)
|
||||
(let ([s (list->string ls)])
|
||||
(let ([a (assoc s *macros*)])
|
||||
(if a
|
||||
(cons 'macro a)
|
||||
(cons 'literal s))))))
|
||||
|
||||
(define read-args
|
||||
(lambda (ib)
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) '()]
|
||||
[(char=? c oparen)
|
||||
(let next-arg ()
|
||||
(let skip-white ()
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) '()]
|
||||
[(char-whitespace? c) (skip-white)]
|
||||
[else (m4-unget-char c ib)])))
|
||||
(let this-arg ([strings '()])
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(or (eof-object? c) (char=? c cparen))
|
||||
(if (null? strings)
|
||||
'()
|
||||
(cons (apply string-append (reverse strings))
|
||||
'()))]
|
||||
[(char=? c oparen)
|
||||
(let nest ([strings (cons (string oparen)
|
||||
strings)]
|
||||
[k this-arg])
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) (this-arg strings)]
|
||||
[(char=? c cparen)
|
||||
(k (cons (string cparen) strings))]
|
||||
[(char=? c oparen)
|
||||
(nest (cons (string oparen) strings)
|
||||
(lambda (strings)
|
||||
(nest strings k)))]
|
||||
[else
|
||||
(m4-unget-char c ib)
|
||||
(let ([lexeme (read-lexeme ib)])
|
||||
(case (lexeme-type lexeme)
|
||||
[comment (nest strings k)]
|
||||
[literal
|
||||
(nest (cons (lexeme-value lexeme)
|
||||
strings)
|
||||
k)]
|
||||
[macro
|
||||
((cdr (lexeme-value lexeme))
|
||||
(read-args ib)
|
||||
ib)
|
||||
(nest strings k)]
|
||||
[else
|
||||
(error 'm4-internal
|
||||
"unexpected lexeme ~s"
|
||||
lexeme)]))])))]
|
||||
[(char=? c #\,)
|
||||
(cons (apply string-append (reverse strings))
|
||||
(next-arg))]
|
||||
|
||||
[else
|
||||
(m4-unget-char c ib)
|
||||
(let ([lexeme (read-lexeme ib)])
|
||||
(case (lexeme-type lexeme)
|
||||
[comment (this-arg strings)]
|
||||
[literal
|
||||
(this-arg
|
||||
(cons (lexeme-value lexeme) strings))]
|
||||
[macro
|
||||
((cdr (lexeme-value lexeme)) (read-args ib) ib)
|
||||
(this-arg strings)]
|
||||
[else
|
||||
(error 'm4-internal
|
||||
"unexpected lexeme ~s"
|
||||
lexeme)]))]))))]
|
||||
[else (m4-unget-char c ib) '()]))))
|
||||
|
||||
;;; builtin macros
|
||||
|
||||
(define $$ (lambda (ls) (if (null? ls) ls (cdr ls))))
|
||||
(define $1 (lambda (ls) (if (null? ls) "" (car ls))))
|
||||
(define $2 (lambda (ls) ($1 ($$ ls))))
|
||||
(define $3 (lambda (ls) ($2 ($$ ls))))
|
||||
(define $4 (lambda (ls) ($3 ($$ ls))))
|
||||
(define $5 (lambda (ls) ($4 ($$ ls))))
|
||||
(define $6 (lambda (ls) ($5 ($$ ls))))
|
||||
(define $7 (lambda (ls) ($6 ($$ ls))))
|
||||
(define $8 (lambda (ls) ($7 ($$ ls))))
|
||||
(define $9 (lambda (ls) ($8 ($$ ls))))
|
||||
|
||||
(define-builtin-macro "changequote"
|
||||
(lambda (args ib)
|
||||
(set! *open-quote*
|
||||
(if (string=? ($1 args) "") #\` (string-ref ($1 args) 0)))
|
||||
(set! *close-quote*
|
||||
(if (string=? ($2 args) "") #\' (string-ref ($2 args) 0)))))
|
||||
|
||||
(define-builtin-macro "define"
|
||||
(lambda (args ib)
|
||||
(let ([name ($1 args)])
|
||||
(unless (let ([n (string-length name)])
|
||||
(and (fx> n 0)
|
||||
(name-start-char? (string-ref name 0))
|
||||
(let ok? ([i 1])
|
||||
(or (fx= i n)
|
||||
(and (name-char? (string-ref name i))
|
||||
(ok? (fx+ i 1)))))))
|
||||
(error 'm4-define "invalid macro name ~s" name))
|
||||
(let ([proc (make-macro ($2 args))])
|
||||
(let ([a (assoc name *macros*)])
|
||||
(if a
|
||||
(set-cdr! a proc)
|
||||
(set! *macros* (cons (cons name proc) *macros*))))))))
|
||||
|
||||
(define make-macro
|
||||
(lambda (s)
|
||||
(let ([ls (string->list s)])
|
||||
(lambda (args ib)
|
||||
(let loop ([ls ls])
|
||||
(unless (null? ls)
|
||||
(case (and (char=? (car ls) #\$)
|
||||
(not (null? (cdr ls)))
|
||||
(cadr ls))
|
||||
[#\1 (loop (cddr ls)) (unget-string ($1 args) ib)]
|
||||
[#\2 (loop (cddr ls)) (unget-string ($2 args) ib)]
|
||||
[#\3 (loop (cddr ls)) (unget-string ($3 args) ib)]
|
||||
[#\4 (loop (cddr ls)) (unget-string ($4 args) ib)]
|
||||
[#\5 (loop (cddr ls)) (unget-string ($5 args) ib)]
|
||||
[#\6 (loop (cddr ls)) (unget-string ($6 args) ib)]
|
||||
[#\7 (loop (cddr ls)) (unget-string ($7 args) ib)]
|
||||
[#\8 (loop (cddr ls)) (unget-string ($8 args) ib)]
|
||||
[#\9 (loop (cddr ls)) (unget-string ($9 args) ib)]
|
||||
[else (loop (cdr ls)) (m4-unget-char (car ls) ib)])))))))
|
||||
|
||||
(define-builtin-macro "divert"
|
||||
(lambda (args ib)
|
||||
(set! *divnum*
|
||||
(if (string=? ($1 args) "")
|
||||
0
|
||||
(case (string-ref ($1 args) 0)
|
||||
[#\0 0]
|
||||
[#\1 1]
|
||||
[#\2 2]
|
||||
[#\3 3]
|
||||
[#\4 4]
|
||||
[#\5 5]
|
||||
[#\6 6]
|
||||
[#\7 7]
|
||||
[#\8 8]
|
||||
[#\9 9]
|
||||
[else -1])))
|
||||
(when (and (<= 1 *divnum* 9) (not (vector-ref *diversions* *divnum*)))
|
||||
(vector-set! *diversions* *divnum* (open-output-string)))))
|
||||
|
||||
(define-builtin-macro "divnum"
|
||||
(lambda (args ib)
|
||||
(unget-string (format "~a" *divnum*) ib)))
|
||||
|
||||
(define-builtin-macro "dnl"
|
||||
(lambda (args ib)
|
||||
(let loop ()
|
||||
(let ([c (m4-get-char ib)])
|
||||
(cond
|
||||
[(eof-object? c) '()]
|
||||
[(char=? c #\newline) '()]
|
||||
[else (loop)])))))
|
||||
|
||||
(define-builtin-macro "dumpdef"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no dumpdef yet~%")))
|
||||
|
||||
(define-builtin-macro "errprint"
|
||||
(lambda (args ib)
|
||||
(display ($1 args) *error-output*)
|
||||
(newline *error-output*)))
|
||||
|
||||
(define-builtin-macro "eval"
|
||||
(lambda (args ib)
|
||||
(unget-string (int->str (eval-string ($1 args))) ib)))
|
||||
|
||||
(define-builtin-macro "ifdef"
|
||||
(lambda (args ib)
|
||||
(unget-string ((if (assoc ($1 args) *macros*) $2 $3) args) ib)))
|
||||
|
||||
(define-builtin-macro "ifelse"
|
||||
(rec ifelse
|
||||
(lambda (args ib)
|
||||
(if (string=? ($1 args) ($2 args))
|
||||
(unget-string ($3 args) ib)
|
||||
(if (> (length args) 4)
|
||||
(ifelse ($$ ($$ ($$ args))) ib)
|
||||
(unget-string ($4 args) ib))))))
|
||||
|
||||
(define-builtin-macro "include"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no include yet~%")))
|
||||
|
||||
(define-builtin-macro "incr"
|
||||
(lambda (args ib)
|
||||
(unget-string (int->str (+ (str->int ($1 args)) 1)) ib)))
|
||||
|
||||
(define-builtin-macro "index"
|
||||
(lambda (args ib)
|
||||
(let ([s1 ($1 args)] [s2 ($2 args)])
|
||||
(let ([n1 (string-length s1)] [n2 (string-length s2)])
|
||||
(let find ([i 0])
|
||||
(if (fx> n2 (fx- n1 i))
|
||||
(unget-string "-1" ib)
|
||||
(let try ([i1 i] [i2 0])
|
||||
(if (fx= i2 n2)
|
||||
(unget-string (int->str i) ib)
|
||||
(if (char=? (string-ref s1 i1) (string-ref s2 i2))
|
||||
(try (fx+ i1 1) (fx+ i2 1))
|
||||
(find (fx+ i 1)))))))))))
|
||||
|
||||
(define-builtin-macro "len"
|
||||
(lambda (args ib)
|
||||
(unget-string (int->str (string-length ($1 args))) ib)))
|
||||
|
||||
(define-builtin-macro "maketemp"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no maketemp yet~%")))
|
||||
|
||||
(define-builtin-macro "shift"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no shift yet~%")))
|
||||
|
||||
(define-builtin-macro "sinclude"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no sinclude yet~%")))
|
||||
|
||||
(define-builtin-macro "substr"
|
||||
(lambda (args ib)
|
||||
(let ([s ($1 args)] [start ($2 args)] [count ($3 args)])
|
||||
(let ([n (string-length s)])
|
||||
(let ([start (min (max (str->int start) 0) n)])
|
||||
(let ([end (if (string=? count "")
|
||||
n
|
||||
(min (max (+ (str->int count) start) start) n))])
|
||||
(unget-string (substring s start end) ib)))))))
|
||||
|
||||
(define-builtin-macro "syscmd"
|
||||
;;; cannot be written in Scheme---needs something more powerful than
|
||||
;;; "system" or "process"
|
||||
(lambda (args ib)
|
||||
(printf "m4 warning: no syscmd yet~%")))
|
||||
|
||||
(define-builtin-macro "translit"
|
||||
(lambda (args ib)
|
||||
(let ([s1 ($1 args)] [s2 ($2 args)] [s3 ($3 args)])
|
||||
(let ([n1 (string-length s1)] [n2 (string-length s2)])
|
||||
(unless (= n2 (string-length s3))
|
||||
(error 'm4 "translit arguments ~s and ~s are not of same length"
|
||||
s2 s3))
|
||||
(when (null? *translit-table*)
|
||||
(set! *translit-table* (make-string 256)))
|
||||
(do ([i 0 (fx+ i 1)])
|
||||
((fx= i 256))
|
||||
(string-set! *translit-table* i (integer->char i)))
|
||||
(do ([i 0 (fx+ i 1)])
|
||||
((fx= i n2))
|
||||
(string-set! *translit-table*
|
||||
(char->integer (string-ref s2 i))
|
||||
(string-ref s3 i)))
|
||||
(let ([s4 (make-string n1)])
|
||||
(do ([i 0 (fx+ i 1)])
|
||||
((fx= i n1))
|
||||
(string-set! s4 i
|
||||
(string-ref *translit-table*
|
||||
(char->integer (string-ref s1 i)))))
|
||||
(unget-string s4 ib))))))
|
||||
|
||||
(define-builtin-macro "undefine"
|
||||
(lambda (args ib)
|
||||
(let ([a (assoc ($1 args) *macros*)])
|
||||
(unless a (error 'm4 "cannot undefine ~s (not defined)" ($1 args)))
|
||||
(set-car! a #f))))
|
||||
|
||||
(define-builtin-macro "undivert"
|
||||
(rec myself
|
||||
(lambda (args ib)
|
||||
(if (null? args)
|
||||
(myself '("1" "2" "3" "4" "5" "6" "7" "8" "9") ib)
|
||||
(for-each
|
||||
(lambda (x)
|
||||
(case (and (not (string=? x "")) (string-ref x 0))
|
||||
[#\1 (undivert 1)]
|
||||
[#\2 (undivert 2)]
|
||||
[#\3 (undivert 3)]
|
||||
[#\4 (undivert 4)]
|
||||
[#\5 (undivert 5)]
|
||||
[#\6 (undivert 6)]
|
||||
[#\7 (undivert 7)]
|
||||
[#\8 (undivert 8)]
|
||||
[#\9 (undivert 9)]))
|
||||
args)))))
|
||||
|
||||
(define undivert
|
||||
(lambda (n)
|
||||
(let ([op (vector-ref *diversions* n)])
|
||||
(when op
|
||||
(display (get-output-string op) (vector-ref *diversions* 0))))))
|
89
ta6ob/examples/macro.ss
Normal file
89
ta6ob/examples/macro.ss
Normal file
|
@ -0,0 +1,89 @@
|
|||
;;; Copyright (C) 1989 R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; PCScheme/MacScheme "macro" defined in terms of "syntax-case":
|
||||
|
||||
(define-syntax macro
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
((_ name fcn)
|
||||
#'(define-syntax name
|
||||
(lambda (x)
|
||||
(syntax-case x ()
|
||||
((k . stuff)
|
||||
(datum->syntax-object #'k
|
||||
(fcn (syntax-object->datum x)))))))))))
|
||||
|
||||
|
||||
;;; PCScheme/MacScheme "macro" defined in terms of "extend-syntax":
|
||||
;;; requires (current-expand eps-expand)
|
||||
|
||||
;(extend-syntax (macro)
|
||||
; [(macro name fcn)
|
||||
; (eval-when (compile load eval)
|
||||
; (let ([f fcn])
|
||||
; (extend-syntax (name)
|
||||
; [anything
|
||||
; ((with ([w 'with]) w)
|
||||
; ([v (f 'anything)]) v)])))])
|
||||
|
||||
;;; The strange expression "(with ([w 'with]) w)" is used to insert the
|
||||
;;; keyword "with" into the expansion. The "eval-when" in the expansion is
|
||||
;;; necessary to allow macros defined in a file to be used later in the
|
||||
;;; file, even if the file is compiled with "compile-file". If it were
|
||||
;;; left out, the implicit "eval-when" wrapped around the "extend-syntax"
|
||||
;;; would cause it to be evaluated, but without the enclosing "let"
|
||||
;;; expression. The enclosing "let" expression is necessary to cause the
|
||||
;;; function to be evaluated once, which may be important if the function
|
||||
;;; something other than a simple lambda expression.
|
||||
|
||||
|
||||
;;; PCScheme/MacScheme "macro" defined in terms of "define-syntax-expander":
|
||||
;;; requires (current-expand eps-expand)
|
||||
|
||||
;(extend-syntax (macro)
|
||||
; [(macro name fcn)
|
||||
; (define-syntax-expander name
|
||||
; (let ([f fcn])
|
||||
; (lambda (x e) (e (f x) e))))])
|
||||
|
||||
;;; The "eval-when" is not necessary because "define-syntax-expander"
|
||||
;;; expands into an "eval-when" expression, and the "let" expression is
|
||||
;;; tucked inside the "define-syntax-expander" expression.
|
||||
|
||||
;;; If you want to see the expander generated by either of the above
|
||||
;;; "extend-syntax" definitions looks like, use "extend-syntax/code" in
|
||||
;;; place of "extend-syntax":
|
||||
|
||||
;;; > (extend-syntax/code (macro)
|
||||
;;; [(macro name fcn)
|
||||
;;; (define-syntax-expander name
|
||||
;;; (let ([f fcn])
|
||||
;;; (lambda (x e) (e (f x) e))))])
|
||||
;;;
|
||||
;;; (lambda (x e)
|
||||
;;; (unless (procedure? e)
|
||||
;;; (error 'macro "~s is not a procedure" e))
|
||||
;;; (e (cond
|
||||
;;; [(syntax-match? '(macro * *) x)
|
||||
;;; `(define-syntax-expander ,(cadr x)
|
||||
;;; (let ([f ,@(cddr x)]) (lambda (x e) (e (f x) e))))]
|
||||
;;; [else (error 'macro "invalid syntax ~s" x)])
|
||||
;;; e))
|
127
ta6ob/examples/matrix.ss
Normal file
127
ta6ob/examples/matrix.ss
Normal file
|
@ -0,0 +1,127 @@
|
|||
;;; matrix.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; make-matrix creates a matrix (a vector of vectors).
|
||||
(define make-matrix
|
||||
(lambda (rows columns)
|
||||
(do ((m (make-vector rows))
|
||||
(i 0 (+ i 1)))
|
||||
((= i rows) m)
|
||||
(vector-set! m i (make-vector columns)))))
|
||||
|
||||
;;; matrix? checks to see if its argument is a matrix.
|
||||
;;; It isn't foolproof, but it's generally good enough.
|
||||
(define matrix?
|
||||
(lambda (x)
|
||||
(and (vector? x)
|
||||
(> (vector-length x) 0)
|
||||
(vector? (vector-ref x 0)))))
|
||||
|
||||
;;; matrix-ref returns the jth element of the ith row.
|
||||
(define matrix-ref
|
||||
(lambda (m i j)
|
||||
(vector-ref (vector-ref m i) j)))
|
||||
|
||||
;;; matrix-set! changes the jth element of the ith row.
|
||||
(define matrix-set!
|
||||
(lambda (m i j x)
|
||||
(vector-set! (vector-ref m i) j x)))
|
||||
|
||||
;;; mul is the generic matrix/scalar multiplication procedure
|
||||
(define mul
|
||||
(lambda (x y)
|
||||
;; type-error is called to complain when mul receives an invalid
|
||||
;; type of argument.
|
||||
(define type-error
|
||||
(lambda (what)
|
||||
(error 'mul
|
||||
"~s is not a number or matrix"
|
||||
what)))
|
||||
|
||||
;; match-error is called to complain when mul receives a pair of
|
||||
;; incompatible arguments.
|
||||
(define match-error
|
||||
(lambda (what1 what2)
|
||||
(error 'mul
|
||||
"~s and ~s are incompatible operands"
|
||||
what1
|
||||
what2)))
|
||||
|
||||
;; matrix-rows returns the number of rows in a matrix.
|
||||
(define matrix-rows
|
||||
(lambda (x)
|
||||
(vector-length x)))
|
||||
|
||||
;; matrix-columns returns the number of columns in a matrix.
|
||||
(define matrix-columns
|
||||
(lambda (x)
|
||||
(vector-length (vector-ref x 0))))
|
||||
|
||||
;; mat-sca-mul multiplies a matrix by a scalar.
|
||||
(define mat-sca-mul
|
||||
(lambda (m x)
|
||||
(let* ((nr (matrix-rows m))
|
||||
(nc (matrix-columns m))
|
||||
(r (make-matrix nr nc)))
|
||||
(do ((i 0 (+ i 1)))
|
||||
((= i nr) r)
|
||||
(do ((j 0 (+ j 1)))
|
||||
((= j nc))
|
||||
(matrix-set! r i j
|
||||
(* x (matrix-ref m i j))))))))
|
||||
|
||||
;; mat-mat-mul multiplies one matrix by another, after verifying
|
||||
;; that the first matrix has as many columns as the second
|
||||
;; matrix has rows.
|
||||
(define mat-mat-mul
|
||||
(lambda (m1 m2)
|
||||
(let* ((nr1 (matrix-rows m1))
|
||||
(nr2 (matrix-rows m2))
|
||||
(nc2 (matrix-columns m2))
|
||||
(r (make-matrix nr1 nc2)))
|
||||
(if (not (= (matrix-columns m1) nr2))
|
||||
(match-error m1 m2))
|
||||
(do ((i 0 (+ i 1)))
|
||||
((= i nr1) r)
|
||||
(do ((j 0 (+ j 1)))
|
||||
((= j nc2))
|
||||
(do ((k 0 (+ k 1))
|
||||
(a 0
|
||||
(+ a
|
||||
(* (matrix-ref m1 i k)
|
||||
(matrix-ref m2 k j)))))
|
||||
((= k nr2)
|
||||
(matrix-set! r i j a))))))))
|
||||
|
||||
;; body of mul; dispatch based on input types
|
||||
(cond
|
||||
((number? x)
|
||||
(cond
|
||||
((number? y) (* x y))
|
||||
((matrix? y) (mat-sca-mul y x))
|
||||
(else (type-error y))))
|
||||
((matrix? x)
|
||||
(cond
|
||||
((number? y) (mat-sca-mul x y))
|
||||
((matrix? y) (mat-mat-mul x y))
|
||||
(else (type-error y))))
|
||||
(else (type-error x)))))
|
54
ta6ob/examples/object.ss
Normal file
54
ta6ob/examples/object.ss
Normal file
|
@ -0,0 +1,54 @@
|
|||
;;; object.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; define-object creates an object constructor that uses let* to bind
|
||||
;;; local fields and letrec to define the exported procedures. An
|
||||
;;; object is itself a procedure that accepts messages corresponding
|
||||
;;; to the names of the exported procedures. The second pattern is
|
||||
;;; used to allow the set of local fields to be omitted.
|
||||
(define-syntax define-object
|
||||
(syntax-rules ()
|
||||
((_ (name . varlist)
|
||||
((var1 val1) ...)
|
||||
((var2 val2) ...))
|
||||
(define name
|
||||
(lambda varlist
|
||||
(let* ((var1 val1) ...)
|
||||
(letrec ((var2 val2) ...)
|
||||
(lambda (msg . args)
|
||||
(case msg
|
||||
((var2) (apply var2 args)) ...
|
||||
(else
|
||||
(error 'name "invalid message ~s"
|
||||
(cons msg args))))))))))
|
||||
((_ (name . varlist)
|
||||
((var2 val2) ...))
|
||||
(define-object (name . varlist)
|
||||
()
|
||||
((var2 val2) ...)))))
|
||||
|
||||
;;; send-message abstracts the act of sending a message from the act
|
||||
;;; of applying a procedure and allows the message to be unquoted.
|
||||
(define-syntax send-message
|
||||
(syntax-rules ()
|
||||
((_ obj msg arg ...)
|
||||
(obj 'msg arg ...))))
|
12
ta6ob/examples/power.ss
Normal file
12
ta6ob/examples/power.ss
Normal file
|
@ -0,0 +1,12 @@
|
|||
;;; doubly recursive power (expt) function
|
||||
|
||||
;;; try using trace-lambda to see the nesting.
|
||||
|
||||
(define power
|
||||
(lambda (x n)
|
||||
(cond
|
||||
[(= n 0) 1]
|
||||
[(= n 1) x]
|
||||
[else
|
||||
(let ([q (quotient n 2)])
|
||||
(* (power x q) (power x (- n q))))])))
|
56
ta6ob/examples/queue.ss
Normal file
56
ta6ob/examples/queue.ss
Normal file
|
@ -0,0 +1,56 @@
|
|||
;;; queue
|
||||
;;; an abstract datatype
|
||||
|
||||
;;; operations:
|
||||
;;; (queue) ;create a queue object
|
||||
|
||||
;;; if 'q' is a queue object:
|
||||
|
||||
;;; (q 'type?) ;return the type (queue), useful if there are other
|
||||
;;; ;abstract datatypes floating around.
|
||||
;;; (q 'empty?) ;returns true iff q is empty
|
||||
;;; (q 'put val) ;adds val to end of q; returns val
|
||||
;;; (q 'get) ;removes first element of q and returns it
|
||||
|
||||
;;; Examples
|
||||
|
||||
;;; (define! q (queue))
|
||||
;;; (q 'type?) => queue
|
||||
;;; (q 'empty?) => #!true
|
||||
;;; (q 'put 3)
|
||||
;;; (q 'put 4)
|
||||
;;; (q 'put 5)
|
||||
;;; (q 'empty?) => ()
|
||||
;;; (q 'get) => 3
|
||||
;;; (q 'get) => 4
|
||||
;;; (q 'put 7)
|
||||
;;; (q 'get) => 5
|
||||
;;; (q 'get) => 7
|
||||
;;; (q 'empty?) => #!true
|
||||
|
||||
(define queue
|
||||
(lambda ()
|
||||
(let ([head '()] [tail '()])
|
||||
(lambda (request . args)
|
||||
(case request
|
||||
[type? 'queue]
|
||||
[empty? (null? head)]
|
||||
[put
|
||||
(let ([v (car args)])
|
||||
(if (null? head)
|
||||
(let ([p (cons v '())])
|
||||
(set! tail p)
|
||||
(set! head p))
|
||||
(let ([quebit (cons v '())])
|
||||
(set-cdr! tail quebit)
|
||||
(set! tail quebit)))
|
||||
v)]
|
||||
[get
|
||||
(if (null? head)
|
||||
(error 'queue "queue is empty")
|
||||
(let ([v (car head)])
|
||||
(set! head (cdr head))
|
||||
(when (null? head) (set! tail '()))
|
||||
v))]
|
||||
[else
|
||||
(error 'queue "~s is not a valid request" request)])))))
|
90
ta6ob/examples/rabbit.ss
Normal file
90
ta6ob/examples/rabbit.ss
Normal file
|
@ -0,0 +1,90 @@
|
|||
;;; rabbit
|
||||
|
||||
;;; The rabbit program highlights the use of continuations and
|
||||
;;; timer interrupts to perform thread scheduling. The scheduler
|
||||
;;; maintains a thread queue and operating system primitives for
|
||||
;;; dispatching and thread creation. The queue is only visible
|
||||
;;; to the operating system kernel and all accesses are performed
|
||||
;;; with the timer off to prevent corruption.
|
||||
|
||||
;;; (thread exp) will create a thread out of exp and place it in
|
||||
;;; the thread queue. you may do this for as many threads as
|
||||
;;; you like. (dispatch) starts the threads going. If the
|
||||
;;; thread queue ever becomes empty, dispatch exits. Threads
|
||||
;;; may create other threads.
|
||||
|
||||
;;; The rabbit function creates a thread that spawns two offspring
|
||||
;;; and dies. Each thread has a generation number associated with
|
||||
;;; it. The generation number of each rabbit is one lower than that
|
||||
;;; of it's parent; rabbits in generation 0 are sterile.
|
||||
|
||||
;;; load the queue datatype -- might need a fuller pathname
|
||||
(load "queue.ss")
|
||||
|
||||
;;; swap-time determines the number of timer ticks in a time slice
|
||||
(define swap-time
|
||||
(make-parameter
|
||||
100
|
||||
(lambda (x)
|
||||
(unless (and (integer? x) (positive? x))
|
||||
(error 'swap-time "~s is not a positive integer" x))
|
||||
x)))
|
||||
|
||||
(define dispatch #f)
|
||||
(define thread #f)
|
||||
|
||||
(let ([pq (queue)])
|
||||
(set! dispatch
|
||||
(lambda ()
|
||||
(unless (pq 'empty?)
|
||||
; the thread queue holds continuations---grab one and invoke it
|
||||
(let ([next (pq 'get)])
|
||||
(set-timer (swap-time))
|
||||
(next #f)))))
|
||||
(set! thread
|
||||
(lambda (thunk)
|
||||
(call/cc
|
||||
(lambda (return)
|
||||
(call/cc
|
||||
(lambda (k)
|
||||
; turn off the timer while accessing the queue
|
||||
(let ([time-left (set-timer 0)])
|
||||
; put the thread on the queue
|
||||
(pq 'put k)
|
||||
(set-timer time-left)
|
||||
; get out of here
|
||||
(return #f))))
|
||||
; the first time through we will return before getting
|
||||
; here. the second time is when a thread is first
|
||||
; dispatched from the thread queue.
|
||||
(thunk)
|
||||
(set-timer 0)
|
||||
(dispatch)))))
|
||||
(timer-interrupt-handler
|
||||
(lambda ()
|
||||
(printf "swapping~%")
|
||||
(call/cc
|
||||
(lambda (l)
|
||||
; place the continuation of the interrupt on the queue
|
||||
(pq 'put l)
|
||||
(dispatch))))))
|
||||
|
||||
|
||||
;;; *delay-max* gives the maximum random delay before a rabbit
|
||||
;;; reaches child-bearing age.
|
||||
(define *delay-max* 10000)
|
||||
|
||||
(define rabbit
|
||||
(lambda (n)
|
||||
(thread
|
||||
(lambda ()
|
||||
(printf "~s~%" n)
|
||||
(unless (zero? n)
|
||||
(do ([i (random *delay-max*) (1- i)]) ((zero? i)))
|
||||
(rabbit (1- n))
|
||||
(rabbit (1- n)))))))
|
||||
|
||||
;;; try:
|
||||
;;; (rabbit 3)
|
||||
;;; (rabbit 5)
|
||||
;;; (dispatch)
|
308
ta6ob/examples/rsa.ss
Normal file
308
ta6ob/examples/rsa.ss
Normal file
|
@ -0,0 +1,308 @@
|
|||
;;; rsa.ss
|
||||
;;; Bruce T. Smith, University of North Carolina at Chapel Hill
|
||||
;;; (circa 1984)
|
||||
|
||||
;;; Updated for Chez Scheme Version 7, May 2005
|
||||
|
||||
;;; This is a toy example of an RSA public-key encryption system. It
|
||||
;;; is possible to create users who register their public keys with a
|
||||
;;; center and hide their private keys. Then, it is possible to have
|
||||
;;; the users exchange messages. To a limited extent one can look at
|
||||
;;; the intermediate steps of the process by using encrypt and decrypt.
|
||||
;;; The encrypted messages are represented by lists of numbers.
|
||||
|
||||
;;; Example session:
|
||||
|
||||
#|
|
||||
> (make-user bonzo)
|
||||
Registered with Center
|
||||
User: bonzo
|
||||
Base: 152024296883113044375867034718782727467
|
||||
Encryption exponent: 7
|
||||
> (make-user bobo)
|
||||
Registered with Center
|
||||
User: bobo
|
||||
Base: 244692569127295893294157219042233636899
|
||||
Encryption exponent: 5
|
||||
> (make-user tiger)
|
||||
Registered with Center
|
||||
User: tiger
|
||||
Base: 138555414233087084786368622588289286073
|
||||
Encryption exponent: 7
|
||||
> (show-center)
|
||||
|
||||
User: tiger
|
||||
Base: 138555414233087084786368622588289286073
|
||||
Encryption exponent: 7
|
||||
|
||||
User: bobo
|
||||
Base: 244692569127295893294157219042233636899
|
||||
Encryption exponent: 5
|
||||
|
||||
User: bonzo
|
||||
Base: 152024296883113044375867034718782727467
|
||||
Encryption exponent: 7
|
||||
> (send "hi there" bonzo bobo)
|
||||
"hi there"
|
||||
> (send "hi there to you" bobo bonzo)
|
||||
"hi there to you"
|
||||
> (decrypt (encrypt "hi there" bonzo bobo) tiger)
|
||||
" #z R4WN Zbb E8J"
|
||||
|#
|
||||
|
||||
;;; Implementation:
|
||||
|
||||
(module ((make-user user) show-center encrypt decrypt send)
|
||||
|
||||
;;; (make-user name) creates a user with the chosen name. When it
|
||||
;;; creates the user, it tells him what his name is. He will use
|
||||
;;; this when registering with the center.
|
||||
|
||||
(define-syntax make-user
|
||||
(syntax-rules ()
|
||||
[(_ uid)
|
||||
(begin (define uid (user 'uid)) (uid 'register))]))
|
||||
|
||||
;;; (encrypt mesg u1 u2) causes user 1 to encrypt mesg using the public
|
||||
;;; keys for user 2.
|
||||
|
||||
(define-syntax encrypt
|
||||
(syntax-rules ()
|
||||
[(_ mesg u1 u2) ((u1 'send) mesg 'u2)]))
|
||||
|
||||
;;; (decrypt number-list u) causes the user to decrypt the list of
|
||||
;;; numbers using his private key.
|
||||
|
||||
(define-syntax decrypt
|
||||
(syntax-rules ()
|
||||
[(_ numbers u) ((u 'receive) numbers)]))
|
||||
|
||||
;;; (send mesg u1 u2) this combines the functions 'encrypt' and 'decrypt',
|
||||
;;; calling on user 1 to encrypt the message for user 2 and calling on
|
||||
;;; user 2 to decrypt the message.
|
||||
|
||||
(define-syntax send
|
||||
(syntax-rules ()
|
||||
[(_ mesg u1 u2) (decrypt (encrypt mesg u1 u2) u2)]))
|
||||
|
||||
;;; A user is capable of the following:
|
||||
;;; - choosing public and private keys and registering with the center
|
||||
;;; - revealing his public and private keys
|
||||
;;; - retrieving user's private keys from the center and encrypting a
|
||||
;;; message for that user
|
||||
;;; - decrypting a message with his private key
|
||||
|
||||
(define user
|
||||
(lambda (name)
|
||||
(let* ([low (expt 2 63)] ; low, high = bounds on p and q
|
||||
[high (* 2 low)]
|
||||
[p 0] ; p,q = two large, probable primes
|
||||
[q 0]
|
||||
[n 0] ; n = p * q, base for modulo arithmetic
|
||||
[phi 0] ; phi = lcm(p-1,q-1), not quite the Euler phi function,
|
||||
; but it will serve for our purposes
|
||||
[e 0] ; e = exponent for encryption
|
||||
[d 0]) ; d = exponent for decryption
|
||||
(lambda (request)
|
||||
(case request
|
||||
;; choose keys and register with the center
|
||||
[register
|
||||
(set! p (find-prime low high))
|
||||
(set! q
|
||||
(let loop ([q1 (find-prime low high)])
|
||||
(if (= 1 (gcd p q1))
|
||||
q1
|
||||
(loop (find-prime low high)))))
|
||||
(set! n (* p q))
|
||||
(set! phi
|
||||
(/ (* (1- p) (1- q))
|
||||
(gcd (1- p) (1- q))))
|
||||
(set! e
|
||||
(do ([i 3 (+ 2 i)])
|
||||
((= 1 (gcd i phi)) i)))
|
||||
(set! d (mod-inverse e phi))
|
||||
(register-center (cons name (list n e)))
|
||||
(printf "Registered with Center~%")
|
||||
(printf "User: ~s~%" name)
|
||||
(printf "Base: ~d~%" n)
|
||||
(printf "Encryption exponent: ~d~%" e)]
|
||||
|
||||
;; divulge your keys-- you should resist doing this...
|
||||
[show-all
|
||||
(printf "p = ~d ; q = ~d~%" p q)
|
||||
(printf "n = ~d~%" n)
|
||||
(printf "phi = ~d~%" (* (1- p) (1- q)))
|
||||
(printf "e = ~d ; d = ~d~%" e d)]
|
||||
|
||||
;; get u's public key from the center and encode
|
||||
;; a message for him
|
||||
[send
|
||||
(lambda (mesg u)
|
||||
(let* ([public (request-center u)]
|
||||
[base (car public)]
|
||||
[exponent (cadr public)]
|
||||
[mesg-list (string->numbers mesg base)])
|
||||
(map (lambda (x) (expt-mod x exponent base))
|
||||
mesg-list)))]
|
||||
|
||||
;; decrypt a message with your private key
|
||||
[receive
|
||||
(lambda (crypt-mesg)
|
||||
(let ([mesg-list (map (lambda (x) (expt-mod x d n)) crypt-mesg)])
|
||||
(numbers->string mesg-list)))])))))
|
||||
|
||||
;;; The center maintains the list of public keys. It can register
|
||||
;;; new users, provide the public keys for any particular user, or
|
||||
;;; display the whole public file.
|
||||
|
||||
(module (register-center request-center show-center)
|
||||
(define public-keys '())
|
||||
(define register-center
|
||||
(lambda (entry)
|
||||
(set! public-keys
|
||||
(cons entry
|
||||
(remq (assq (car entry) public-keys) public-keys)))))
|
||||
(define request-center
|
||||
(lambda (u)
|
||||
(let ([a (assoc u public-keys)])
|
||||
(when (null? a)
|
||||
(error 'request-center
|
||||
"User ~s not registered in center"
|
||||
u))
|
||||
(cdr a))))
|
||||
(define show-center
|
||||
(lambda ()
|
||||
(for-each
|
||||
(lambda (entry)
|
||||
(printf "~%User: ~s~%" (car entry))
|
||||
(printf "Base: ~s~%" (cadr entry))
|
||||
(printf "Encryption exponent: ~s~%" (caddr entry)))
|
||||
public-keys)))
|
||||
)
|
||||
|
||||
;;; string->numbers encodes a string as a list of numbers
|
||||
;;; numbers->string decodes a string from a list of numbers
|
||||
|
||||
;;; string->numbers and numbers->string are defined with respect to
|
||||
;;; an alphabet. Any characters in the alphabet are translated into
|
||||
;;; integers---their regular ascii codes. Any characters outside
|
||||
;;; the alphabet cause an error during encoding. An invalid code
|
||||
;;; during decoding is translated to a space.
|
||||
|
||||
(module (string->numbers numbers->string)
|
||||
(define first-code 32)
|
||||
(define last-code 126)
|
||||
(define alphabet
|
||||
; printed form of the characters, indexed by their ascii codes
|
||||
(let ([alpha (make-string 128 #\space)])
|
||||
(do ([i first-code (1+ i)])
|
||||
((= i last-code) alpha)
|
||||
(string-set! alpha i (integer->char i)))))
|
||||
|
||||
(define string->integer
|
||||
(lambda (str)
|
||||
(let ([ln (string-length str)])
|
||||
(let loop ([i 0] [m 0])
|
||||
(if (= i ln)
|
||||
m
|
||||
(let* ([c (string-ref str i)] [code (char->integer c)])
|
||||
(when (or (< code first-code) (>= code last-code))
|
||||
(error 'rsa "Illegal character ~s" c))
|
||||
(loop (1+ i) (+ code (* m 128)))))))))
|
||||
|
||||
(define integer->string
|
||||
(lambda (n)
|
||||
(list->string
|
||||
(map (lambda (n) (string-ref alphabet n))
|
||||
(let loop ([m n] [lst '()])
|
||||
(if (zero? m)
|
||||
lst
|
||||
(loop (quotient m 128)
|
||||
(cons (remainder m 128) lst))))))))
|
||||
|
||||
; turn a string into a list of numbers, each no larger than base
|
||||
(define string->numbers
|
||||
(lambda (str base)
|
||||
(letrec ([block-size
|
||||
(do ([i -1 (1+ i)] [m 1 (* m 128)]) ((>= m base) i))]
|
||||
[substring-list
|
||||
(lambda (str)
|
||||
(let ([ln (string-length str)])
|
||||
(if (>= block-size ln)
|
||||
(list str)
|
||||
(cons (substring str 0 block-size)
|
||||
(substring-list
|
||||
(substring str block-size ln))))))])
|
||||
(map string->integer (substring-list str)))))
|
||||
|
||||
; turn a list of numbers into a string
|
||||
(define numbers->string
|
||||
(lambda (lst)
|
||||
(letrec ([reduce
|
||||
(lambda (f l)
|
||||
(if (null? (cdr l))
|
||||
(car l)
|
||||
(f (car l) (reduce f (cdr l)))))])
|
||||
(reduce
|
||||
string-append
|
||||
(map (lambda (x) (integer->string x)) lst)))))
|
||||
)
|
||||
|
||||
;;; find-prime finds a probable prime between two given arguments.
|
||||
;;; find-prime uses a cheap but fairly dependable test for primality
|
||||
;;; for large numbers, by first weeding out multiples of first 200
|
||||
;;; primes, then applies Fermat's theorem with base 2.
|
||||
|
||||
(module (find-prime)
|
||||
(define product-of-primes
|
||||
; compute product of first n primes, n > 0
|
||||
(lambda (n)
|
||||
(let loop ([n (1- n)] [p 2] [i 3])
|
||||
(cond
|
||||
[(zero? n) p]
|
||||
[(= 1 (gcd i p)) (loop (1- n) (* p i) (+ i 2))]
|
||||
[else (loop n p (+ i 2))]))))
|
||||
(define prod-first-200-primes (product-of-primes 200))
|
||||
(define probable-prime
|
||||
; first check is quick, and weeds out most non-primes
|
||||
; second check is slower, but weeds out almost all non-primes
|
||||
(lambda (p)
|
||||
(and (= 1 (gcd p prod-first-200-primes))
|
||||
(= 1 (expt-mod 2 (1- p) p)))))
|
||||
(define find-prime
|
||||
; find probable prime in range low to high (inclusive)
|
||||
(lambda (low high)
|
||||
(let ([guess
|
||||
(lambda (low high)
|
||||
(let ([g (+ low (random (1+ (- high low))))])
|
||||
(if (odd? g) g (1+ g))))])
|
||||
(let loop ([g (guess low high)])
|
||||
(cond
|
||||
; start over if already too high
|
||||
[(> g high) (loop (guess low high))]
|
||||
; if guess is probably prime, return
|
||||
[(probable-prime g) g]
|
||||
; don't bother with even guesses
|
||||
[else (loop (+ 2 g))])))))
|
||||
)
|
||||
|
||||
;;; mod-inverse finds the multiplicative inverse of x mod b, if it exists
|
||||
|
||||
(module (mod-inverse)
|
||||
(define gcdx
|
||||
; extended Euclid's gcd algorithm, x <= y
|
||||
(lambda (x y)
|
||||
(let loop ([x x] [y y] [u1 1] [u2 0] [v1 0] [v2 1])
|
||||
(if (zero? y)
|
||||
(list x u1 v1)
|
||||
(let ([q (quotient x y)] [r (remainder x y)])
|
||||
(loop y r u2 (- u1 (* q u2)) v2 (- v1 (* q v2))))))))
|
||||
|
||||
(define mod-inverse
|
||||
(lambda (x b)
|
||||
(let* ([x1 (modulo x b)] [g (gcdx x1 b)])
|
||||
(unless (= (car g) 1)
|
||||
(error 'mod-inverse "~d and ~d not relatively prime" x b))
|
||||
(modulo (cadr g) b))))
|
||||
)
|
||||
)
|
41
ta6ob/examples/scons.ss
Normal file
41
ta6ob/examples/scons.ss
Normal file
|
@ -0,0 +1,41 @@
|
|||
;;; scons.ss
|
||||
;;; a stream-construction facility
|
||||
|
||||
;;; The scons special form performs a cons, suspending the cdr field
|
||||
;;; by enclosing it in a procedure of no arguments. scdr tests to see
|
||||
;;; if the cdr is a procedure, and if so, invokes it. scar is provided
|
||||
;;; for symmetry; it is just car.
|
||||
|
||||
;;; The function stream-ref is simply list-ref defined in terms of
|
||||
;;; scdr and scar.
|
||||
|
||||
;;; factlist and fiblist are two infinite streams.
|
||||
;;; Try (stream-ref factlist 10) or (stream-ref fiblist 20).
|
||||
|
||||
;;; scons could easily suspend the car field as well. This would
|
||||
;;; implement the lazy cons of Friedman & Wise.
|
||||
|
||||
(define-syntax scons
|
||||
(syntax-rules ()
|
||||
((_ car cdr) (cons car (lambda () cdr)))))
|
||||
|
||||
(define scar car)
|
||||
|
||||
(define scdr
|
||||
(lambda (x)
|
||||
(when (procedure? (cdr x)) (set-cdr! x ((cdr x))))
|
||||
(cdr x)))
|
||||
|
||||
(define stream-ref
|
||||
(lambda (x n)
|
||||
(if (zero? n)
|
||||
(scar x)
|
||||
(stream-ref (scdr x) (1- n)))))
|
||||
|
||||
(define factlist
|
||||
(let fact ([a 1] [n 1])
|
||||
(scons a (fact (* a n) (1+ n)))))
|
||||
|
||||
(define fiblist
|
||||
(let fib ([fib-2 0] [fib-1 1])
|
||||
(scons fib-1 (fib fib-1 (+ fib-2 fib-1)))))
|
52
ta6ob/examples/setof.ss
Normal file
52
ta6ob/examples/setof.ss
Normal file
|
@ -0,0 +1,52 @@
|
|||
;;; setof.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;;; set-of uses helper syntactic extension set-of-help, passing it
|
||||
;;; an initial base expression of '()
|
||||
(define-syntax set-of
|
||||
(syntax-rules ()
|
||||
((_ e m ...)
|
||||
(set-of-help e '() m ...))))
|
||||
|
||||
;;; set-of-help recognizes in, is, and predicate expressions and
|
||||
;;; changes them into nested named let, let, and if expressions.
|
||||
(define-syntax set-of-help
|
||||
(syntax-rules (in is)
|
||||
((_ e base)
|
||||
(set-cons e base))
|
||||
((_ e base (x in s) m ...)
|
||||
(let loop ((set s))
|
||||
(if (null? set)
|
||||
base
|
||||
(let ((x (car set)))
|
||||
(set-of-help e (loop (cdr set)) m ...)))))
|
||||
((_ e base (x is y) m ...)
|
||||
(let ((x y)) (set-of-help e base m ...)))
|
||||
((_ e base p m ...)
|
||||
(if p (set-of-help e base m ...) base))))
|
||||
|
||||
;;; set-cons returns the original set y if x is already in y.
|
||||
(define set-cons
|
||||
(lambda (x y)
|
||||
(if (memv x y)
|
||||
y
|
||||
(cons x y))))
|
248
ta6ob/examples/socket.ss
Normal file
248
ta6ob/examples/socket.ss
Normal file
|
@ -0,0 +1,248 @@
|
|||
;;; socket.ss
|
||||
;;; R. Kent Dybvig May 1998
|
||||
;;; Updated November 2005
|
||||
;;; Updated by Jamie Taylor, Sept 2016
|
||||
;;; Public Domain
|
||||
;;;
|
||||
;;; bindings for socket operations and other items useful for writing
|
||||
;;; programs that use sockets.
|
||||
|
||||
;;; Requires csocket.so, built from csocket.c.
|
||||
;;; Example compilation command line from macOS:
|
||||
;;; cc -c csocket.c -o csocket.o
|
||||
;;; cc csocket.o -dynamic -dynamiclib -current_version 1.0 -compatibility_version 1.0 -o csocket.so
|
||||
(load-shared-object "./csocket.so")
|
||||
|
||||
;;; Requires from C library:
|
||||
;;; close, dup, execl, fork, kill, listen, tmpnam, unlink
|
||||
(case (machine-type)
|
||||
[(i3le ti3le a6le ta6le) (load-shared-object "libc.so.6")]
|
||||
[(i3osx ti3osx a6osx ta6osx) (load-shared-object "libc.dylib")]
|
||||
[else (load-shared-object "libc.so")])
|
||||
|
||||
;;; basic C-library stuff
|
||||
|
||||
(define close
|
||||
(foreign-procedure "close" (int)
|
||||
int))
|
||||
|
||||
(define dup
|
||||
(foreign-procedure "dup" (int)
|
||||
int))
|
||||
|
||||
(define execl4
|
||||
(let ((execl-help
|
||||
(foreign-procedure "execl"
|
||||
(string string string string void*)
|
||||
int)))
|
||||
(lambda (s1 s2 s3 s4)
|
||||
(execl-help s1 s2 s3 s4 0))))
|
||||
|
||||
(define fork
|
||||
(foreign-procedure "fork" ()
|
||||
int))
|
||||
|
||||
(define kill
|
||||
(foreign-procedure "kill" (int int)
|
||||
int))
|
||||
|
||||
(define listen
|
||||
(foreign-procedure "listen" (int int)
|
||||
int))
|
||||
|
||||
(define tmpnam
|
||||
(foreign-procedure "tmpnam" (void*)
|
||||
string))
|
||||
|
||||
(define unlink
|
||||
(foreign-procedure "unlink" (string)
|
||||
int))
|
||||
|
||||
;;; routines defined in csocket.c
|
||||
|
||||
(define accept
|
||||
(foreign-procedure "do_accept" (int)
|
||||
int))
|
||||
|
||||
(define bytes-ready?
|
||||
(foreign-procedure "bytes_ready" (int)
|
||||
boolean))
|
||||
|
||||
(define bind
|
||||
(foreign-procedure "do_bind" (int string)
|
||||
int))
|
||||
|
||||
(define c-error
|
||||
(foreign-procedure "get_error" ()
|
||||
string))
|
||||
|
||||
(define c-read
|
||||
(foreign-procedure "c_read" (int u8* size_t size_t)
|
||||
ssize_t))
|
||||
|
||||
(define c-write
|
||||
(foreign-procedure "c_write" (int u8* size_t ssize_t)
|
||||
ssize_t))
|
||||
|
||||
(define connect
|
||||
(foreign-procedure "do_connect" (int string)
|
||||
int))
|
||||
|
||||
(define socket
|
||||
(foreign-procedure "do_socket" ()
|
||||
int))
|
||||
|
||||
;;; higher-level routines
|
||||
|
||||
(define dodup
|
||||
; (dodup old new) closes old and dups new, then checks to
|
||||
; make sure that resulting fd is the same as old
|
||||
(lambda (old new)
|
||||
(check 'close (close old))
|
||||
(unless (= (dup new) old)
|
||||
(error 'dodup
|
||||
"couldn't set up child process io for fd ~s" old))))
|
||||
|
||||
(define dofork
|
||||
; (dofork child parent) forks a child process and invokes child
|
||||
; without arguments and parent with the child's pid
|
||||
(lambda (child parent)
|
||||
(let ([pid (fork)])
|
||||
(cond
|
||||
[(= pid 0) (child)]
|
||||
[(> pid 0) (parent pid)]
|
||||
[else (error 'fork (c-error))]))))
|
||||
|
||||
(define setup-server-socket
|
||||
; create a socket, bind it to name, and listen for connections
|
||||
(lambda (name)
|
||||
(let ([sock (check 'socket (socket))])
|
||||
(unlink name)
|
||||
(check 'bind (bind sock name))
|
||||
(check 'listen (listen sock 1))
|
||||
sock)))
|
||||
|
||||
(define setup-client-socket
|
||||
; create a socket and attempt to connect to server
|
||||
(lambda (name)
|
||||
(let ([sock (check 'socket (socket))])
|
||||
(check 'connect (connect sock name))
|
||||
sock)))
|
||||
|
||||
(define accept-socket
|
||||
; accept a connection
|
||||
(lambda (sock)
|
||||
(check 'accept (accept sock))))
|
||||
|
||||
(define check
|
||||
; signal an error if status x is negative, using c-error to
|
||||
; obtain the operating-system's error message
|
||||
(lambda (who x)
|
||||
(if (< x 0)
|
||||
(error who (c-error))
|
||||
x)))
|
||||
|
||||
(define terminate-process
|
||||
; kill the process identified by pid
|
||||
(lambda (pid)
|
||||
(define sigterm 15)
|
||||
(kill pid sigterm)
|
||||
(void)))
|
||||
|
||||
(define open-process
|
||||
(lambda (command)
|
||||
(define (make-r! socket)
|
||||
(lambda (bv start n)
|
||||
(check 'r! (c-read socket bv start n))))
|
||||
(define (make-w! socket)
|
||||
(lambda (bv start n)
|
||||
(check 'w! (c-write socket bv start n))))
|
||||
(define (make-close pid socket)
|
||||
(lambda ()
|
||||
(check 'close (close socket))
|
||||
(terminate-process pid)))
|
||||
(let* ([server-socket-name (tmpnam 0)]
|
||||
[server-socket (setup-server-socket server-socket-name)])
|
||||
(dofork
|
||||
(lambda () ; child
|
||||
(check 'close (close server-socket))
|
||||
(let ([sock (setup-client-socket server-socket-name)])
|
||||
(dodup 0 sock)
|
||||
(dodup 1 sock))
|
||||
(check 'execl (execl4 "/bin/sh" "/bin/sh" "-c" command))
|
||||
(error 'open-process "subprocess exec failed"))
|
||||
(lambda (pid) ; parent
|
||||
(let ([sock (accept-socket server-socket)])
|
||||
(check 'close (close server-socket))
|
||||
(make-custom-binary-input/output-port command
|
||||
(make-r! sock) (make-w! sock) #f #f (make-close pid sock))))))))
|
||||
|
||||
#!eof
|
||||
|
||||
;;; sample session using base socket functionality
|
||||
|
||||
> (define client-pid)
|
||||
> (define client-socket)
|
||||
> (let* ([server-socket-name (tmpnam 0)]
|
||||
[server-socket (setup-server-socket server-socket-name)])
|
||||
; fork a child, use it to exec a client Scheme process, and set
|
||||
; up server-side client-pid and client-socket variables.
|
||||
(dofork ; child
|
||||
(lambda ()
|
||||
; the child establishes the socket input/output fds as
|
||||
; stdin and stdout, then starts a new Scheme session
|
||||
(check 'close (close server-socket))
|
||||
(let ([sock (setup-client-socket server-socket-name)])
|
||||
(dodup 0 sock)
|
||||
(dodup 1 sock))
|
||||
(check 'execl (execl4 "/bin/sh" "/bin/sh" "-c" "exec scheme"))
|
||||
(error 'client "returned!"))
|
||||
(lambda (pid) ; parent
|
||||
; the parent waits for a connection from the client
|
||||
(set! client-pid pid)
|
||||
(set! client-socket (accept-socket server-socket))
|
||||
(check 'close (close server-socket)))))
|
||||
> (define put ; procedure to send data to client
|
||||
(lambda (x)
|
||||
(let* ([s (format "~s~%" x)]
|
||||
[bv (string->utf8 s)])
|
||||
(c-write client-socket bv 0 (bytevector-length bv)))
|
||||
(void)))
|
||||
> (define get ; procedure to read data from client
|
||||
(let ([buff (make-bytevector 1024)])
|
||||
(lambda ()
|
||||
(let* ([n (c-read client-socket buff 0 (bytevector-length buff))]
|
||||
[bv (make-bytevector n)])
|
||||
(bytevector-copy! buff 0 bv 0 n)
|
||||
(printf "client:~%~a~%server:~%" (utf8->string bv))))))
|
||||
> (get)
|
||||
client:
|
||||
Chez Scheme Version 9.5.1
|
||||
Copyright 1984-2017 Cisco Systems, Inc.
|
||||
|
||||
>
|
||||
server:
|
||||
> (put '(let ((x 3)) x))
|
||||
> (get)
|
||||
client:
|
||||
3
|
||||
>
|
||||
server:
|
||||
> (terminate-process client-pid)
|
||||
> (exit)
|
||||
|
||||
|
||||
;;; sample session using process port
|
||||
|
||||
> (define p (transcoded-port (open-process "exec scheme -q") (native-transcoder)))
|
||||
> (pretty-print '(+ 3 4) p)
|
||||
> (read p)
|
||||
7
|
||||
> (pretty-print '(define (f x) (if (= x 0) 1 (* x (f (- x 1))))) p)
|
||||
> (pretty-print '(f 10) p)
|
||||
> (read p)
|
||||
3628800
|
||||
> (pretty-print '(exit) p)
|
||||
> (read p)
|
||||
#!eof
|
||||
> (close-port p)
|
858
ta6ob/examples/template.ss
Normal file
858
ta6ob/examples/template.ss
Normal file
|
@ -0,0 +1,858 @@
|
|||
#!chezscheme
|
||||
;;; Copyright 1984-2017 Cisco Systems, Inc.
|
||||
;;;
|
||||
;;; 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.
|
||||
;;; include-template: a simple yet powerful extensible templating mechanism
|
||||
;;;
|
||||
;;; Authors: Andrew W. Keep and R. Kent Dybvig
|
||||
;;;
|
||||
;;; The syntax (include-template <filename>) expands into an expression whose
|
||||
;;; value is a string containing the contents of the file named by <filename>,
|
||||
;;; except each occurrence of @@ within the file is replaced by @, and each
|
||||
;;; occurrence of @(<scheme expression>) is replaced with the value of
|
||||
;;; <scheme expression>. <filename> must be a string literal, and the value
|
||||
;;; of each <scheme expression> must be a string. The file named by <filename>
|
||||
;;; must be present at expand time and need not be present at run time.
|
||||
;;;
|
||||
;;; The template system can also be extended using the syntax:
|
||||
;;;
|
||||
;;; (define-template-extension <template-pattern> <output-pattern>)
|
||||
;;;
|
||||
;;; where:
|
||||
;;;
|
||||
;;; <template-pattern> -> (<initial-pattern> <additional-pattern>*)
|
||||
;;; | <non-template-pattern>
|
||||
;;; <initial-pattern> -> <keyword> (<syntax-pattern>*) <template-id>
|
||||
;;; | <keyword> <template-id>
|
||||
;;; <additional-pattern> -> <initial-pattern>
|
||||
;;; | (<keyword> (<syntax-pattern>*) <template-id>) ...
|
||||
;;; | (<keyword> <template-id>) ...
|
||||
;;; | (optional <keyword> (<syntax-pattern>*)
|
||||
;;; <template-id>)
|
||||
;;; | (optional <keyword> <template-id>)
|
||||
;;; <non-template-pattern> -> <keyword> (<syntax-pattern>*)
|
||||
;;; <keyword> -> <initial-kw-character> <kw-character>
|
||||
;;; <initial-kw-character> -> a - z | A - Z | ! | $ | % | & | * | / | : | <
|
||||
;;; | = | > | ? | ^ | _ | ~
|
||||
;;; <kw-character> -> <initial-kw-character> | 0 - 9 | - | + | .
|
||||
;;;
|
||||
;;; <output-pattern> is treated as a quasisyntax template,
|
||||
;;; <syntax-pattern> is a syntax-rules pattern and,
|
||||
;;; <template-id> is any valid scheme identifier.
|
||||
;;;
|
||||
;;; For <template-patterns> using a <non-template-pattern> a new @<keyword>
|
||||
;;; form is created and when the @<keyword>(<syntax-pattern>*) is encountered
|
||||
;;; in a template, it is immediately replaced with the <output-pattern>.
|
||||
;;;
|
||||
;;; For instance an extension that converts numbers to strings can be
|
||||
;;; implemented as:
|
||||
;;;
|
||||
;;; (define-template-extension (num (e)) (number->string e))
|
||||
;;;
|
||||
;;; When @num(10) is encountered in a template, the string "10" is generated.
|
||||
;;;
|
||||
;;; For <template-patterns> with an <inital-pattern> each <keyword> in the
|
||||
;;; <template-pattern> the define-template-extension will create a new syntax
|
||||
;;; form @<keyword> along with an @end<keyword> for the <keyword> from the
|
||||
;;; <initial-pattern>. When include-template encounters an
|
||||
;;; @<initial-keyword> ... @end<initial-keyword> pattern it will match the
|
||||
;;; <syntax-pattern>* (when supplied) against the following scheme expression
|
||||
;;; and match the template expressions found between the @<keywords> to the
|
||||
;;; matching <template-id> bindings.
|
||||
;;;
|
||||
;;; For example, we can add a "for" loop extension as:
|
||||
;;;
|
||||
;;; (define-template-extension (for ([x e] [xs es] ...) tmpl)
|
||||
;;; (let ([t e])
|
||||
;;; (apply string-append (map (lambda (x xs ...) tmpl) t es ...))))
|
||||
;;;
|
||||
;;; (for (exprs ...) tmpl)
|
||||
;;; =>
|
||||
;;; @for (lambda (...)
|
||||
;;; (let ([t (read-scheme k...)])
|
||||
;;; (cons (incomplete-node @for t) ...)))
|
||||
;;; @endfor (lambda (...)
|
||||
;;; ---
|
||||
;;; (let loop ([t '()] [tmpl '()] ...)
|
||||
;;; (cond
|
||||
;;; [(and (incomplete-node? (car stack)) (eq? (incomplete-node-kw (car stack)) '@for))
|
||||
;;; (with-syntax (['([x e] [xs es] ...) (incomplete-node-stx (car stack))])
|
||||
;;; '(let ([t e])
|
||||
;;; (apply string-append (map (lambda (x xs ...) tmpl) t es ..))))
|
||||
;;;
|
||||
;;; In a template if: @for([x '("a" "b" "c")]) got @(t). @endfor
|
||||
;;; is encountered, it will print the string " got a. got b. got c. " for
|
||||
;;; this expression (which will be produced by the code:
|
||||
;;; (apply string-append
|
||||
;;; (map (lambda (x) (string-append " got " x ". ") '("a" "b" "c"))))
|
||||
;;;
|
||||
;;; For a more complex example see the @if/@elif/@else/@endif example at
|
||||
;;; the end of the library.
|
||||
;;;
|
||||
;;; Additional examples are embedded within the tests below #!eof.
|
||||
|
||||
;;; The (template-helpers) library supplies scheme procedures that are used at
|
||||
;;; macro-expansion time by both the include-template and
|
||||
;;; define-template-extension macros.
|
||||
(library (template-helpers)
|
||||
(export
|
||||
incomplete-node? make-incomplete-node
|
||||
incomplete-node-type incomplete-node-e* incomplete-node-bfp
|
||||
source-string source-error
|
||||
read-scheme initial-id-char? id-char?
|
||||
make-string-buffer extend-string-buffer! extract-string-and-reset!
|
||||
open-positional-string-output-port
|
||||
strip-blanks)
|
||||
(import (chezscheme))
|
||||
|
||||
(define (source-string sfd bfp)
|
||||
(call-with-values
|
||||
(lambda () (locate-source sfd bfp))
|
||||
(case-lambda
|
||||
[() (format "at char position ~s of ~a" bfp
|
||||
(source-file-descriptor-path sfd))]
|
||||
[(path lp cp) (format "at line ~s, char ~s of ~a" lp cp
|
||||
(source-file-descriptor-path sfd))])))
|
||||
|
||||
(define (source-error sfd bfp msg . args)
|
||||
(errorf 'include-template "~? ~a" msg args (source-string sfd bfp)))
|
||||
|
||||
(define (read-scheme k ip sfd bfp)
|
||||
(let-values ([(x new-bfp) (get-datum/annotations ip sfd bfp)])
|
||||
(let ([x (if (annotation? x) (annotation-expression x) x)])
|
||||
(values (datum->syntax k x) new-bfp))))
|
||||
|
||||
(define-record-type incomplete-node (nongenerative) (fields type e* bfp))
|
||||
|
||||
(define (initial-id-char? c)
|
||||
(or (char<=? #\a c #\z) (char<=? #\A c #\Z)
|
||||
(char=? c #\!) (char<=? #\$ c #\&)
|
||||
(char=? c #\*) (char=? c #\/)
|
||||
(char=? c #\:) (char<=? #\< c #\?)
|
||||
(char=? c #\^) (char=? c #\_) (char=? c #\~)))
|
||||
|
||||
(define (id-char? c)
|
||||
(or (initial-id-char? c) (char<=? #\0 c #\9)
|
||||
(char=? c #\-) (char=? c #\+) (char=? c #\.)))
|
||||
|
||||
(define-record-type string-buffer (nongenerative)
|
||||
(fields (mutable n) (mutable str))
|
||||
(protocol (lambda (new) (lambda () (new 0 (make-string 16))))))
|
||||
(define (get-buffer tb n required-capacity)
|
||||
(let* ([str (string-buffer-str tb)] [len (string-length str)])
|
||||
(if (fx< (fx- len n) required-capacity)
|
||||
(let ([new-str (make-string (fx* 2 (fx+ len required-capacity)))])
|
||||
(string-copy! str 0 new-str 0 n)
|
||||
(string-buffer-str-set! tb new-str)
|
||||
new-str)
|
||||
str)))
|
||||
(define (extend-string-buffer! tb c)
|
||||
(let ([n (string-buffer-n tb)])
|
||||
(string-set! (get-buffer tb n 1) n c)
|
||||
(string-buffer-n-set! tb (fx+ n 1))))
|
||||
(define (append-to-string-buffer! tb str)
|
||||
(let ([n (string-buffer-n tb)] [len (string-length str)])
|
||||
(string-copy! (get-buffer tb n len) n str 0 len)
|
||||
(string-buffer-n-set! tb (fx+ n len))))
|
||||
(define (extract-string-and-reset! tb)
|
||||
(let ([str (substring (string-buffer-str tb) 0 (string-buffer-n tb))])
|
||||
(string-buffer-n-set! tb 0)
|
||||
str))
|
||||
(define (open-positional-string-output-port)
|
||||
(define-record-type position (nongenerative)
|
||||
(fields (mutable line) (mutable column))
|
||||
(protocol (lambda (new) (lambda () (new 1 1)))))
|
||||
(let ([tb (make-string-buffer)] [pos (make-position)])
|
||||
(define (w! str start cnt)
|
||||
(let* ([n (string-buffer-n tb)]
|
||||
[buf (get-buffer tb n cnt)]
|
||||
[end (fx+ start cnt)])
|
||||
(let loop! ([i start] [n n] [line (position-line pos)] [column (position-column pos)])
|
||||
(if (fx= i end)
|
||||
(begin
|
||||
(position-line-set! pos line)
|
||||
(position-column-set! pos column)
|
||||
(string-buffer-n-set! tb n))
|
||||
(let ([c (string-ref str i)])
|
||||
(string-set! buf n c)
|
||||
(if (char=? c #\newline)
|
||||
(loop! (fx+ i 1) (fx+ n 1) (fx+ line 1) 1)
|
||||
(loop! (fx+ i 1) (fx+ n 1) line (fx+ column 1)))))))
|
||||
cnt)
|
||||
(define (gp) (string-buffer-n tb))
|
||||
(let ([op (make-custom-textual-output-port "positional-string-output-port" w! gp #f #f)])
|
||||
(define (line) (flush-output-port op) (position-line pos))
|
||||
(define (column) (flush-output-port op) (position-column pos))
|
||||
(define (p) (flush-output-port op) (extract-string-and-reset! tb))
|
||||
(values op p line column))))
|
||||
|
||||
;; scan forward for blanks, and if it leads you to a new-line, strip
|
||||
;; the previous blanks back to the new line.
|
||||
(define (finish-strip ip stack bfp start-bfp)
|
||||
(let ([node-to-strip (car stack)])
|
||||
(if (string? node-to-strip)
|
||||
(let loop ([i (string-length node-to-strip)])
|
||||
(if (fx= i 0)
|
||||
(values (cdr stack) bfp)
|
||||
(let ([i (fx- i 1)])
|
||||
(let ([c (string-ref node-to-strip i)])
|
||||
(cond
|
||||
[(char=? c #\newline)
|
||||
(values (cons (substring node-to-strip 0 (fx+ i 1)) (cdr stack)) bfp)]
|
||||
[(char-whitespace? c) (loop i)]
|
||||
[else (set-port-position! ip start-bfp) (values stack start-bfp)])))))
|
||||
(begin (set-port-position! ip start-bfp) (values stack start-bfp)))))
|
||||
(define (strip-blanks ip stack start-bfp)
|
||||
(let gather-blanks ([bfp start-bfp])
|
||||
(let ([c (read-char ip)])
|
||||
(cond
|
||||
[(eof-object? c) (finish-strip ip stack bfp start-bfp)]
|
||||
[(char=? c #\newline) (finish-strip ip stack (fx+ bfp 1) start-bfp)]
|
||||
[(char-whitespace? c) (gather-blanks (fx+ bfp 1))]
|
||||
[else (set-port-position! ip start-bfp) (values stack start-bfp)])))))
|
||||
|
||||
(library (template)
|
||||
(export include-template define-template-extension optional @if @elif @else @endif @for @endfor @num)
|
||||
(import (chezscheme) (template-helpers))
|
||||
|
||||
(define-syntax optional (lambda (x) (syntax-violation #f "misplaced aux keyword" x)))
|
||||
|
||||
(define check-string-and-indent
|
||||
(lambda (s at indent)
|
||||
(unless (string? s)
|
||||
(errorf 'include-template "unexpected non-string value ~s of expression ~s" s at))
|
||||
(if (= indent 0)
|
||||
s
|
||||
(let ([ip (open-string-input-port s)])
|
||||
(let ([first-line (get-line ip)])
|
||||
(if (eof-object? first-line)
|
||||
s
|
||||
(let-values ([(op p) (open-string-output-port)])
|
||||
(display first-line op)
|
||||
(let ([indent (make-string indent #\space)])
|
||||
(let loop ()
|
||||
(let ([line (get-line ip)])
|
||||
(if (eof-object? line)
|
||||
(begin
|
||||
(when (char=? (string-ref s (fx- (string-length s) 1)) #\newline) (newline op))
|
||||
(p))
|
||||
(begin
|
||||
(newline op)
|
||||
(display indent op)
|
||||
(display line op)
|
||||
(loop)))))))))))))
|
||||
|
||||
(define-syntax include-template
|
||||
(lambda (x)
|
||||
(define (process-template-file r fn k)
|
||||
(let* ([bip (open-file-input-port fn)]
|
||||
[sfd (make-source-file-descriptor fn bip #t)]
|
||||
[ip (transcoded-port bip (native-transcoder))]
|
||||
[tb (make-string-buffer)])
|
||||
(define (s0 a bfp)
|
||||
(let ([c (read-char ip)])
|
||||
(cond
|
||||
[(eof-object? c)
|
||||
(close-input-port ip)
|
||||
(reverse (cons (extract-string-and-reset! tb) a))]
|
||||
[(char=? c #\@) (s1 a (+ bfp 1))]
|
||||
[else (extend-string-buffer! tb c) (s0 a (+ bfp 1))])))
|
||||
(define (s1 a bfp)
|
||||
(let ([c (read-char ip)])
|
||||
(cond
|
||||
[(eof-object? c) (source-error sfd bfp "expected open paren or @ following @")]
|
||||
[(eqv? c #\@) (extend-string-buffer! tb c) (s0 a (+ bfp 1))]
|
||||
[(eqv? c #\()
|
||||
(unread-char c ip)
|
||||
(let-values ([(e* new-bfp) (read-scheme k ip sfd bfp)])
|
||||
(syntax-case e* ()
|
||||
[(e)
|
||||
(s0
|
||||
(cons*
|
||||
#`(check-string-and-indent e #,(source-string sfd bfp) (fx- (column) 1))
|
||||
(extract-string-and-reset! tb)
|
||||
a)
|
||||
new-bfp)]
|
||||
[else (source-error sfd bfp "found more than one expression within @(---)")]))]
|
||||
[(initial-id-char? c)
|
||||
(let ([str (extract-string-and-reset! tb)])
|
||||
(extend-string-buffer! tb #\@)
|
||||
(extend-string-buffer! tb c)
|
||||
(s2 (cons str a) (+ bfp 1) bfp))]
|
||||
[else (source-error sfd bfp "expected open paren or @ following @")])))
|
||||
(define (s2 a bfp token-start-bfp)
|
||||
(let ([c (read-char ip)])
|
||||
(cond
|
||||
[(eof-object? c) (close-input-port ip) (finish-identifier a bfp token-start-bfp)]
|
||||
[(id-char? c) (extend-string-buffer! tb c) (s2 a (+ bfp 1) token-start-bfp)]
|
||||
[else (unread-char c ip) (finish-identifier a bfp token-start-bfp)])))
|
||||
(define (finish-identifier a bfp token-bfp)
|
||||
(let* ([token (extract-string-and-reset! tb)]
|
||||
[@kw (datum->syntax k (string->symbol token))]
|
||||
[p (r @kw)])
|
||||
(unless p (source-error sfd token-bfp "unrecognized token ~a" token))
|
||||
(call-with-values (lambda () (p k ip sfd a bfp token-bfp)) s0)))
|
||||
(s0 '() 0)))
|
||||
(syntax-case x ()
|
||||
[(k fn)
|
||||
(string? (datum fn))
|
||||
(lambda (r)
|
||||
(with-syntax ([(e ...) (process-template-file r (datum fn) #'k)])
|
||||
#'(let ([filename fn])
|
||||
(let-values ([(op p line column) (open-positional-string-output-port)])
|
||||
(display e op) ...
|
||||
(p)))))])))
|
||||
|
||||
(define-syntax define-template-extension
|
||||
(lambda (x)
|
||||
(define who 'define-template-extension)
|
||||
(define (make-prefix-id prefix kw)
|
||||
(datum->syntax kw
|
||||
(string->symbol
|
||||
(string-append prefix (symbol->string (syntax->datum kw))))))
|
||||
(define build-matcher
|
||||
(case-lambda
|
||||
[(kw)
|
||||
(with-syntax ([kw kw] [@kw (make-prefix-id "@" kw)])
|
||||
#'[@kw (lambda (k ip sfd stack bfp token-bfp)
|
||||
(let-values ([(stack bfp) (strip-blanks ip stack bfp)])
|
||||
(values (cons (make-incomplete-node 'kw #f token-bfp) stack) bfp)))])]
|
||||
[(kw expr)
|
||||
(with-syntax ([kw kw] [@kw (make-prefix-id "@" kw)] [(expr ...) expr])
|
||||
#'[@kw (lambda (k ip sfd stack bfp token-bfp)
|
||||
(let-values ([(e* new-bfp) (read-scheme k ip sfd bfp)])
|
||||
(syntax-case e* ()
|
||||
[(expr ...)
|
||||
(let-values ([(stack new-bfp) (strip-blanks ip stack new-bfp)])
|
||||
(values (cons (make-incomplete-node 'kw e* token-bfp) stack) new-bfp))]
|
||||
[_ (source-error sfd token-bfp "expected @~s~s syntax, but got @~s~s"
|
||||
'kw '(expr ...) 'kw (syntax->datum e*))])))])]))
|
||||
(define (check-id id)
|
||||
(let* ([str (symbol->string (syntax->datum id))]
|
||||
[len (string-length str)])
|
||||
(unless (and (> len 0) (initial-id-char? (string-ref str 0))
|
||||
(let loop ([len len])
|
||||
(or (= len 0)
|
||||
(let ([len (- len 1)])
|
||||
(and (id-char? (string-ref str len)) (loop len))))))
|
||||
(syntax-violation who "invalid template keyword" id))))
|
||||
(define (check-unique! type ids)
|
||||
(let loop ([ids ids])
|
||||
(syntax-case ids ()
|
||||
[(id rest ...)
|
||||
(if (memq (datum id) (datum (rest ...)))
|
||||
(syntax-violation who (format "one or more ~a used more than once" type) #'id #'(rest ...))
|
||||
(loop #'(rest ...)))]
|
||||
[() (void)])))
|
||||
(define (check-syntax-unique! type maybe-expr*)
|
||||
(check-unique! type
|
||||
(let f ([stx maybe-expr*] [ids '()])
|
||||
(syntax-case stx ()
|
||||
[id (and (identifier? #'id) (not (memq (datum id) '(... unquote quote)))) (cons #'id ids)]
|
||||
[(a . d) (f #'a (f #'d ids))]
|
||||
[_ ids]))))
|
||||
(define (build-check kw tmpl x)
|
||||
#`(unless #,(if x #`(and #,x #,tmpl) tmpl)
|
||||
(source-error sfd token-bfp "found ~s without required ~s" token '#,kw)))
|
||||
(define (build-initial-values bindings list?*)
|
||||
(fold-right (lambda (binding list? init-val**)
|
||||
(cons
|
||||
(if list?
|
||||
(make-list (length binding) #''())
|
||||
(make-list (length binding) #'#f))
|
||||
init-val**))
|
||||
'() bindings list?*))
|
||||
(define (build-bodies list?* tmpls updates bindings)
|
||||
(let f ([list?* list?*] [tmpls tmpls] [updates updates] [bindings bindings] [rbindings '()])
|
||||
(if (null? list?*)
|
||||
'()
|
||||
(with-syntax ([(checks ...)
|
||||
(if (car list?*)
|
||||
'()
|
||||
#`((when #,(car tmpls)
|
||||
(source-error token-bfp "found more @~s than expected" type))))]
|
||||
[((args ...) ...) (fold-left (lambda (args binding) (cons binding args))
|
||||
(cons (car updates) (cdr bindings)) rbindings)])
|
||||
(cons #'(begin checks ... (loop (cdr stack) '() args ... ...))
|
||||
(f (cdr list?*) (cdr tmpls) (cdr updates) (cdr bindings) (cons (car bindings) rbindings)))))))
|
||||
(define (process-template output pat)
|
||||
(define (squawk type)
|
||||
(syntax-violation who (format "extension cannot start with ~s keyword" type) pat))
|
||||
(syntax-case pat (optional)
|
||||
[((optional kw (expr ...) tmpl) . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(squawk 'optional)]
|
||||
[((optional kw tmpl) . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(squawk 'optional)]
|
||||
[((kw (expr ...) tmpl) dots . rest)
|
||||
(and (eq? (datum dots) '...) (identifier? #'kw) (identifier? #'tmpl))
|
||||
(squawk 'list)]
|
||||
[((kw tmpl) dots . rest)
|
||||
(and (eq? (datum dots) '...) (identifier? #'kw) (identifier? #'tmpl))
|
||||
(squawk 'optional)]
|
||||
[(kw (expr ...) tmpl . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(process-rest output #'kw #'rest
|
||||
(list (build-matcher #'kw #'(expr ...)))
|
||||
#'([tmpl #`(string-append #,@rstack)]
|
||||
[(expr ...) (incomplete-node-e* item)]))]
|
||||
[(kw tmpl . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(process-rest output #'kw #'rest (list (build-matcher #'kw))
|
||||
#'([tmpl #`(string-append #,@rstack)]))]
|
||||
[(kw (expr ...))
|
||||
(with-syntax ([@kw (make-prefix-id "@" #'kw)] [output output])
|
||||
#'([@kw (lambda (k ip sfd stack bfp token-bfp)
|
||||
(let-values ([(e* new-bfp) (read-scheme k ip sfd bfp)])
|
||||
(syntax-case e* ()
|
||||
[(expr ...) (values (cons #`output stack) new-bfp)]
|
||||
[_ (source-error sfd token-bfp "expected @~s~s syntax, but got @~s~s"
|
||||
'kw '(expr ...) 'kw (syntax->datum e*))])))]))]
|
||||
[(kw)
|
||||
(with-syntax ([@kw (make-prefix-id "@" #'kw)] [output output])
|
||||
#'([@kw (lambda (k ip sfd stack bfp indent token-bfp)
|
||||
(values (cons #`output stack) bfp indent))]))]))
|
||||
(define (process-rest output first-kw rest as* matches)
|
||||
(let f ([pat rest]
|
||||
[as* as*]
|
||||
[checks '()]
|
||||
[kws '()]
|
||||
[tmpls '()]
|
||||
[list?* '()]
|
||||
[bindings '()]
|
||||
[updates '()]
|
||||
[exprs '()]
|
||||
[matches matches])
|
||||
(syntax-case pat (optional)
|
||||
[((optional kw (expr ...) tmpl) . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(with-syntax ([(t) (generate-temporaries '(t))])
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw #'(expr ...)) as*) checks
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #f list?*)
|
||||
(cons (list #'tmpl #'t) bindings)
|
||||
(cons (list #'#`(string-append #,@rstack) #'(incomplete-node-e* item)) updates)
|
||||
(cons #'(expr ...) exprs) (cons* #'[tmpl tmpl] #'[(expr ...) #'t] matches)))]
|
||||
[((optional kw tmpl) . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw) as*) checks
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #f list?*) (cons (list #'tmpl) bindings)
|
||||
(cons (list #'#`(string-append #,@rstack)) updates)
|
||||
(cons #f exprs) (cons #'[tmpl tmpl] matches))]
|
||||
[((kw (expr ...) tmpl) dots . rest)
|
||||
(and (eq? (datum dots) '...) (identifier? #'kw) (identifier? #'tmpl))
|
||||
(with-syntax ([(t*) (generate-temporaries '(t*))])
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw #'(expr ...)) as*) checks
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #t list?*) (cons (list #'tmpl #'t*) bindings)
|
||||
(cons (list #'(cons #`(string-append #,@rstack) tmpl) #'(cons (incomplete-node-e* item) t*)) updates)
|
||||
(cons #'(expr ...) exprs) (cons* #'[(tmpl (... ...)) tmpl] #'[((expr ...) (... ...)) t*] matches)))]
|
||||
[((kw tmpl) dots . rest)
|
||||
(and (eq? (datum dots) '...) (identifier? #'kw) (identifier? #'tmpl))
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw) as*) checks
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #t list?*) (cons (list #'tmpl) bindings)
|
||||
(cons (list #'(cons #`(string-append #,@rstack) tmpl)) updates)
|
||||
(cons #f exprs) (cons* #'[(tmpl (... ...)) tmpl] matches))]
|
||||
[(kw (expr ...) tmpl . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(with-syntax ([(t) (generate-temporaries '(t))])
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw #'(expr ...)) as*)
|
||||
(cons (build-check #'kw #'tmpl #'t) checks)
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #f list?*) (cons (list #'tmpl #'t) bindings)
|
||||
(cons (list #'#`(string-append #,@rstack) #'(incomplete-node-e* item)) updates)
|
||||
(cons #'(expr ...) exprs) (cons* #'[tmpl tmpl] #'[(expr ...) #'t] matches)))]
|
||||
[(kw tmpl . rest)
|
||||
(and (identifier? #'kw) (identifier? #'tmpl))
|
||||
(f #'rest
|
||||
(cons (build-matcher #'kw) as*)
|
||||
(cons (build-check #'kw #'tmpl #f) checks)
|
||||
(cons #'kw kws) (cons #'tmpl tmpls) (cons #f list?*) (cons (list #'tmpl) bindings)
|
||||
(cons (list #'#`(string-append #,@rstack)) updates)
|
||||
(cons #f exprs) (cons #'[tmpl tmpl] matches))]
|
||||
[()
|
||||
(begin
|
||||
(for-each check-id kws)
|
||||
(check-unique! "keyword" kws)
|
||||
(check-unique! "template bindings" tmpls)
|
||||
(check-syntax-unique! "scheme syntax matching expressions" exprs)
|
||||
(cons
|
||||
(with-syntax ([startkw first-kw]
|
||||
[endkw (make-prefix-id "end" first-kw)]
|
||||
[@endkw (make-prefix-id "@end" first-kw)]
|
||||
[output output]
|
||||
[(matches ...) matches]
|
||||
[(checks ...) checks]
|
||||
[((x ...) ...) bindings]
|
||||
[((init-val ...) ...) (build-initial-values bindings list?*)]
|
||||
[(kw ...) kws]
|
||||
[(body ...) (build-bodies list?* tmpls updates bindings)])
|
||||
#'[@endkw (lambda (k ip sfd stack bfp token-bfp)
|
||||
(let-values ([(stack bfp) (strip-blanks ip stack bfp)])
|
||||
(let loop ([stack stack] [rstack '()] [x init-val] ... ...)
|
||||
(if (null? stack)
|
||||
(source-error sfd token-bfp "found @~s with no initial @~s" 'endkw 'startkw)
|
||||
(let ([item (car stack)])
|
||||
(if (incomplete-node? item)
|
||||
(let ([type (incomplete-node-type item)])
|
||||
(case type
|
||||
[(startkw) checks ...
|
||||
(with-syntax (matches ...)
|
||||
(values (cons #`output (cdr stack)) bfp))]
|
||||
[(kw) body] ...
|
||||
[else (source-error sfd token-bfp
|
||||
"found unexpected @~s (~a) instead of expected @~s before @~s"
|
||||
type (source-string sfd (incomplete-node-bfp item)) 'startkw 'endkw)]))
|
||||
(loop (cdr stack) (cons item rstack) x ... ...)))))))])
|
||||
as*))]
|
||||
[_ (syntax-violation who "unrecognized pattern" pat)])))
|
||||
(syntax-case x ()
|
||||
[(_ pat output)
|
||||
(with-syntax ([([@kw proc] ...) (process-template #'output #'pat)])
|
||||
#'(begin (define-syntax @kw (make-compile-time-value proc)) ...))])))
|
||||
|
||||
(define-template-extension (num (e)) (number->string e))
|
||||
|
||||
(define-template-extension (for ([binding e] [bindings es] ...) tmpl)
|
||||
(with-output-to-string
|
||||
(lambda ()
|
||||
(for-each (lambda (binding bindings ...) (display tmpl)) e es ...))))
|
||||
|
||||
(define-template-extension (if (expr) tmpl (elif (exprs) tmpls) ... (optional else else-tmpl))
|
||||
(if expr
|
||||
tmpl
|
||||
#,(let f ([exprs #'(exprs ...)] [tmpls #'(tmpls ...)])
|
||||
(if (null? exprs)
|
||||
(or #'else-tmpl #'"")
|
||||
(with-syntax ([expr (car exprs)] [tmpl (car tmpls)] [else (f (cdr exprs) (cdr tmpls))])
|
||||
#'(if expr
|
||||
tmpl
|
||||
else)))))))
|
||||
#!eof
|
||||
-------- saving remainder of file to /tmp/t and running /tmp/t should produce only "end of tests" --------
|
||||
#!/bin/tcsh
|
||||
|
||||
cat >! /tmp/spam.h << END
|
||||
extern void @(name)(void);
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
@((include-template "/tmp/spam.h"))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
}
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(let ([name "bob"]) (include-template "/tmp/spam.c"))
|
||||
"#include <stdio.h>\n\nextern void bob(void);\n\n\nvoid bob() {\n bob();\n}\n")
|
||||
(error #f "test 1 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(guard (c [else (with-output-to-string (lambda () (display-condition c)))])
|
||||
(expand '(let ([name "bob"]) (include-template "/tmp/spam.c"))))
|
||||
"Exception in get-datum/annotations: unexpected end-of-file reading list at line 6, char 4 of /tmp/spam.c")
|
||||
(error #f "test 2 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
@((include-template "/tmp/spam.h"))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
@
|
||||
}
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(guard (c [else (with-output-to-string (lambda () (display-condition c)))])
|
||||
(expand '(let ([name "bob"]) (include-template "/tmp/spam.c"))))
|
||||
"Exception in include-template: expected open paren or @ following @ at line 7, char 4 of /tmp/spam.c")
|
||||
(error #f "test 3 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
@((include-template "/tmp/spam.h"))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
}
|
||||
END
|
||||
echo -n "@" >> /tmp/spam.c
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(guard (c [else (with-output-to-string (lambda () (display-condition c)))])
|
||||
(expand '(let ([name "bob"]) (include-template "/tmp/spam.c"))))
|
||||
"Exception in include-template: expected open paren or @ following @ at line 8, char 2 of /tmp/spam.c")
|
||||
(error #f "test 4 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
@((include-template #xGO! "/tmp/spam.h"))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
}
|
||||
END
|
||||
echo -n "@" >> /tmp/spam.c
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(guard (c [else (with-output-to-string (lambda () (display-condition c)))])
|
||||
(expand '(let ([name "bob"]) (include-template "/tmp/spam.c"))))
|
||||
"Exception in get-datum/annotations: invalid number syntax #xGO! at line 3, char 21 of /tmp/spam.c")
|
||||
(error #f "test 5 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
@((include-template))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
}
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal?
|
||||
(guard (c [else (with-output-to-string (lambda () (display-condition c)))])
|
||||
(expand '(let ([name "bob"]) (include-template "/tmp/spam.c"))))
|
||||
"Exception: invalid syntax (include-template) at line 3, char 3 of /tmp/spam.c")
|
||||
(error #f "test 6 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/spam.c <<END
|
||||
#include <stdio.h>
|
||||
|
||||
/* function: @(name)
|
||||
*
|
||||
* @@param: @(name)
|
||||
*/
|
||||
@((include-template "/tmp/spam.h"))
|
||||
|
||||
void @(name)() {
|
||||
@(name)();
|
||||
}
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([name "bob"]) (include-template "/tmp/spam.c"))
|
||||
"#include <stdio.h>\n\n/* function: bob\n *\n * @param: bob\n */\nextern void bob(void);\n\n\nvoid bob() {\n bob();\n}\n")
|
||||
(error #f "test 7 failed"))
|
||||
END
|
||||
|
||||
cat >! /tmp/rockets <<END
|
||||
Test
|
||||
@for([x xs])
|
||||
@num(x) ...
|
||||
@endfor
|
||||
Blast off!
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([xs '(3 2 1)]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\n")
|
||||
(error #f "test 8 failed"))
|
||||
END
|
||||
cat >> /tmp/rockets <<END
|
||||
@if((= y 0))
|
||||
Again!
|
||||
@elif((= y 1))
|
||||
Rockets are awesome!
|
||||
@elif((= y 2))
|
||||
Explosions!
|
||||
@else
|
||||
That was fun.
|
||||
@endif
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 0]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nAgain!\n")
|
||||
(error #f "test 9 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 1]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nRockets are awesome!\n")
|
||||
(error #f "test 10 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 2]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nExplosions!\n")
|
||||
(error #f "test 11 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 3]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nThat was fun.\n")
|
||||
(error #f "test 12 failed"))
|
||||
END
|
||||
cat >> /tmp/rockets <<END
|
||||
@join(" and " [a as])@(a)@endjoin
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(define-template-extension (join (?str [binding ls-expr] [bindings ls-exprs] ...) tmpl)
|
||||
#,(with-syntax ([(t ts ...) (generate-temporaries #'(binding bindings ...))])
|
||||
#'(with-output-to-string
|
||||
(lambda ()
|
||||
(let ([str ?str] [t ls-expr] [ts ls-exprs] ...)
|
||||
(if (null? t)
|
||||
""
|
||||
(let loop ([t t] [ts ts] ...)
|
||||
(let ([binding (car t)] [bindings (car ts)] ... [t (cdr t)] [ts (cdr ts)] ...)
|
||||
(display tmpl)
|
||||
(unless (null? t)
|
||||
(display str)
|
||||
(loop t ts ...))))))))))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 3] [as '()]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nThat was fun.\n")
|
||||
(error #f "test 13 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(define-template-extension (join (?str [binding ls-expr] [bindings ls-exprs] ...) tmpl)
|
||||
#,(with-syntax ([(t ts ...) (generate-temporaries #'(binding bindings ...))])
|
||||
#'(with-output-to-string
|
||||
(lambda ()
|
||||
(let ([str ?str] [t ls-expr] [ts ls-exprs] ...)
|
||||
(if (null? t)
|
||||
""
|
||||
(let loop ([t t] [ts ts] ...)
|
||||
(let ([binding (car t)] [bindings (car ts)] ... [t (cdr t)] [ts (cdr ts)] ...)
|
||||
(display tmpl)
|
||||
(unless (null? t)
|
||||
(display str)
|
||||
(loop t ts ...))))))))))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 3] [as '("a")]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nThat was fun.\na")
|
||||
(error #f "test 14 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(define-template-extension (join (?str [binding ls-expr] [bindings ls-exprs] ...) tmpl)
|
||||
#,(with-syntax ([(t ts ...) (generate-temporaries #'(binding bindings ...))])
|
||||
#'(with-output-to-string
|
||||
(lambda ()
|
||||
(let ([str ?str] [t ls-expr] [ts ls-exprs] ...)
|
||||
(if (null? t)
|
||||
""
|
||||
(let loop ([t t] [ts ts] ...)
|
||||
(let ([binding (car t)] [bindings (car ts)] ... [t (cdr t)] [ts (cdr ts)] ...)
|
||||
(display tmpl)
|
||||
(unless (null? t)
|
||||
(display str)
|
||||
(loop t ts ...))))))))))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 3] [as '("a" "b")]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nThat was fun.\na and b")
|
||||
(error #f "test 15 failed"))
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(define-template-extension (join (?str [binding ls-expr] [bindings ls-exprs] ...) tmpl)
|
||||
#,(with-syntax ([(t ts ...) (generate-temporaries #'(binding bindings ...))])
|
||||
#'(with-output-to-string
|
||||
(lambda ()
|
||||
(let ([str ?str] [t ls-expr] [ts ls-exprs] ...)
|
||||
(if (null? t)
|
||||
""
|
||||
(let loop ([t t] [ts ts] ...)
|
||||
(let ([binding (car t)] [bindings (car ts)] ... [t (cdr t)] [ts (cdr ts)] ...)
|
||||
(display tmpl)
|
||||
(unless (null? t)
|
||||
(display str)
|
||||
(loop t ts ...))))))))))
|
||||
(unless (equal? (let ([xs '(3 2 1)] [y 3] [as '("a" "b" "c")]) (include-template "/tmp/rockets"))
|
||||
"Test\n3 ...\n2 ...\n1 ...\nBlast off!\nThat was fun.\na and b and c")
|
||||
(error #f "test 16 failed"))
|
||||
END
|
||||
cat >! /tmp/indent-test.c << END
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@(body)
|
||||
}
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([body "printf(\"Hello, world!\\\\n\");\nprintf(\"So... uh, what's going on?\\\\n\");\nprintf(\"Well, goodbye then.\\\\n\");"])
|
||||
(include-template "/tmp/indent-test.c"))
|
||||
"#include <stdio.h>\n\nint main(int argc, char *argv[]) {\n printf(\"Hello, world!\\\\n\");\n printf(\"So... uh, what's going on?\\\\n\");\n printf(\"Well, goodbye then.\\\\n\");\n}\n")
|
||||
(error #f "test 17 failed"))
|
||||
END
|
||||
cat >! /tmp/hygeine << END
|
||||
Hygiene test:
|
||||
|
||||
@for([t i*])
|
||||
got @(t) .
|
||||
@endfor
|
||||
|
||||
@for([x j*])
|
||||
got @(x) and t is @(t).
|
||||
@endfor
|
||||
|
||||
@let([outer-t t])
|
||||
@for([x i*] [t j*])
|
||||
got @(x) and @(t) and @(outer-t).
|
||||
@endfor
|
||||
@endlet
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(let ()
|
||||
(define-template-extension (for ([x e] [xs es] ...) tmpl)
|
||||
(let ([t e])
|
||||
(apply string-append (map (lambda (x xs ...) tmpl) t es ...))))
|
||||
(define-template-extension (let ([x e] [xs es] ...) tmpl)
|
||||
(let ([x e] [xs es] ...) tmpl))
|
||||
(unless (equal? (let ([t "10"] [i* '("1" "2" "3")] [j* '("a" "b" "c")])
|
||||
(include-template "/tmp/hygeine"))
|
||||
"Hygiene test:\n\n got 1 .\n got 2 .\n got 3 .\n\n got a and t is 10.\n got b and t is 10.\n got c and t is 10.\n\n got 1 and a and 10.\n got 2 and b and 10.\n got 3 and c and 10.\n")
|
||||
(error #f "test 18 failed")))
|
||||
END
|
||||
cat >! /tmp/indent << END
|
||||
This is to test indents:
|
||||
simple indent: @(x)
|
||||
|
||||
nested indents: @(y) @(x)
|
||||
END
|
||||
scheme -q << END
|
||||
(import (template))
|
||||
(unless (equal? (let ([x "a\nb\nc\n"] [y "x\ny\nz"])
|
||||
(include-template "/tmp/indent"))
|
||||
"This is to test indents:\n simple indent: a\n b\n c\n\n\n nested indents: x\n y\n z a\n b\n c\n\n")
|
||||
(error #f "test 19 failed"))
|
||||
END
|
||||
echo "end of tests"
|
||||
|
91
ta6ob/examples/unify.ss
Normal file
91
ta6ob/examples/unify.ss
Normal file
|
@ -0,0 +1,91 @@
|
|||
;;; unify.ss
|
||||
;;; Copyright (C) 1996 R. Kent Dybvig
|
||||
;;; from "The Scheme Programming Language, 2ed" by R. Kent Dybvig
|
||||
|
||||
;;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||
;;; copy of this software and associated documentation files (the "Software"),
|
||||
;;; to deal in the Software without restriction, including without limitation
|
||||
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
;;; and/or sell copies of the Software, and to permit persons to whom the
|
||||
;;; Software is furnished to do so, subject to the following conditions:
|
||||
;;;
|
||||
;;; The above copyright notice and this permission notice shall be included in
|
||||
;;; all copies or substantial portions of the Software.
|
||||
;;;
|
||||
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(define unify #f)
|
||||
(let ()
|
||||
;; occurs? returns true if and only if u occurs in v
|
||||
(define occurs?
|
||||
(lambda (u v)
|
||||
(and (pair? v)
|
||||
(let f ((l (cdr v)))
|
||||
(and (pair? l)
|
||||
(or (eq? u (car l))
|
||||
(occurs? u (car l))
|
||||
(f (cdr l))))))))
|
||||
|
||||
;; sigma returns a new substitution procedure extending s by
|
||||
;; the substitution of u with v
|
||||
(define sigma
|
||||
(lambda (u v s)
|
||||
(lambda (x)
|
||||
(let f ((x (s x)))
|
||||
(if (symbol? x)
|
||||
(if (eq? x u) v x)
|
||||
(cons (car x) (map f (cdr x))))))))
|
||||
|
||||
;; try-subst tries to substitute u for v but may require a
|
||||
;; full unification if (s u) is not a variable, and it may
|
||||
;; fail if it sees that u occurs in v.
|
||||
(define try-subst
|
||||
(lambda (u v s ks kf)
|
||||
(let ((u (s u)))
|
||||
(if (not (symbol? u))
|
||||
(uni u v s ks kf)
|
||||
(let ((v (s v)))
|
||||
(cond
|
||||
((eq? u v) (ks s))
|
||||
((occurs? u v) (kf "cycle"))
|
||||
(else (ks (sigma u v s)))))))))
|
||||
|
||||
;; uni attempts to unify u and v with a continuation-passing
|
||||
;; style that returns a substitution to the success argument
|
||||
;; ks or an error message to the failure argument kf. The
|
||||
;; substitution itself is represented by a procedure from
|
||||
;; variables to terms.
|
||||
(define uni
|
||||
(lambda (u v s ks kf)
|
||||
(cond
|
||||
((symbol? u) (try-subst u v s ks kf))
|
||||
((symbol? v) (try-subst v u s ks kf))
|
||||
((and (eq? (car u) (car v))
|
||||
(= (length u) (length v)))
|
||||
(let f ((u (cdr u)) (v (cdr v)) (s s))
|
||||
(if (null? u)
|
||||
(ks s)
|
||||
(uni (car u)
|
||||
(car v)
|
||||
s
|
||||
(lambda (s) (f (cdr u) (cdr v) s))
|
||||
kf))))
|
||||
(else (kf "clash")))))
|
||||
|
||||
;; unify shows one possible interface to uni, where the initial
|
||||
;; substitution is the identity procedure, the initial success
|
||||
;; continuation returns the unified term, and the initial failure
|
||||
;; continuation returns the error message.
|
||||
(set! unify
|
||||
(lambda (u v)
|
||||
(uni u
|
||||
v
|
||||
(lambda (x) x)
|
||||
(lambda (s) (s u))
|
||||
(lambda (msg) msg)))))
|
Reference in a new issue