(in-package :html-jen)
(defun dosya-metni-oku (dosya)
(let* ((uzunluk 0)
(metin
(with-output-to-string (out)
(with-open-file (in dosya :external-format :utf-8)
(loop :with arabellek := (make-array 8192 :element-type 'character)
:for n := (read-sequence arabellek in)
:while (< 0 n)
:do (incf uzunluk n)
(write-sequence arabellek out :start 0 :end n))))))
(values metin uzunluk)))
(defclass okur ()
((metin :initarg :metin :accessor metin
:documentation "Markdown string")
(i :initform 0 :accessor i
:documentation "Metin indeksi")
(uzunluk :initarg :uzunluk :accessor uzunluk
:documentation "Metin uzunluğu")
(blok-baş :accessor blok-baş :initform 0
:documentation "Bloğun başının indexi")
(blok-son :accessor blok-son :initform 0
:documentation "Bloğun sonunun indexi")
(dosya-ismi :initarg :dosya-ismi
:documentation "Okunan markdown dosyasının ismi")))
(defun okur! (dosya-ismi)
(multiple-value-bind (metin uzunluk)
(dosya-metni-oku dosya-ismi)
(make-instance 'okur :metin metin :uzunluk uzunluk :dosya-ismi dosya-ismi)))
(defun metin-okur! (metin)
(make-instance 'okur :metin metin :uzunluk (length metin) :dosya-ismi ""))
(defmethod son? ((okur okur) index)
(>= index
(uzunluk okur)))
(defmethod index-reset ((okur okur))
(setf (i okur) 0
(blok-son okur) 0))
(defmethod blok-reset ((okur okur))
(setf (i okur) (blok-baş okur)))
(defmethod bak ((okur okur))
(and (not (son? okur (i okur)))
(char (metin okur) (i okur))))
(defmethod ye ((okur okur))
(with-slots (metin i) okur
(and (son? okur i)
(prog1 (char metin i)
(incf i)))))
(defun son-ara (parça metin baş)
(let ((eş (search parça metin :start2 baş)))
(when (numberp eş)
(+ eş (length parça)))))
(let ((blok-sonu (format nil "~%~%")))
(defmethod sonraki-blok ((okur okur))
"blok-baş ve blok-son indexlerinin atamasını yapar. Başka blok kalmadıysa nil döndürür."
(unless (son? okur (blok-son okur))
(with-slots (i uzunluk metin blok-son blok-baş) okur
(let ((yeni-baş (progn (setf i blok-son)
(loop :for ch := (bak okur)
:while (and ch
(or (char= #\Newline ch)
(char= #\Space ch)))
:do (incf i)
:finally (return i))))
(yeni-son (or (if (string= "```" metin :start2 i :end2 (+ i 3))
(progn (setf i (son-ara "```" metin (+ i 3)))
(son-ara blok-sonu metin i))
(son-ara blok-sonu metin i))
uzunluk)))
(setf blok-son yeni-son
blok-baş yeni-baş
i blok-baş))))))
(defmethod boşluk-yut ((okur okur))
(with-slots (i metin) okur
(loop :while (char= #\Space (char metin i))
:do (incf i))))
(defmethod metin-parçası ((okur okur) uzunluk baş)
(make-array uzunluk
:element-type 'character
:displaced-to (metin okur)
:displaced-index-offset baş))
(defgeneric ayrıştır (okur tag)
(:documentation "Tag'a göre bloktaki elementleri belirler."))
(defmethod ayrıştır ((okur okur) (tag (eql :h)))
(with-slots (i blok-son metin) okur
(let ((h-sayı 0))
(loop :while (char= #\# (char metin i))
:do (incf i)
(incf h-sayı))
(assert (char= #\Space (char metin i)))
(assert (< h-sayı 7))
(boşluk-yut okur)
(çocuk-node! (intern (format nil "H~D" h-sayı) "KEYWORD")
(metin-parçası okur
(- (or (position #\Newline metin :start i)
blok-son)
i)
i)))))
(defmethod ayrıştır ((okur okur) (tag (eql :ul)))
(with-slots (i blok-son metin) okur
(let ((itemler (loop :while (and (< i blok-son)
(not (char= #\Newline (char metin i))))
:do (assert (string= "- " metin :start2 i :end2 (+ i 2)))
(incf i 2)
:collect (let ((yeni (or (position #\Newline metin :start i)
blok-son)))
(prog1 (metin-parçası okur (- yeni i) i)
(setf i (+ yeni 1)))))))
(ebeveyn-node! :ul
(mapcar (lambda (item)
(çocuk-node! :li item))
itemler)))))
(defmethod ayrıştır ((okur okur) (tag (eql :ol)))
(with-slots (i blok-son metin) okur
(let ((item-sayısı 1))
(let ((itemler
(loop :while (and (< i blok-son)
(not (char= #\Newline (char metin i))))
:do (let ((i2 i))
(loop :while (char<= #\0 (char metin i2) #\9)
:do (incf i2))
(assert (string= ". " metin :start2 i2 :end2 (+ i2 2)))
(assert (= item-sayısı (parse-integer metin :start i :end i2)))
(incf item-sayısı)
(setf i (+ i2 2)))
:collect (let ((yeni (or (position #\Newline metin :start i)
blok-son)))
(prog1 (metin-parçası okur (- yeni i) i)
(setf i (+ yeni 1)))))))
(ebeveyn-node! :ol
(mapcar (lambda (item)
(çocuk-node! :li item))
itemler))))))
(defmethod ayrıştır ((okur okur) (tag (eql :code)))
(with-slots (i metin blok-son blok-baş) okur
(assert (string= "```" metin :start2 blok-baş :end2 (+ blok-baş 3)))
(let ((kod-baş (+ 1 (position #\Newline metin :start (+ blok-baş 3)))))
(let ((kod (metin-parçası okur
(- (search "```" metin :from-end t :end2 blok-son)
kod-baş)
kod-baş)))
(ebeveyn-node! :pre
(list (çocuk-node! :code
kod)))))))
(defgeneric inline-ayrıştır (okur tag son)
(:documentation "Paragraf içindeki elementleri ayrıştırır"))
(defmethod inline-ayrıştır ((okur okur) tag str)
(with-slots (i metin blok-son) okur
(let ((son (search str metin
:start2 i
:end2 blok-son)))
(prog1 (çocuk-node! tag (metin-parçası okur (- son i) i))
(setf i (+ son (length str)))))))
(defmethod link-ayrıştır ((okur okur))
(with-slots (i metin blok-son) okur
(let* ((son (search "]" metin
:start2 i
:end2 blok-son))
(link-metni (metin-parçası okur (- son i) i)))
(setf i (+ son 1))
(assert (char= #\( (char metin i)))
(incf i)
(let ((son (search ")" metin
:start2 i
:end2 blok-son)))
(prog1 (çocuk-node! :a link-metni
(list :href (metin-parçası okur (- son i) i)))
(setf i (+ 1 son)))))))
(defparameter *dipnot-limit* 36)
(defparameter *dipnotlar*
(make-array *dipnot-limit* :element-type 'bit :initial-element 0))
(defun dipnot-var? (n)
(= 1 (aref *dipnotlar* n)))
(defun dipnot-ata (n)
(setf (aref *dipnotlar* n) #b1))
(defmethod dipnot-ayrıştır ((okur okur))
(declare (optimize (debug 3) (safety 3)))
(with-slots (i metin blok-son) okur
(let* ((son (position #\] metin
:start i
:end blok-son))
(n (parse-integer metin :start i :end son)))
(prog1 (if (dipnot-var? n)
(ebeveyn-node! :li
(list (let ((baş (+ 1 son)))
(assert (char= (char metin baş) #\:))
(setf son (or (position #\Newline metin :start baş :end blok-son) blok-son))
(metin-parçası okur (- son baş 1) (+ baş 1)))
(çocuk-node! :a " ↩" (list :href (format nil "#dipnot-ref-~d" n))))
(list :id (format nil "dipnot-~d" n)))
(progn (dipnot-ata n)
(ebeveyn-node! :sup
(list (çocuk-node! :a (metin-parçası okur (- son i) i)
(list :href (format nil "#dipnot-~d" n)
:id (format nil "dipnot-ref-~d" n)))))))
(setf i (min blok-son (+ son 1)))))))
(defmethod paragraf-ayrıştır ((okur okur))
(with-slots (i blok-son metin) okur
(let ((elementler (list))
(dipnotlar (list))
(baş i))
(labels ((pushla-ve-ayrıştır (tag str)
(push (metin-parçası okur (- i baş) baş) elementler)
(incf i (length str))
(push (inline-ayrıştır okur tag str) elementler)
(setf baş i)))
(loop :while (< i blok-son)
:do (case (char metin i)
(#\[ (cond ((char= #\^ (char metin (+ 1 i)))
(push (metin-parçası okur (- i baş) baş) elementler)
(incf i 2)
(let ((dipnot (dipnot-ayrıştır okur)))
(case (tag dipnot)
(:sup (push dipnot elementler))
(:li (push dipnot dipnotlar))))
(setf baş i))
((char= #\( (char metin (+ 1 (position #\] metin :start i))))
(push (metin-parçası okur (- i baş) baş) elementler)
(incf i 1)
(push (link-ayrıştır okur) elementler)
(setf baş i))
(t (incf i))))
(#\` (pushla-ve-ayrıştır :code "`"))
(#\_ (pushla-ve-ayrıştır :i "_"))
(#\* (if (char= #\* (char metin (+ i 1)))
(pushla-ve-ayrıştır :b "**")
(pushla-ve-ayrıştır :i "*")))
(#\!
(when (char= #\[ (char metin (+ i 1)))
(push (metin-parçası okur (- i baş) baş) elementler)
(incf i 2)
(let ((link (link-ayrıştır okur)))
(push (çocuk-node! :img
nil
(list :src (getf (props link) :href)
:alt (value link)))
elementler))
(setf baş i))
(incf i))
(t (incf i)))
:finally (loop :while (char= #\Newline (char metin (- i 1)))
:do (decf i))
(when (/= baş i)
(push (metin-parçası okur (- i baş) baş) elementler))
(when (not (null dipnotlar))
(push (ebeveyn-node! :ol (reverse dipnotlar)) elementler)))
(ebeveyn-node! :p (reverse elementler))))))
(defmethod blok-ayrıştır ((okur okur))
(when (sonraki-blok okur)
(with-slots (i metin) okur
(case (char metin i)
(#\# (ayrıştır okur :h))
(#\- (ayrıştır okur :ul))
(#\` (ayrıştır okur :code))
(#\> "")
(t (if (char<= #\0 (char metin i) #\9)
(ayrıştır okur :ol)
(paragraf-ayrıştır okur)))))))
(defmethod markdown-ayrıştır ((okur okur))
(labels ((öz (okur)
(let ((blok-node (blok-ayrıştır okur)))
(if (null blok-node)
nil
(cons blok-node (öz okur))))))
(ebeveyn-node! :div
(öz okur))))
(defparameter *okur* (okur! "/home/riton/projects/html-jen/cheat.md"))
(defmethod blok-yazdır ((okur okur) &optional (stream *standard-output*))
(write-sequence (metin okur) stream
:start (blok-baş okur)
:end (blok-son okur))
(values))
(defmethod blok-metni ((okur okur))
(with-slots (blok-son blok-baş metin) okur
(make-array (- blok-son blok-baş)
:element-type 'character
:displaced-to metin
:displaced-index-offset blok-baş)))
(defmethod okur-blok-listesi ((okur okur))
(loop :while (sonraki-blok okur)
:collect (blok-metni okur)))