;; some library stuff

(define (++ . args) (apply string-append args))

(define (as-string s)
  (if (symbol? s) (symbol->string s) s))

(define (intercalate x xs)
  (define (go ls)
    (cond ((null? ls) '())
          ((null? (cdr ls)) ls)
          (else
           (cons (car ls) (cons x (go (cdr ls)))))))
  (apply ++ (go xs)))

(define (scss->css scss)
  (define (mk-selector selector)
    (cond ((symbol? selector)
           (symbol->string selector))
          ((and (list? selector)
                (eq? (car selector) '=)
                (eq? (cadr selector) 'class))
           (++ "." (symbol->string (caddr selector))))
          ((and (list? selector)
                (eq? (car selector) '=)
                (eq? (cadr selector) 'id))
           (++ "#" (symbol->string (caddr selector))))
          ((and (list? selector)
                (eq? (car selector) '//))
           (intercalate " " (map mk-selector (cdr selector))))
          ((and (list? selector)
                (eq? (car selector) '&&))
           (intercalate "," (map mk-selector (cdr selector))))
          (else "...")))
  (define (mk-line line)
    (++ "  " (as-string (car line)) ": " (as-string (cadr line)) ";\n"))
  (define (mk-clause clause)
    (++
     (mk-selector (car clause))
     " {\n"
     (apply ++ (map mk-line (cdr clause)))
     "}\n"))
  (apply ++ (map mk-clause scss)))

(define (cadr? sexp)
  (if (or (null? sexp)
          (null? (cdr sexp))
          (not (list? (cadr sexp))))
      #f (cadr sexp)))

(define (serialize-sxml sxml)
  (define (mk-prop prop)
    (if (null? (cdr prop))
        (++ " " (as-string (car prop)))
        (let ((key (as-string (car prop))) (val (as-string (cadr prop))))
          (++ " " key "="
              (if (string? val) (++ "\"" val "\"") (as-string val))))))
  (cond ((null? sxml) "")
        ((string? sxml) sxml)
        ((symbol? sxml) (symbol->string sxml))
        ((not (symbol? (car sxml)))
         (apply string-append (map serialize-sxml sxml)))
        (else
         (let ((tag (symbol->string (car sxml)))
               (props (if (and (cadr? sxml) (eq? (caadr sxml) '@))
                          (cdadr sxml) '()))
               (vals (if (and (cadr? sxml) (eq? (caadr sxml) '@))
                         (cddr sxml) (cdr sxml))))
           (if (null? vals)
               (++ "<"
                   tag
                   (apply ++ (map mk-prop props))
                   " />")
               (++ "<"
                   tag
                   (apply ++ (map mk-prop props))
                   ">"
                   (apply ++ (map serialize-sxml vals))
                   "</"
                   tag
                   ">"))))))

(define (sxml->html5 sxml)
  (++ "<!DOCTYPE html>" (serialize-sxml sxml)))
