(define (caaar x) (car (car (car x))))
(define (caadr x) (car (car (cdr x))))
(define (cadar x) (car (cdr (car x))))
(define (cdddr x) (cdr (cdr (cdr x))))
(define (cdaar x) (cdr (car (car x))))
(define (cdadr x) (cdr (car (cdr x))))
(define (cddar x) (cdr (cdr (car x))))
(define (caaaar x) (car (car (car (car x)))))
(define (caaadr x) (car (car (car (cdr x)))))
(define (caadar x) (car (car (cdr (car x)))))
(define (caaddr x) (car (car (cdr (cdr x)))))
(define (cadaar x) (car (cdr (car (car x)))))
(define (cadadr x) (car (cdr (car (cdr x)))))
(define (caddar x) (car (cdr (cdr (car x)))))
(define (cadddr x) (car (cdr (cdr (cdr x)))))
(define (cdaaar x) (cdr (car (car (car x)))))
(define (cdaadr x) (cdr (car (car (cdr x)))))
(define (cdadar x) (cdr (car (cdr (car x)))))
(define (cdaddr x) (cdr (car (cdr (cdr x)))))
(define (cddaar x) (cdr (cdr (car (car x)))))
(define (cddadr x) (cdr (cdr (car (cdr x)))))
(define (cdddar x) (cdr (cdr (cdr (car x)))))
(define (cddddr x) (cdr (cdr (cdr (cdr x)))))
(defmacro define-service (var-name svc-name . opts)
(define (kw? v)
(and (symbol? v)
(string=? (substring (symbol->string v) 0 1) ":")))
(define (split-opts lst)
(if (null? lst) '()
(let loop ((rest (cdr lst))
(kw (car lst))
(vals '())
(result '()))
(cond
((null? rest)
(reverse (cons (cons kw (reverse vals)) result)))
((kw? (car rest))
(loop (cdr rest)
(car rest)
'()
(cons (cons kw (reverse vals)) result)))
(else
(loop (cdr rest) kw (cons (car rest) vals) result))))))
(define (expand-opt group)
(let* ((kw (car group))
(sym (string->symbol (substring (symbol->string kw) 1)))
(args (cdr group)))
(if (and (not (null? args)) (pair? (car args)))
(map (lambda (sub)
(if (list? sub)
`(list ',sym ,@sub) `(list ',sym ,(car sub) ,(cdr sub)))) args)
(list `(list ',sym ,@args)))))
`(define ,var-name
(service ,svc-name ,@(apply append (map expand-opt (split-opts opts))))))
(define (assoc key lst)
(cond ((null? lst) #f)
((string=? key (car (car lst))) (car lst))
(else (assoc key (cdr lst)))))
(define (result-ref results name)
(let ((entry (assoc name results)))
(if entry
(cdr entry)
(errorf "result-ref: no result for '~a'" name))))
(define (zero? x) (= x 0))
(defmacro logf (fmt . args)
`(log (format ,fmt ,@args)))
(defmacro errorf (fmt . args)
`(error (format ,fmt ,@args)))
(defmacro unless (condition . body)
`(when (not ,condition) ,@body))
(define (ok value) (list 'ok value))
(define (err reason) (list 'err reason))
(define (ok? r) (and (pair? r) (eq? (car r) 'ok)))
(define (err? r) (and (pair? r) (eq? (car r) 'err)))
(define (ok-value r) (cadr r))
(define (err-reason r) (cadr r))
(defmacro with-cleanup (cleanup . body)
`(guard (exn (#t (,cleanup (err exn)) (error exn)))
(let ((result (begin ,@body)))
(,cleanup (ok result))
result)))
(defmacro define-nodes bindings
`(begin
,@(map (lambda (b) `(define ,(car b) (start ,(cadr b))))
bindings)))
(defmacro define-results (results-var . bindings)
`(begin
,@(map (lambda (b) `(define ,(car b) (result-ref ,results-var ,(cadr b))))
bindings)))
(defmacro define-run opts-and-bindings
(define (split-args lst)
(let loop ((rest lst) (kws '()))
(cond
((null? rest) (cons (reverse kws) '()))
((pair? (car rest)) (cons (reverse kws) rest))
(else (loop (cdr rest) (cons (car rest) kws))))))
(let* ((parts (split-args opts-and-bindings))
(kws (car parts))
(bindings (cdr parts))
(vars (map cadr bindings))
(names (map car bindings))
(keys (map (lambda (b) (symbol->string (cadr b))) bindings)))
`(begin
(define _run_result_ (run (list ,@vars) ,@kws))
,@(map (lambda (name key)
`(define ,name (result-ref _run_result_ ,key)))
names keys))))
(defmacro define-then (name upstream params . body)
`(define ,name (then ,upstream (lambda ,params ,@body) :name ,(symbol->string name))))