anatomy 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ use(require("atomy"))
2
+
3
+ require("set")
4
+
5
+ data(Block(@content, @style = nil)):
6
+ Table(@rows)
7
+ Itemization(@elements)
8
+ NestedFlow(@blocks)
9
+ Paragraph(@content, @closed? = false)
10
+ MetaBlock(@action)
11
+ TraverseBlock(@action)
12
+ ResolveBlock(@action)
13
+ List(@elements):
14
+ OrderedList(@elements)
15
+
16
+ data(Element(@content, @style = nil)):
17
+ Target(@name)
18
+ TOC(@content)
19
+ Reference(@tag, @content = nil)
20
+ Index(@keys, @description)
21
+ TraverseElement(@action)
22
+ ResolveElement(@action)
23
+ CollectElement(@action)
24
+ RenderElement(@action)
25
+
26
+ data(Style(@type = nil, @properties = []))
27
+
28
+ data(Tag(@part, @name, @target = .self, @display = nil))
29
+
30
+ data(
31
+ Part(
32
+ -- nil or title content
33
+ @title = nil
34
+
35
+ -- the part style
36
+ @style = Style new
37
+
38
+ -- initial content before sub-parts
39
+ @body = []
40
+
41
+ -- sub-parts
42
+ @parts = []
43
+
44
+ -- parent part
45
+ @parent = nil
46
+
47
+ -- tags
48
+ -- tag -> reference
49
+ @tags = #{}
50
+ @tag-prefix = nil
51
+
52
+ -- tags that cannot be found by parents
53
+ @local-tags = #{}
54
+
55
+ -- the primary tag used for this Part
56
+ -- used for URIs
57
+ @tag = nil
58
+
59
+ -- content that is inspected during the collect pass,
60
+ -- but doesn't produce output
61
+ @to-collect = []
62
+
63
+ -- dependent assets
64
+ @assets = []
65
+
66
+ -- css files to add
67
+ @css-additions = Set new
68
+
69
+ -- omit children from table of contents
70
+ @omit-children-from-table-of-contents? = false))
71
+
72
+ def(Part set-tag(name, target = .self, display = nil)):
73
+ @tags[name] = Tag new(self, name, target, display)
74
+
75
+ def(Part set-local-tag(name, target = nil, display = nil)):
76
+ @local-tags[name] = Tag new(self, name, target, display)
77
+
78
+ def(Part toc?): style properties include?(.toc)
79
+
80
+ def(Part inspect): i"#<Part: '#{@tag name}'>"
81
+
82
+ -- search in order:
83
+ -- 1. local hidden tags
84
+ -- 2. local tags
85
+ -- 3. children
86
+ -- 1. local tags
87
+ -- 2. children
88
+ -- 4. parent
89
+ -- 1. local hidden tags
90
+ -- 2. tags
91
+ -- 3. siblings, searching in order
92
+ -- 4. parent ...
93
+ def(Part find-tag(tag, excluding = nil)):
94
+ catch(.found):
95
+ search-tag-excluding(tag, nil)
96
+ nil
97
+
98
+ def(Part search-tag-excluding(name, excluding)): do:
99
+ when(found = (@local-tags[name] || @tags[name])):
100
+ throw(.found, found)
101
+
102
+ when(name == @title):
103
+ throw(.found, @tag)
104
+
105
+ @parts each [p]:
106
+ unless(p == excluding):
107
+ p search-tag(name)
108
+
109
+ when(@parent):
110
+ @parent search-tag-excluding(name, self)
111
+
112
+ nil
113
+
114
+ -- called from find-tag; only search exposed tags and children
115
+ def(Part search-tag(name)): do:
116
+ when(name == @title):
117
+ throw(.found, @tag)
118
+
119
+ when(found = @tags[name]):
120
+ throw(.found, found)
121
+
122
+ @parts each [p]:
123
+ p search-tag(name)
124
+
125
+
126
+ def(Part top):
127
+ if(@parent)
128
+ then: @parent top
129
+ else: self
130
+
131
+ def(content?(String)): true
132
+ def(content?(Element)): true
133
+ def(content?(nil)): true
134
+ def(content?(x & Array)): x all? &.content?
135
+ def(content?(_)): false
136
+
137
+ def(sanitize(x)):
138
+ contents-of(x) gsub(r" & ", " and ")
139
+ gsub(r"\s+", "-")
140
+ gsub(r"[^[:alnum:]_\-:.]", "")
141
+ downcase
142
+
143
+ def(contents-of(s & String)): s
144
+ def(contents-of(e & Element)): contents-of(e content)
145
+ def(contents-of(b & Block)): contents-of(b content)
146
+ def(contents-of(a & Array)): a collect [x] { contents-of(x) } join
147
+ def(contents-of(nil)): ""
@@ -0,0 +1,73 @@
1
+ use(require("atomy"))
2
+
3
+ require("cgi")
4
+
5
+
6
+ data(HTMLElement(@name, @attributes = #{}, @body = nil))
7
+
8
+ fn(attr-to-pair(`.~name)): `(.class -> ~(Atomy Code StringLiteral new(name text to-s)))
9
+ fn(attr-to-pair(`#~name)): `(.id -> ~(Atomy Code StringLiteral new(name text to-s)))
10
+ fn(attr-to-pair(`(~x = ~y))): `(.~x -> ~y)
11
+
12
+ macro(<(~name)(~*attrs): ~*body):
13
+ `(HTMLElement new(
14
+ .~name
15
+ #{ ~*(attrs collect [a]: attr-to-pair(a)) }
16
+ [~*body]))
17
+
18
+ macro(<(~name): ~*body):
19
+ `(HTMLElement new(.~name, #{}, [~*body]))
20
+
21
+ macro(<(~name)(~*attrs)):
22
+ `(HTMLElement new(
23
+ .~name
24
+ #{ ~*(attrs collect [a]: attr-to-pair(a)) }
25
+ nil))
26
+
27
+ macro(<~name):
28
+ `(HTMLElement new(.~name))
29
+
30
+ data(UnescapedString(@raw))
31
+
32
+ def(UnescapedString to-s): @raw
33
+
34
+ fn(render(out, x & Array)):
35
+ x collect [y]: render(out, y)
36
+ fn(render(out, x & String)):
37
+ out << CGI escapeHTML(x)
38
+ fn(render(out, x)):
39
+ out << x to-s
40
+
41
+ stripped? = dynamic(false)
42
+
43
+ def(HTMLElement to-s):
44
+ out = ""
45
+
46
+ when(^stripped?):
47
+ render(out, @body)
48
+ return(out)
49
+
50
+ out << i"<#{@name}"
51
+
52
+ @attributes each [k, v]:
53
+ out << i" #{k}=\"#{v}\""
54
+
55
+ unless(@body):
56
+ out << " />"
57
+ return(out)
58
+
59
+ out << ">"
60
+
61
+ render(out, @body)
62
+
63
+ out << i"</#{@name}>"
64
+
65
+ out
66
+
67
+ data(StrippedTags(@body))
68
+
69
+ def(StrippedTags to-s):
70
+ with(stripped? = true):
71
+ out = ""
72
+ render(out, @body)
73
+ out
@@ -0,0 +1,130 @@
1
+ use(require("atomy"))
2
+ use(require("grammar"))
3
+
4
+ parser(Parser):
5
+ %%:
6
+ def(trim-leading(str, n)):
7
+ unless(n > 0):
8
+ return(str)
9
+
10
+ str gsub!(r"\n {0,#{n}}", "\n")
11
+
12
+ def(word(l, x)):
13
+ w = Atomy Grammar AST Word new(x)
14
+ w line = l
15
+ w
16
+
17
+ %atomy = Atomy Grammar
18
+
19
+ rule(line): { current-line }
20
+ rule(column): { current-column }
21
+
22
+ rule(comment): "{-" in-multi
23
+
24
+ rule(in-multi): [
25
+ /"[^\-\{\}]*"/ "-}"
26
+ /"[^\-\{\}]*"/ "{-" in-multi /"[^\-\{\}]*"/ "-}"
27
+ /"[^\-\{\}]*"/ /"[-{}]"/ in-multi
28
+ ]
29
+
30
+ rule(content(s)): comment? c=(chunk(s) | escaped) comment? { c }
31
+
32
+ rule(insignificant): (<(/"[^\\\{\}]+"/)> | "\\" <(/"[\\\{\}]"/)>) {
33
+ text
34
+ }
35
+
36
+ rule(chunk(s)): [
37
+ l=(line) chunk=(insignificant+) c=((&"}" | comment)?) {
38
+ text = chunk join
39
+
40
+ when(c):
41
+ text rstrip!
42
+
43
+ trim-leading(text, s)
44
+
45
+ Atomy Grammar AST StringLiteral new(text)
46
+ }
47
+
48
+ nested
49
+ ]
50
+
51
+ rule(escaped): [
52
+ l=(line) "\\" n=(%atomy(identifier)) as=(argument+) {
53
+ `((~word(l, n))(~*as))
54
+ }
55
+
56
+ l=(line) "\\" n=(%atomy(identifier)) { word(l, n) }
57
+
58
+ l=(line) "\\" "(" e=(%atomy(expression)) ")" { e }
59
+ ]
60
+
61
+ rule(leading): [
62
+ &(/"\n+"/ b=(column) /"\s+"/ a=(column)) { a - b }
63
+ { 0 }
64
+ ]
65
+
66
+ rule(nested):
67
+ l=(line) "{" s=(leading) cs=(content(s)*) "}" {
68
+ when(cs[0] is-a?(Atomy Grammar AST StringLiteral)):
69
+ cs[0] value sub!(r"^\n", "")
70
+
71
+ cs match:
72
+ []: Atomy Grammar AST StringLiteral new("")
73
+ [x]: x
74
+ _: `[~*cs]
75
+ }
76
+
77
+ rule(atomy): "[" e=(%atomy(expression)) "]" { e }
78
+
79
+ rule(argument): nested | atomy
80
+
81
+ rule(root):
82
+ l=(line) cs=(content(0)*) !_ {
83
+ setup = Array[]
84
+ definitions = Array[]
85
+
86
+ filter = [c]:
87
+ c match:
88
+ (`use(~_) | `require(~_)):
89
+ setup << c
90
+ 'nil
91
+
92
+ `(def(~x): ~*y):
93
+ definitions << c
94
+ 'nil
95
+
96
+ `let(~x, ~y):
97
+ definitions << `(~x = ~y)
98
+ 'nil
99
+
100
+ `[~*cs]:
101
+ `[~*(cs map &filter)]
102
+
103
+ _:
104
+ c
105
+
106
+ body = cs map &filter
107
+
108
+ Atomy Grammar AST Sequence new([
109
+ 'use(require("atomy"))
110
+ 'use(require("anatomy/base"))
111
+
112
+ `(do:
113
+ ~*setup
114
+ ~*definitions
115
+ def(doc): decode(~*body))
116
+ ])
117
+ }
118
+
119
+
120
+ Parser singleton:
121
+ def(parse-string(str)):
122
+ p = new(str)
123
+ unless(p parse):
124
+ p raise-error
125
+
126
+ p result
127
+
128
+ def(parse-file(path)):
129
+ File open(path, "r") [f]:
130
+ parse-string(f read)
@@ -0,0 +1,35 @@
1
+ use(require("atomy"))
2
+
3
+ require("atomy/codeloader")
4
+
5
+ pretty = require("pretty")
6
+
7
+ require("fileutils")
8
+
9
+ traverse = require("anatomy/stages/traverse")
10
+ collect = require("anatomy/stages/collect")
11
+ resolve = require("anatomy/stages/resolve")
12
+ html = require("anatomy/renderers/html")
13
+
14
+ def(process(input, renderer, output = ".")):
15
+ input = File expand-path(input)
16
+ output = File expand-path(output)
17
+
18
+ mod = Atomy CodeLoader require(input)
19
+
20
+ part = mod doc
21
+ traversed = traverse over(part)
22
+ collected = collect over(traversed)
23
+ resolved = resolve over(collected)
24
+
25
+ unless(Dir exists?(output)):
26
+ Dir mkdir(output)
27
+
28
+ unless(Dir exists?(output + "/public")):
29
+ Dir mkdir(output + "/public")
30
+
31
+ FileUtils cp-r(
32
+ File expand-path("../../../public", __FILE__) + "/."
33
+ output + "/public")
34
+
35
+ renderer render(resolved, output)
@@ -0,0 +1,306 @@
1
+ use(require("atomy"))
2
+ use(require("io"))
3
+ use(require("dynamic"))
4
+
5
+ require("json")
6
+ require("fileutils")
7
+
8
+ use(require("anatomy/html"))
9
+ data = require("anatomy/data")
10
+
11
+
12
+ fn(tag-url(t)):
13
+ t target match:
14
+ .anchor(x):
15
+ url(t part, x)
16
+
17
+ .self:
18
+ url(t part)
19
+
20
+ .url(x):
21
+ x
22
+
23
+ def(over(i & data Itemization)):
24
+ i elements collect [name, body]:
25
+ <dl:
26
+ [ <dt: over(name)
27
+ <dd: over(body)
28
+ ]
29
+ def(over(l & data List)):
30
+ <ul:
31
+ l elements collect [body]:
32
+ <li: over(body)
33
+ def(over(l & data OrderedList)):
34
+ <ol:
35
+ l elements collect [body]:
36
+ <li: over(body)
37
+ def(over(p & data Paragraph)):
38
+ <p: over(p content)
39
+ def(over(b & data Block)):
40
+ b style match:
41
+ .tt:
42
+ <pre: over(b content)
43
+
44
+ .verbatim:
45
+ <pre(.verbatim): over(b content)
46
+
47
+ .class(*classes):
48
+ <div(class = classes join(" ")): over(b content)
49
+
50
+ .header(depth):
51
+ <"h#{depth}": over(b content)
52
+
53
+ .inset:
54
+ <div(style = "margin: 0 2em 1em"): over(b content)
55
+
56
+ .centered:
57
+ <div(style = "text-align: center"): over(b content)
58
+
59
+ .margin-note:
60
+ <blockquote: over(b content)
61
+
62
+ nil:
63
+ <div: over(b content)
64
+
65
+ in-reference? = dynamic(false)
66
+
67
+ def(over(t & data Target)):
68
+ <a(name = t name): nil
69
+ def(over(p & data Reference)):
70
+ if(p tag)
71
+ then:
72
+ <a(href = tag-url(p tag)):
73
+ with(in-reference? = true):
74
+ over(p content)
75
+ else:
76
+ <a(.undefined): over(p content)
77
+ def(over(r & data ResolveElement)):
78
+ error(.did-not-resolve(r))
79
+ def(over(e & data Element)):
80
+ e style match:
81
+ .italic:
82
+ <em: over(e content)
83
+
84
+ .bold:
85
+ <strong: over(e content)
86
+
87
+ .tt:
88
+ <code: over(e content)
89
+
90
+ .superscript:
91
+ <sup: over(e content)
92
+
93
+ .subscript:
94
+ <sub: over(e content)
95
+
96
+ .smaller:
97
+ <span(style = "font-size: 80%"): over(e content)
98
+
99
+ .larger:
100
+ <span(style = "font-size: 120%"): over(e content)
101
+
102
+ .strike:
103
+ <span(style = "text-decoration: line-through"): over(e content)
104
+
105
+ .class(*classes):
106
+ <span(class = classes join(" ")): over(e content)
107
+
108
+ .hyperlink(url):
109
+ <a(href = url): over(e content)
110
+
111
+ .image(file):
112
+ <img(src = file, alt = over(e content))
113
+
114
+ .svg(file):
115
+ <object(data = file, type = "image/svg+xml", width = "100%", height = "100%"):
116
+ <param(name = "src", value = file)
117
+
118
+ .aux:
119
+ unless(^(in-reference?)):
120
+ over(e content)
121
+
122
+ def(over(nil)): nil
123
+ def(over(s & String)): s
124
+ def(over(a & Array)):
125
+ a collect [x]: over(x)
126
+
127
+
128
+ def(filename(p)): p tag name + ".html"
129
+
130
+
131
+ def(top(p)):
132
+ condition:
133
+ (p parent && p parent toc?):
134
+ p
135
+
136
+ p parent:
137
+ top(p parent)
138
+
139
+ otherwise:
140
+ p
141
+
142
+ def(url(p, anchor = nil)):
143
+ condition:
144
+ (top(p) != p):
145
+ (url(top(p)) + "#") + (anchor || p tag name)
146
+
147
+ anchor:
148
+ (filename(p) + "#") + anchor
149
+
150
+ otherwise:
151
+ filename(p)
152
+
153
+
154
+ def(toc-leaf(part)):
155
+ <li:
156
+ <a(href = url(part)):
157
+ over(part title)
158
+
159
+ unless(part omit-children-from-table-of-contents? || part parts empty?):
160
+ <ol:
161
+ part parts collect [p]: toc-leaf(p) --&.(Self toc-leaf(_))
162
+
163
+
164
+ def(render(part, out = ".")):
165
+ unless(part parent):
166
+ tags = search-tags(part) collect [t, d, u]:
167
+ [t, d to-s, u]
168
+
169
+ File open(i"#{out}/public/tags.js", "w") [f]:
170
+ f write(i"var SEARCH_TAGS = #{tags to-json};")
171
+
172
+ class =
173
+ if(part style properties empty?)
174
+ then: "normal"
175
+ else: part style properties collect &.to-s join(" ")
176
+
177
+ File open(i"#{out}/#{filename(part)}", "w") [f]:
178
+ f write("<!DOCTYPE html>")
179
+ f write(
180
+ <html {
181
+ <head:
182
+ <meta("http-equiv" = "Content-Type", content = "text/html; charset=UTF-8")
183
+ <title: StrippedTags new(over(part title))
184
+ <link(type = "text/css", rel = "stylesheet", href = "public/anatomy.css")
185
+ <link(type = "text/css", rel = "stylesheet", href = "public/highlight.css")
186
+ <script(type = "text/javascript", src = "public/jquery.js") {}
187
+ <script(type = "text/javascript", src = "public/jquery.hotkeys.js") {}
188
+ <script(type = "text/javascript", src = "public/tags.js") {}
189
+ <script(type = "text/javascript", src = "public/main.js") {}
190
+
191
+ all-css-additions(part) collect [addition]:
192
+ <link(type = "text/css", rel = "stylesheet", href = File basename(addition))
193
+
194
+ <body(class = class):
195
+ <div(#main):
196
+ <div(#content):
197
+ render-part(part, out)
198
+
199
+ <div(.search):
200
+ <form(action = "javascript:void(0)"):
201
+ <input(
202
+ type = "text"
203
+ placeholder = "Search&hellip;"
204
+ autocomplete = "off"
205
+ #search)
206
+
207
+ <ul(.search-results) {}
208
+
209
+ <div(#sidebar):
210
+ unless(part parts empty?):
211
+ [ <h4: "On this page:"
212
+
213
+ <ol(.toc):
214
+ part parts collect [s]: toc-leaf(s)
215
+ ]
216
+
217
+ when(part parent):
218
+ [ <h4: "Up one level:"
219
+ <ol(.toc):
220
+ toc-leaf(part parent)
221
+ ]
222
+
223
+ when(analytics-id = ENV["ANALYTICS_ID"]):
224
+ <script: UnescapedString new(i"
225
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
226
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
227
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
228
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
229
+
230
+ ga('create', '#{analytics-id}', 'auto');
231
+ ga('send', 'pageview');
232
+ ")
233
+ })
234
+
235
+ def(render-part(part, out = ".", depth = 1)):
236
+ part assets each [a]:
237
+ FileUtils cp(a, out)
238
+
239
+ part css-additions each [a]:
240
+ FileUtils cp(a, out)
241
+
242
+ <div(.section, id = i"section_#{part tag name}"):
243
+ <"h#{depth}"(.section_header):
244
+ <a(name = part tag name) {}
245
+ over(part title)
246
+
247
+ over(part body)
248
+
249
+ if(part toc?)
250
+ then:
251
+ <ol(.toc, #table-of-contents):
252
+ part parts collect [sub]:
253
+ render(sub, out)
254
+ toc-leaf(sub)
255
+ else:
256
+ part parts collect [sub]:
257
+ render-part(sub, out, depth + 1)
258
+
259
+
260
+ def(search-tags(p)):
261
+ tags = [
262
+ [ data contents-of(p title)
263
+ <span(.title):
264
+ <a(href = url(p)):
265
+ over(p title)
266
+ url(p)
267
+ ]
268
+ ]
269
+
270
+ p tags each-key [tag-name]:
271
+ tag = p find-tag(tag-name)
272
+ url = tag-url(tag)
273
+ tags <<
274
+ [ tag name
275
+ (tag display && over(tag display)) ||
276
+ <span(.tag):
277
+ <code:
278
+ <a(href = url):
279
+ tag name
280
+ url
281
+ ]
282
+
283
+ p parts each [sub]:
284
+ tags += search-tags(sub) collect [t, d, u]:
285
+ [ t
286
+ if(d is-a?(HTMLElement) && (d attributes[.class] == "with_parent"))
287
+ then: d
288
+ else:
289
+ <span(.with-parent):
290
+ d
291
+ <span(.parent):
292
+ " in "
293
+ <a(href = url(p)):
294
+ over(p title)
295
+ u
296
+ ]
297
+
298
+ tags
299
+
300
+ def(all-css-additions(p)):
301
+ assets = p css-additions to-a
302
+
303
+ p parts each [sub]:
304
+ assets += all-css-additions(sub)
305
+
306
+ assets