(library (yuni core)
         (export ~ := 
                 define*
                 lambda*
                 let-with let-with*
                 is-a?
                 typeof
                 make touch!)
         (import (rnrs) 
                 (yuni miniobj)
                 (yuni miniobj minitype)
                 (yuni util invalid-form))

; internal
(define-syntax ref
  (syntax-rules ()
    ((_ target slot)
     (miniobj-ref target slot))))

; internal
(define-syntax refset!
  (syntax-rules ()
    ((_ target slot value)
     (miniobj-set! target slot value))))

; ~: generic, recursive ref/set syntax.
(define-syntax ~
  (syntax-rules (:=)
    ((_ target slot := obj)
     (refset! target slot obj))
    ((_ target slot)
     (ref target slot))
    ((_ target slot next-slot ...)
     (~ (ref target slot) next-slot ...))))

; define-composite
(define-syntax define-composite
  (syntax-rules ()
    ((_ typename slots)
     (define-minitype typename slots))))

; ~new
(define-syntax ~new
  (syntax-rules ()
    ((_ typename)
     (make-minitype-obj typename))))

; let-with
(define-syntax let-with
  (syntax-rules ()
    ((_ OBJ (specs0) body ...)
     (let-with-binder OBJ specs0 body ...))
    ((_ OBJ (specs0 specs1 ...) body ...)
     (let ((myobj OBJ))
       (let-with-binder myobj specs0
                        (let-with myobj (specs1 ...) body ...))))))

(define-syntax let-with*
  (syntax-rules ()
    ((_ (specs0 specs1 ...) body ...)
     (let-with specs0 (let-with* (specs1 ...) body ...)))
    ((_ (specs0) body ...)
     (let-with specs0 body ...))))

(define-syntax let-with-binder
  (syntax-rules ()
    ((_ OBJ (bindname name) body ...)
     (let ((bindname (~ OBJ 'name)))
       body ...))
    ((_ OBJ name body ...)
     (let ((name (~ OBJ 'name)))
       body ...))))

(define-syntax typeof
  (syntax-rules ()
    ((_ obj)
     (miniobj-typeof obj))))

(define-syntax is-a?
  (syntax-rules ()
    ((_ obj type)
     (and type (eq? type
                    (miniobj-typeof obj))))))

; make
(define-syntax make-apply-rule1!
  (syntax-rules ()
    ((_ NAME (slot body))
     (let ((result body))
       (~ NAME 'slot := result)))))

(define-syntax make
  (syntax-rules ()
    ((_ TYPE rule0 ...)
     (let ((new-object ((~new TYPE))))
       (make-apply-rule1! new-object rule0)
       ...
       new-object))))

(define-syntax touch!-bind-spec1
  (syntax-rules ()
    ((_ OBJ (slot) body ...)
     (begin body ...)) ; nothing to do (legacy form)
    ((_ OBJ (#f slot) body ...)
     (begin body ...)) ; nothing to do
    ((_ OBJ (bind slot) body ...)
     (let-with OBJ ((bind slot))
               body ...))
    ((_ OBJ slot body ...)
     (touch!-bind-spec1 OBJ (slot slot) body ...))))

(define-syntax touch!-bind-spec
  (syntax-rules ()
    ((_ OBJ (spec0) body ...)
     (touch!-bind-spec1 OBJ spec0 body ...))
    ((_ OBJ (spec0 spec1 ...) body ...)
     (touch!-bind-spec1 OBJ spec0
                        (touch!-bind-spec OBJ (spec1 ...) body ...)))))

(define-syntax touch!-apply-spec1!
  (syntax-rules ()
    ((_ OBJ (slot) body ...)
     (~ OBJ 'slot := body ...))
    ((_ OBJ (#f slot) body ...)
     (~ OBJ 'slot := body ...))
    ((_ OBJ (bind slot) body ...)
     (~ OBJ 'slot := body ...))
    ((_ OBJ slot body ...)
     (~ OBJ 'slot := body ...))))

(define-syntax touch!
  (syntax-rules ()
    ((_ OBJ (bind-spec0 body-spec0) ...)
     (let ((myobj OBJ))
       (touch!-bind-spec myobj (bind-spec0 ...)
                         (touch!-apply-spec1! myobj bind-spec0 body-spec0)
                         ...
                         myobj)))))


(define (type-check sym id-name type-name id type)
  (if (is-a? id type)
    'ok
    (begin ;; FIXME
      (assertion-violation 'yuni-core
                           "type violation"
                           id-name
                           type-name))))

(define-syntax annotate-check
  (syntax-rules ()
    ((_ sym (id type))
     (type-check sym 'id 'type id type))
    ((_ sym id)
     'ok)))

(define-syntax raw-name
  (syntax-rules ()
    ((_ id) id)
    ((_ (id type)) id)))

(define-syntax lambda*0-itr
  (syntax-rules ()
    ((_ sym (cur ...) (spec ...) ((id bogus) rest0 ...) body ...)
     (lambda*0-itr sym (cur ... id) ((id bogus) spec ...) (rest0 ...) body ...))
    ((_ sym (cur ...) (spec ...) ((id) rest0 ...) body ...)
     (let ((proxy id))
       (lambda*0-itr sym (cur ...) (spec ...) ((id proxy) rest0 ...) body ...)))
    ((_ sym (cur ...) (spec ...) (id rest0 ...) body ...)
     (lambda*0-itr sym (cur ... id) (spec ...) (rest0 ...) body ...))
    ((_ sym (cur ...) (spec ...) () body ...)
     (lambda (cur ...) 
       (annotate-check sym spec) ...
       (let () body ...)))))

(define-syntax lambda*0
  (syntax-rules ()
    ((_ sym (spec0 ...) body ...)
     (lambda*0-itr sym () () (spec0 ...) body ...))))

(define-syntax lambda*
  (syntax-rules ()
    ((_ sym (spec0 ...) body ...)
     (lambda*0 'lambda (spec0 ...) body ...))))

(define-syntax define*
  (syntax-rules ()
    ((_ (name spec0 ...) body ...)
     (define name (lambda*0 'name (spec0 ...) body ...)))
    ((_ name spec)
     (define-composite name spec))))

(define-invalid-form :=)

)
