(import (scheme base) (scheme write) (scheme case-lambda))

;(call-with-values (lambda () (values 1 2)) (lambda (x y) (write `(,x ,y))))
;(define-syntax letrec*
;  (syntax-rules ()
;    ((letrec* ((var val) ...) . body)
;     (let () (define var val) ... . body))))
;
;(define-syntax guard
;  (syntax-rules ()
;    ((guard (var clause ...) e1 e2 ...)
;     ((call-with-current-continuation
;       (lambda (guard-k)
;         (with-exception-handler
;          (lambda (condition)
;            ((call-with-current-continuation
;              (lambda (handler-k)
;                (guard-k
;                 (lambda ()
;                   (let ((var condition))      ; clauses may SET! var
;                     (guard-aux (handler-k (lambda ()
;                                             (raise-continuable condition)))
;                                clause ...))))))))
;          (lambda ()
;            (let ((res (begin e1 e2 ...)))
;              (guard-k (lambda () res)))))))))))
;
;(define-syntax guard-aux
;  (syntax-rules (else =>)
;    ((guard-aux reraise (else result1 result2 ...))
;     (begin result1 result2 ...))
;    ((guard-aux reraise (test => result))
;     (let ((temp test))
;       (if temp (result temp) reraise)))
;    ((guard-aux reraise (test => result) clause1 clause2 ...)
;     (let ((temp test))
;       (if temp (result temp) (guard-aux reraise clause1 clause2 ...))))
;    ((guard-aux reraise (test))
;     (or test reraise))
;    ((guard-aux reraise (test) clause1 clause2 ...)
;     (or test (guard-aux reraise clause1 clause2 ...)))
;    ((guard-aux reraise (test result1 result2 ...))
;     (if test (begin result1 result2 ...) reraise))
;    ((guard-aux reraise (test result1 result2 ...) clause1 clause2 ...)
;     (if test
;         (begin result1 result2 ...)
;         (guard-aux reraise clause1 clause2 ...)))))
;
;   (define-syntax %case
;     (syntax-rules ()
;       ((%case args len n p ((params ...) . body) . rest)
;        (if (= len (length '(params ...)))
;            (apply (lambda (params ...) . body) args)
;            (%case args len 0 () . rest)))
;       ((%case args len n (p ...) ((x . y) . body) . rest)
;        (%case args len (+ n 1) (p ... x) (y . body) . rest))
;       ((%case args len n (p ...) (y . body) . rest)
;        (if (>= len n)
;            (apply (lambda (p ... . y) . body) args)
;            (%case args len 0 () . rest)))
;       ((%case args len n p)
;        (error "case-lambda: no cases matched"))))
;   (define-syntax case-lambda
;     (syntax-rules ()
;       ((case-lambda . clauses)
;        (lambda args (let ((len (length args))) (%case args len 0 () . clauses))))))
;        ;(lambda args (let ((len (length* args))) (%case args len 0 () . clauses))))))
;
;(define-syntax define-values
;  (syntax-rules ()
;    ((define-values () expr)
;     (define dummy
;       (call-with-values (lambda () expr)
;         (lambda args #f))))
;    ((define-values (var) expr)
;     (define var expr))
;    ((define-values (var0 var1 ... varn) expr)
;     (begin
;       (define var0
;         (call-with-values (lambda () expr) list))
;       (define var1
;         (let ((v (cadr var0)))
;           (set-cdr! var0 (cddr var0))
;           v))
;       ...
;       (define varn
;         (let ((v (cadr var0)))
;           (set! var0 (car var0))
;           v))))
;    ((define-values (var0 var1 ... . var-dot) expr)
;     (begin
;       (define var0
;         (call-with-values (lambda () expr) list))
;       (define var1
;         (let ((v (cadr var0)))
;           (set-cdr! var0 (cddr var0))
;           v))
;       ...
;       (define var-dot
;         (let ((v (cdr var0)))
;           (set! var0 (car var0))
;           v))))
;    ((define-values var expr)
;     (define var
;       (call-with-values (lambda () expr) list)))))
;
;(write
;  (letrec* ((x 1)) x))
;
;(write
;  (guard (condition
;          ((assq 'a condition) => cdr)
;          ((assq 'b condition)))
;    (raise (list (cons 'a 42))))) ;=> 42
;
;(write
;  (guard (condition
;          ((assq 'a condition) => cdr)
;          ((assq 'b condition)))
;    (raise (list (cons 'b 23))))) ;=> (b . 23)
;
;(define range
;  (case-lambda
;    ((e) (range 0 e))
;    ((b e) (do ((r '() (cons e r))
;                (e (- e 1) (- e 1)))
;               ((< e b) r)))))
;(write
;  (range 3)) ; => (0 1 2)
;(write
;  (range 3 5)) ; => (3 4)
;
;(define-values (x y) (integer-sqrt 17))
;(write
;  (list x y)) ; => (4 1)
;
;(write
;  (let ()
;    (define-values (x y) (values 1 2))
;    (+ x y))) ;=> 3


; TODO: parameter objects
;(define (parameter-convert param value)
;  (let ((proc (parameter-converter param)))
;    (if (procedure? proc)
;        (proc value)
;        value)))
;
;(define (make-parameter init . o)
;  (let ((conv (and (pair? o) (car o))))
;    (%make-parameter (if conv (conv init) init) conv)))
;
;(define-syntax parameterize
;  (syntax-rules ()
;    ((parameterize ("step") old cons-new ((param value ptmp vtmp) ...) () body)
;     (let ((ptmp param) ...)
;       (let ((vtmp (parameter-convert ptmp value)) ...)
;         (let ((old (thread-parameters)))
;           (let ((new cons-new))
;             (dynamic-wind
;                 (lambda () (thread-parameters-set! new))
;                 (lambda () . body)
;                 (lambda () (thread-parameters-set! old))))))))
;    ((parameterize ("step") old cons-new args ((param value) . rest) body)
;     (parameterize ("step") old (cons (cons ptmp vtmp) cons-new) ((param value ptmp vtmp) . args) rest body))
;    ((parameterize ((param value) ...) . body)
;     (parameterize ("step") old (thread-parameters) () ((param value) ...) body))))

(define radix
  (make-parameter
    10
    (lambda (x)
      (if (and (exact-integer? x) (<= 2 x 16))
          x
          (error "invalid radix")))))
(define (f n) (number->string n (radix)))

(display
  (f 12)) ;=> "12"
(newline)

(display
  (parameterize ((radix 2))
    (f 12))) ;=> "1100"
(newline)

(display
  (f 12)) ;=> "12"

;(display (radix 16))
;(display (parameterize ((radix 0)) (f 12)))
