Login
7 branches 0 tags
Ben (X13/Arch) Simplified things a little 0643405 9 days ago 1260 Commits
nujel / stdlib / string / printer.nuj
;;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;;; This project uses the MIT license, a copy should be included under /LICENSE
;;;
;;; Contains the printer, which is used pretty much everywhere a Nujel value needs
;;; a text representation.

(defn int->string/binary (α)
      "Turn α into a its **binary** string representation"
      (def ret "")
      (when (or (not α) (zero? α))
        (return "0"))
      (while (not-zero? α)
             (set! ret (cat (from-char-code (+ #\0 (bit-and α #b1))) ret))
             (set! α (bit-shift-right α 1)))
      ret)

(defn int->string/octal (α)
      "Turn α into a its **octal** string representation"
      (def ret "")
      (when (or (not α) (zero? α))
        (return "0"))
      (while (not-zero? α)
             (set! ret (cat (from-char-code (+ #\0 (bit-and α #b111))) ret))
             (set! α (bit-shift-right α 3)))
      ret)

(def int->string/hex/conversion-arr ##("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F"))
(defn int->string/HEX (α)
      "Turn α into a its **hexadecimal** string representation"
      (def ret "")
      (when (or (not α) (zero? α))
        (return "0"))
      (when (< α 0) (exception :type-error "Can't print negative numbers in hex for now" α ))
      (while (not-zero? α)
             (set! ret (cat (ref int->string/hex/conversion-arr (bit-and α #b1111)) ret))
             (set! α (bit-shift-right α 4)))
      ret)

(defn int->string/hex (α)
      "Turn α into a its **hexadecimal** string representation"
      (lower-case (int->string/HEX α)))

(defn int->string/decimal (α)
      "Turn α into a its **decimal** string representation"
      (:string α))
(def int->string int->string/decimal)

(def write/raw (let*
                 (defn write/raw/array (v port hr?)
                       (:block-write port "##(")
                       (dotimes (i (:length v))
                         (write/raw (ref v i) port hr?)
                         (when (< i (- (:length v) 1))
                           (:block-write port " ")))
                       (:block-write port ")"))

                 (defn write/raw/buffer (v port hr?)
                       (if hr?
                           (:write port "#<buffer :id " (int->string/decimal (val->id v))
                                   " :size " (int->string/hex (:length v)) ">")
                           (do (:block-write port "#m")
                               (def view (:u8 v))
                             (dotimes (i (:length v))
                               (:block-write port (pad-start (int->string/HEX (ref view i)) 2 "0"))))))

                 (defn write/raw/bytecode-array (v port hr?)
                       (:block-write port "#{")
                       (write/raw/array (:literals v) port #f)
                       (dotimes (i (:length v))
                         (when (zero? (bit-and i #x1F))
                           (:block-write port "\n"))
                         (:block-write port (pad-start (int->string/HEX (ref v i)) 2 "0")))
                       (:block-write port "\n}"))

                 (defn write/raw/tree/rec (v port hr? always-space?)
                       (when (or (not v)
                                 (not (:key* v)))
                         (return always-space?))
                       (when (write/raw/tree/rec (:left* v) port hr? always-space?)
                         (:block-write port " "))
                       (:block-write port (:string (:key* v)))
                       (:block-write port ": ")
                       (write/raw (:value* v) port hr?)
                       (write/raw/tree/rec (:right* v) port hr? #t))

                 (defn write/raw/tree (v port hr?)
                       (:block-write port "#@(")
                       (write/raw/tree/rec v port hr? #f)
                       (:block-write port ")"))

                 (defn write/raw/map (v port hr?)
                       (:block-write port "(map/new")
                       (dotimes (i (:size* v))
                         (def k (:key* v i))
                         (when-not (nil? k)
                                   (:block-write port " ")
                                   (write/raw k port hr?)
                                   (:block-write port " ")
                                   (write/raw (:value* v i) port hr?)))
                       (:block-write port ")"))

                 (defn write/raw/pair (v port hr?)
                       (when (and (= 'quote (car v))
                                  (nil? (cddr v))
                                  (pair? (cdr v)))
                         (:block-write port "'")
                         (return (write/raw (cadr v) port hr?)))
                       (:block-write port "(")
                       (def first? #f)
                       (while v
                         (if first?
                             (:block-write port " ")
                             (set! first? #t))
                         (if (pair? v)
                             (write/raw (car v) port hr?)
                             (do (:block-write port ". ")
                                 (write/raw v port hr?)))
                         (cdr! v))
                       (:block-write port ")"))

                 (defn write/raw/string (v port hr?)
                       (when hr? (return (:block-write port v)))
                       (:block-write port "\"")
                       (dotimes (i (:length v))
                         (def c (ref v i))
                         (case c
                               (0 (return (:block-write port "\"")))
                               (#x07 (:block-write port "\\a"))
                               (#x08 (:block-write port "\\b"))
                               (#x09 (:block-write port "\\t"))
                               (#x0A (:block-write port "\\n"))
                               (#x0B (:block-write port "\\v"))
                               (#x0C (:block-write port "\\f"))
                               (#x0D (:block-write port "\\r"))
                               (#x1B (:block-write port "\\e"))
                               (#x22 (:block-write port "\\\""))
                               (#x5C (:block-write port "\\\\"))
                               (otherwise (:char-write port c))))
                       (:block-write port "\""))

                 (defn write/raw (v port hr?)
                       (case (:type-name v)
                             (:nil             (when-not hr? (:block-write port "#nil")))
                             (:bool            (:block-write port (if v "#t" "#f")))
                             (:environment     (:write port "#<environment " (int->string/hex (val->id v)) ">"))
                             (:file-handle     (:write port "#<file-handle " (int->string/hex (val->id v)) ">"))
                             (:buffer-view     (:write port "#<buffer-view " (int->string/hex (val->id v)) ">"))
                             ((:lambda :macro :native-function) (:block-write port (:string (or (closure/name v) 'anonymous))))
                             (:int             (:block-write port (int->string/decimal v)))
                             (:float           (:block-write port (:string v))) ;; This one is kinda cheating
                             (:keyword         (:write port ":"   (:string v)))
                             (:symbol          (:block-write port (:string v)))
                             (:array           (write/raw/array v port hr?))
                             (:buffer          (write/raw/buffer v port hr?))
                             (:bytecode-array  (write/raw/bytecode-array v port hr?))
                             (:string          (write/raw/string v port hr?))
                             (:tree            (write/raw/tree v port hr?))
                             (:map             (write/raw/map v port hr?))
                             (:pair            (write/raw/pair v port hr?))
                             (:type            (:write port (capitalize (:string (:name v)))))
                             (otherwise        (:block-write port "#<unprintable>"))))))