nested callable data structures pasted by mario-goulart on Sat Dec 7 15:37:16 2013

(use callable-hash-tables)

(define +none+
  (list 'none))

(define (nested-get callable path #!optional default)
  (let loop ((path path)
             (callable/val callable))
    (if (null? path)
        (if (eq? callable/val +none+)
            default
            callable/val)
        (if (procedure? callable/val)
            (let* ((key (car path))
                   (val (callable/val key +none+)))
              (if (eq? val +none+)
                  default
                  (loop (cdr path) val)))
            default))))

(define ~make-callable-hash-table)

(define silly-list
  (~(car . 0)
       (cdr . ,(~(car . 1)
                    (cdr . ,(~(car . 2)
                                 (cdr))))))))))

(print (nested-get silly-list '(cdr cdr car)))

fixed nested callable data structures pasted by mario-goulart on Sat Dec 7 15:40:49 2013

(use callable-hash-tables)

(define +none+
  (list 'none))

(define (nested-get callable path #!optional default)
  (let loop ((path path)
             (callable/val callable))
    (if (null? path)
        (if (eq? callable/val +none+)
            default
            callable/val)
        (if (procedure? callable/val)
            (let* ((key (car path))
                   (val (callable/val key +none+)))
              (if (eq? val +none+)
                  default
                  (loop (cdr path) val)))
            default))))

(define ^ make-callable-hash-table)

(define silly-list
  (^ `((car . 0)
       (cdr . ,(^ `((car . 1)
                    (cdr . ,(^ `((car . 2)
                                 (cdr))))))))))

(print (nested-get silly-list '(cdr cdr car)))

get-in & set-in! pasted by mario-goulart on Sat Dec 7 19:33:58 2013

(use callable-hash-tables)

(define +none+
  (list 'none))

(define (get-in callable path #!optional default)
  (let loop ((path path)
             (node callable))
    (if (null? path)
        (if (eq? node +none+)
            default
            node)
        (if (procedure? node)
            (let* ((key (car path))
                   (val (node key +none+)))
              (if (eq? val +none+)
                  default
                  (loop (cdr path) val)))
            default))))

(define (new-callable callable)
  ((case (procedure-information callable)
     ((callable-alist) make-callable-alist)
     ((callable-hash-table) make-callable-hash-table)
     (else (error 'set-in!
                  "Invalid callable datastructure type:"
                  (procedure-information callable))))))

(define (callable-mapping-data-structure? obj required-type)
  (and (procedure? obj)
       ((case required-type
          ((callable-alist) callable-alist?)
          ((callable-hash-table) callable-hash-table?)
          (else (error 'set-in!
                       "Invalid callable datastructure type:"
                       required-type)))
        obj)))

(define (set-in! callable path value)
  (let loop ((path path)
             (node callable))
    (if (null? (cdr path))
        ;; Reached the last item in path, so set it and return it
        (begin
          (set! (node (car path)) value)
          node)
        ;; Not the last item in path. If the current path doesn't
        ;; exist, create it.  If it exists but it is not a callable,
        ;; set it to a new callable. Otherwise, just leave it as it is.
        (let* ((key (car path))
               (val (node key +none+)))
          (when (or (eq? val +none+)
                    (not (callable-mapping-data-structure?
                          val
                          (procedure-information node))))
            (set! (node key)
                  (new-callable node)))
          (loop (cdr path)
                (node key))))))

(define ^ make-callable-hash-table)

(define silly-list
  (^ `((car . 0)
       (cdr . ,(^ `((car . 1)
                    (cdr . ,(^ `((car . 2)
                                 (cdr))))))))))

(use test)

(test-assert (callable-hash-table? (get-in silly-list '())))

(test "checking car = 0"
      0
      (get-in silly-list '(car)))

(test "checking cadr = 1"
      1
      (get-in silly-list '(cdr car)))

(test "checking caddr = 2"
      2
      (get-in silly-list '(cdr cdr car)))

(test "setting car to a"
      'a
      (get-in (set-in! silly-list '(car) 'a) '(car)))

(test "setting cadr to b"
      '(a b)
      (begin
        (set-in! silly-list '(cdr car) 'b)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car)))))

(test "setting caddr to c"
      '(a b c)
      (begin
        (set-in! silly-list '(cdr cdr car) 'c)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car)))))

(test "setting cadddr (non existent up til now) to d"
      '(a b c d)
      (begin
        (set-in! silly-list '(cdr cdr cdr car) 'd)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car))
              (get-in silly-list '(cdr cdr cdr car)))))

(test "setting caddr to x"
      '(a b x d)
      (begin
        (set-in! silly-list '(cdr cdr car) 'x)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car))
              (get-in silly-list '(cdr cdr cdr car)))))

(define something (^))
(set-in! something '(foo) 'a)
(test 'a (something 'foo))
(test 'a (get-in something '(foo)))

(set-in! something '(foo bar) 'b)
(test 'b (get-in something '(foo bar)))

(set-in! something '(foo bar baz) (^ '((foo . 1) (bar . 2))))
(test-assert (callable-hash-table? (get-in something '(foo))))
(test-assert (callable-hash-table? (get-in something '(foo bar))))
(test-assert (callable-hash-table? (get-in something '(foo bar baz))))
(test 1 (get-in something '(foo bar baz foo)))
(test 2 (get-in something '(foo bar baz bar)))

get-in & set-in! (bugfix!) added by mario-goulart on Sat Dec 7 19:39:42 2013

(use callable-hash-tables)

(define +none+
  (list 'none))

(define (get-in callable path #!optional default)
  (let loop ((path path)
             (node callable))
    (if (null? path)
        (if (eq? node +none+)
            default
            node)
        (if (procedure? node)
            (let* ((key (car path))
                   (val (node key +none+)))
              (if (eq? val +none+)
                  default
                  (loop (cdr path) val)))
            default))))

(define (new-callable callable)
  ((case (procedure-information callable)
     ((callable-alist) make-callable-alist)
     ((callable-hash-table) make-callable-hash-table)
     (else (error 'set-in!
                  "Invalid callable datastructure type:"
                  (procedure-information callable))))))

(define (callable-mapping-data-structure? obj required-type)
  (and (procedure? obj)
       ((case required-type
          ((callable-alist) callable-alist?)
          ((callable-hash-table) callable-hash-table?)
          (else (error 'set-in!
                       "Invalid callable datastructure type:"
                       required-type)))
        obj)))

(define (set-in! callable path value)
  (if (null? path)
      callable
      (let loop ((path path)
                 (node callable))
        (if (null? (cdr path))
            ;; Reached the last item in path, so set it and return it
            (begin
              (set! (node (car path)) value)
              node)
            ;; Not the last item in path. If the current path doesn't
            ;; exist, create it.  If it exists but it is not a callable,
            ;; set it to a new callable. Otherwise, just leave it as it is.
            (let* ((key (car path))
                   (val (node key +none+)))
              (when (or (eq? val +none+)
                        (not (callable-mapping-data-structure?
                              val
                              (procedure-information node))))
                (set! (node key)
                      (new-callable node)))
              (loop (cdr path)
                    (node key)))))))

(define ^ make-callable-hash-table)

(define silly-list
  (^ `((car . 0)
       (cdr . ,(^ `((car . 1)
                    (cdr . ,(^ `((car . 2)
                                 (cdr))))))))))

(use test)

(test-assert (callable-hash-table? (get-in silly-list '())))

(test "checking car = 0"
      0
      (get-in silly-list '(car)))

(test "checking cadr = 1"
      1
      (get-in silly-list '(cdr car)))

(test "checking caddr = 2"
      2
      (get-in silly-list '(cdr cdr car)))

(test "setting car to a"
      'a
      (get-in (set-in! silly-list '(car) 'a) '(car)))

(test "setting cadr to b"
      '(a b)
      (begin
        (set-in! silly-list '(cdr car) 'b)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car)))))

(test "setting caddr to c"
      '(a b c)
      (begin
        (set-in! silly-list '(cdr cdr car) 'c)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car)))))

(test "setting cadddr (non existent up til now) to d"
      '(a b c d)
      (begin
        (set-in! silly-list '(cdr cdr cdr car) 'd)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car))
              (get-in silly-list '(cdr cdr cdr car)))))

(test "setting caddr to x"
      '(a b x d)
      (begin
        (set-in! silly-list '(cdr cdr car) 'x)
        (list (get-in silly-list '(car))
              (get-in silly-list '(cdr car))
              (get-in silly-list '(cdr cdr car))
              (get-in silly-list '(cdr cdr cdr car)))))

(define something (^))
(set-in! something '(foo) 'a)
(test 'a (something 'foo))
(test 'a (get-in something '(foo)))

(set-in! something '(foo bar) 'b)
(test 'b (get-in something '(foo bar)))

(set-in! something '(foo bar baz) (^ '((foo . 1) (bar . 2))))
(test-assert (callable-hash-table? (get-in something '(foo))))
(test-assert (callable-hash-table? (get-in something '(foo bar))))
(test-assert (callable-hash-table? (get-in something '(foo bar baz))))
(test 1 (get-in something '(foo bar baz foo)))
(test 2 (get-in something '(foo bar baz bar)))