-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.lisp
85 lines (80 loc) · 3.57 KB
/
parser.lisp
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
(in-package :aion.parser)
(defgeneric handle-begin (client block)
(:documentation "handle the beginning of a new block in the iCalendar data"))
(defgeneric handle-end (client block)
(:documentation "handle the ending of a block in the iCalendar data"))
(defgeneric handle-property (client tag params content)
(:documentation "handle a property for the current iCalendar block"))
(defun get-line (stream)
(loop for line = (read-line stream nil)
while line
collect line into results
while (eql #\space (peek-char nil stream nil))
finally (return (when results
(string-right-trim
'(#\newline #\return)
(serapeum:string-replace-all
#1=#.(coerce (list #\return #\space)
'string)
(serapeum:string-join results "")
""))))))
(defmacro with-temporary-keywords ((intern) &body body)
(alexandria:with-gensyms (kw-list)
`(let ((,kw-list '()))
(unwind-protect
(flet ((,intern (inp)
(multiple-value-bind (kw existing?)
(alexandria:make-keyword (string-upcase inp))
(prog1 kw
(unless existing?
(push kw ,kw-list))))))
,@body)
(mapc 'unintern ,kw-list)))))
(defgeneric as-stream (it)
(:method ((it string))
(make-string-input-stream it))
(:method ((it pathname))
(open it))
(:method ((it stream))
it))
(defun process-ics (client file)
(let ((states '()))
(with-temporary-keywords (normalize)
(labels ((%handle-block-delimiter (tag type)
(push type states)
(ecase tag
((:begin) (handle-begin client type))
((:end) (handle-end client type))))
(parse-params (inp)
(destructuring-bind (head params) (fwoar.string-utils:partition #\; inp)
(values head
(when params
(map 'list
(data-lens:• (data-lens:transform-head #'normalize)
(serapeum:op
(fwoar.string-utils:partition #\= _)))
(fwoar.string-utils:split #\; params))))))
(parse-property (it)
(destructuring-bind (s e) (fwoar.string-utils:partition #\: it)
(multiple-value-bind (head params) (parse-params s)
(list (normalize head)
params
e))))
(%handle-property (it)
(apply 'handle-property client it))
(handle-line (tag tagged line)
(case tag
((:begin)
(%handle-block-delimiter tag (normalize tagged)))
((:end)
(%handle-block-delimiter tag (normalize tagged)))
(t (%handle-property (parse-property line))))))
(with-open-stream (s (as-stream file))
(loop for line = (get-line s)
for (tag tagged) = (if line
(fwoar.string-utils:partition #\: line)
'(nil nil))
while line
do (handle-line (fw.lu:may (normalize tag))
tagged
line)))))))