Carp/core/Format.carp
2020-01-14 09:32:22 +01:00

40 lines
1.9 KiB
Plaintext

(private fmt-internal)
(hidden fmt-internal)
(defndynamic fmt-internal [s args]
(let [idx (String.index-of s \%)
len (String.length s)]
(if (= idx -1)
(list 'copy s) ; no more splits found, just return string
(if (= len 1)
(macro-error "error in format string: expected expression after last %")
(if (= \% (String.char-at s (inc idx))) ; this is an escaped %
(list 'ref
(list 'String.append
"%"
(fmt-internal (String.substring s (+ idx 2) len) args)))
(if (= 0 (length args)) ; we need to insert something, but have nothing
(macro-error
(str "error in format string: not enough arguments to format string (missing argument for '%"
(String.substring s (inc idx) (inc (inc idx)))
"')"))
; okay, this is the meat:
; get the next % after our escaper
(let [next (String.index-of (String.substring s (inc idx) len) \%)]
(if (= -1 next)
(if (< 1 (length args))
(macro-error
(str "error in format string: too many arguments to format string (missing directive for '"
(cadr args)
"')"))
(list 'ref (list 'format s (car args))))
(let [slice (String.substring s 0 (+ (inc idx) next))]
(list 'ref
(list 'String.append
(list 'ref (list 'format slice (car args)))
(fmt-internal (String.substring s (+ (inc idx) next) len)
(cdr args)))))))))))))
(doc fmt "formats a string. It supports all of the string interpolations defined in format of the type that should be interpolated (e.g. %d and %x on integers).")
(defmacro fmt [s :rest args]
(list 'copy (fmt-internal s args)))