File: lml2.lisp

package info (click to toggle)
cl-markdown 20101006-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 556 kB
  • sloc: lisp: 5,863; makefile: 11
file content (194 lines) | stat: -rw-r--r-- 6,957 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
(in-package #:cl-markdown)

(defparameter *markup->lml2*
  (make-container 
   'simple-associative-container
   :test #'equal
   :initial-contents 
   '((header1)    (nil :h1)
     (header2)    (nil :h2)
     (header3)    (nil :h3)
     (header4)    (nil :h4)
     (header5)    (nil :h5)
     (header6)    (nil :h6)
     
     (bullet)     ((:ul) :li)
     (code)       ((:pre :code) nil)
     (number)     ((:ol) :li)
     (quote)      ((:blockquote) nil)
     (horizontal-rule) (nil :hr))))

;;; ---------------------------------------------------------------------------

(defmethod render ((document document) (style (eql :lml2)) stream)
  (let ((*current-document* document))
    (setf (level document) 0
          (markup document) nil)
    (let* ((chunks (collect-elements (chunks document)))
           (result (lml2-list->tree chunks)))
      (if stream
        (format stream "~S" result)
        result))))

;;; ---------------------------------------------------------------------------

(defun lml2-marker (chunk)
  (bind ((markup (markup-class-for-lml2 chunk)))
    (first markup)))

;;; ---------------------------------------------------------------------------

(defmethod render-to-lml2 ((chunk chunk))
  (bind ((block (collect-elements
                 (lines chunk)
                 :transform (lambda (line)
                              (render-to-lml2 line))))
         (markup (second (markup-class-for-lml2 chunk)))
         (paragraph? (paragraph? chunk)))
    (cond ((and paragraph? markup)
           (values `(,markup (:P ,@block)) t))
          (paragraph?
           (values `(:P ,@block) t))
          (markup
           (values `(,markup ,@block) t))
          (t
           (values block nil)))))

;;; ---------------------------------------------------------------------------

(defmethod markup-class-for-lml2 ((chunk chunk))
  (when (markup-class chunk)
    (let ((translation (item-at-1 *markup->lml2* (markup-class chunk))))
      (unless translation 
        (warn "No translation for '~A'" (markup-class chunk)))
      translation)))

;;; ---------------------------------------------------------------------------

(defmethod render-to-lml2 ((chunk list))
  (render-span-to-lml2 (first chunk) (rest chunk)))

;;; ---------------------------------------------------------------------------

(defmethod render-to-lml2 ((chunk string))
  ;;?? unlovely
  (format nil "~A" chunk))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'strong)) body)
  `(:strong ,@body))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'mail)) body)
  (let ((address (first body)))
    `((:a :href ,(format nil "mailto:~A" address)) ,address)))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'emphasis)) body)
  `(:em ,@body))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'strong-em)) body)
  `(:strong (:em ,@body)))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'code)) body)
  `(:code ,(render-to-lml2 (first body))))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'entity)) body)
  (first body))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'reference-link)) body)
  (bind (((text &optional (id text)) body)
         (link-info (item-at-1 (link-info *current-document*) id)))
    (if link-info
      `((:a :href ,(url link-info) ,@(awhen (title link-info) `(:title ,it)))
        ,text)
      `,text)))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'inline-link)) body)
  (bind (((text  &optional (url "") title) body))
    `((:a :href ,url ,@(awhen title `(:title ,it)))
      ,text)))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'link)) body)
  (bind ((url body))
    `((:a :href ,@url) ,@url)))

;;; ---------------------------------------------------------------------------

(defmethod render-span-to-lml2 ((code (eql 'html)) body)
  (html-encode:encode-for-pre (first body)))



(defun lml2-list->tree (chunks &key (level nil))
  (unless level
    (setf level (or (and (first chunks) (level (first chunks))) 0)))
  
  (labels ((do-it (chunks level)
             
             ;;?? rather inpenetrable... don't understand at the level I should...
             (apply-mark 
              (lml2-marker (first chunks))
              (let (output append? result)
                (loop for rest = chunks then (rest rest) 
                      for chunk = (first rest) then (first rest) 
                      while chunk 
                      for new-level = (level chunk)
                      
                      do (setf (values output append?) (render-to-lml2 chunk))
                      
                       do (format t "~%C(~D/~D): ~A, ~A" level new-level append? chunk)
                      
                      when (and (= level new-level) append?) do
                      (setf result `(,output ,@result))
                      
                      when (and (= level new-level) (not append?)) do
                      (setf result `(,@output ,@result))
                      
                      when (< level new-level) do
                      (multiple-value-bind (block remaining method)
                                           (next-block rest new-level)
                        (let ((inner (do-it block (1+ level))))
                          ; (format t "~%--- ~A" method)
                          (setf rest remaining)
                          (ecase method
                            (:level (if (listp (first result))
                                      (push-end inner (first result))
                                      (push inner result)))
                            (:markup (push inner result))
                            (:none 
                             (setf result `(,inner ,@result))))))
                      
                      when (> level new-level) do 
                      (warn "unexpected chunk level"))
                (reverse result)))))
    (apply #'do-it chunks level)))

;;; ---------------------------------------------------------------------------

(defun apply-mark (mark rest)
  (cond ((null mark) rest)
        ((consp mark) 
         (if (length-1-list-p mark)
           `(,(first mark) ,@(apply-mark (rest mark) rest))
           `(,(first mark) (,@(apply-mark (rest mark) rest)))))
        (t
         (error "unhandled case"))))