/
decode.cljc
193 lines (153 loc) · 4.91 KB
/
decode.cljc
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
(ns pinkgorilla.encoding.decode
(:require
[clojure.string :as str]
[clojure.edn :as edn]
[instaparse.core :as insta]
[pinkgorilla.encoding.helper :refer [unmake-clojure-comment from-json]]))
(def parse-notebook
"parse-notebook gets passed in a string and returns an intermediary output format.
It is based on InstaParse.
Notes:
SEGMENTS=SEGMENT (<N> SEGMENT)*
This basically decodes one or more segments, that are separated by newline.
LINE = #'.*'
This is an regex that spans maximum one line and can contain one or more
characters of any type.
One of multiple keywords:
keyword = 'cond' | 'defn'
<IN> = #'[a-zA-Z0-9]+[\r\n]'
<OUT> = #'[a-zA-Z0-9]+[\r\n]'
"
(insta/parser
"NOTEBOOK = HEADER SEGMENTS
SEGMENT = MD | CODE
SEGMENTS = SEGMENT (<N> SEGMENT)*
HEADER = <F> VERSION <N> <N>
F = ';; gorilla-repl.fileformat = '
VERSION = #'[1-9]'
N = '\n'
LINE = #'.*'
DATA = #'[.\\w\\d\\s\\-\\+()]'
LINES = LINE (<N> LINE)*
MD = <MD-B> LINES <MD-E>
MD-B = ';; **' N
MD-E = N ';; **' N
CODE = INP CON? VAL?
KERNEL = ' [clj]' | ' [cljs]' | ' [mock]' | ' [meta]'
INP = <INP-B> KERNEL? <N> LINES <INP-E>
INP-B = ';; @@'
INP-E = N ';; @@' N
CON = <CON-B> LINES <CON-E>
CON-B = ';; ->' N
CON-E = N ';; <-' N
VAL = <VAL-B> LINES <VAL-E>
VAL-B = ';; =>' N
VAL-E = N ';; <=' N
"))
(defn get-lines [lines]
(let [;_ (println "lines: " lines)
; lines-with-wrapper (second lines)
rlines (rest lines)
slines (map second rlines)
;_ (println "rlines " rlines)
]
(str/join "\n" slines)))
(defn process-md [seg]
{:type :free
:markup-visible false
:content
{:value (or (unmake-clojure-comment (get-lines seg)) "")
:type "text/x-markdown"}})
(defn is-type [kw data]
(let [t (first data)
;_ (println "checking type: " t)
]
(= t kw)
)
)
(defn find-element [data kw]
(first (filter (partial is-type kw) (rest data))))
(defn kernel-s-to-kw [skernel]
(case skernel
" [clj]" :clj
" [cljs]" :cljs
" [mock]" :mock
" [meta]" :meta
:unknown))
(defn create-code-segment [inp]
;(println "code input is: " inp)
(let [lines (find-element inp :LINES)
kernel (find-element inp :KERNEL)
;_ (println "k is:" kernel)
]
{:type :code
:kernel (if (nil? kernel) :clj (kernel-s-to-kw(second kernel)) )
:content {:value (or (get-lines lines) "")
:type "text/x-clojure"}}))
(defn add-console-response [segment con]
(assoc segment
:console-response
(or (unmake-clojure-comment (get-lines (second con))) "")))
(defn add-value-response [segment val]
(assoc segment
:value-response
(from-json (or (unmake-clojure-comment (get-lines (second val))) ""))))
(defn add-addon [segment addon]
(let [addon-type (first addon)
;_ (println "processing code-addon type: " addon-type)
]
(case addon-type
:CON (add-console-response segment addon)
:VAL (add-value-response segment addon)
(do (println "unknwn code-addon type: " addon-type)
segment))))
(defn process-code [seg]
(let [;_ (println "code is: " seg)
inp (first seg)
segment (create-code-segment inp)
;_ (println "code segment base: " segment)
addons (rest seg)
;_ (println "code segment addons: " addons)
]
(reduce add-addon segment addons)))
(defn process-segment [seg]
(let [seg-with-wrapper (second seg)
type (first seg-with-wrapper)
data (rest seg-with-wrapper)]
;(get-lines (first data))
(case type
:MD (process-md (first data))
:CODE (process-code data)
nil)))
; meta
(defn meta? [segment]
(and (= (:type segment) :code)
(= (:kernel segment) :meta)
))
(defn get-meta [segments]
(let [meta-segment (first (filter meta? segments))]
(if (nil? meta-segment)
{}
(edn/read-string (get-in meta-segment [:content :value]))
)))
(def vector-type
#?(:clj clojure.lang.PersistentVector
:cljs cljs.core/PersistentVector))
(defn decode [s]
(let [nb (parse-notebook s)
;_ (println "parse result type is: " (type nb))
]
; awb99: a case would be good here, however it does not work
; cheshire has condp - but only for cloure, nut we also need cljs
(if (= (type nb) vector-type)
(let [segments (rest (nth nb 2))
segments (vec (map process-segment segments))
segments-no-meta (vec (remove meta? segments)) ]
{:meta (get-meta segments)
:segments segments-no-meta})
(do (when (not (nil? nb))
; ;instaparse.gll.Failure
(println "notebook format is invalid. error:" nb))
nil))))
; (process-segment (first segments))
; (process-segment (nth segments 3))