-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathbasic.lit
More file actions
248 lines (190 loc) · 4.8 KB
/
basic.lit
File metadata and controls
248 lines (190 loc) · 4.8 KB
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# Literate commands
This document will teach you the basic commands of literate, while also acting as a test for all the basic features that are available.
One of the great advantages of literate programming is that information and code are combined into one useful document.
## Including blocks
Literate programming is about decomposing code into blocks and then organizing them in the way that makes most sense for the reader.
The fundamental operation is block inclusion.
This next example is a little more complex than a "hello world"
in order to test multiple inclusion levels.
One way to design programs is to focus on algorithms and data structures.
--- program design
@{data structures}
@{algorithms}
---
But of course we have all this other stuff we have to worry about that the compiler needs.
--- /sample.c
@{includes}
@{program design}
@{tests}
---
For this sample, we will make a linked list:
--- data structures
typedef struct ListNode
{
int val;
struct ListNode *next;
} ListNode;
---
An important algorithm is the length:
--- algorithms
int list_length(ListNode* head)
{
int n = 0;
while (head)
{
++n;
head = head->next;
}
return n;
}
---
Here is a test for the algorithm:
--- test length algorithm
ListNode* c = malloc(sizeof(ListNode));
c->val = 3;
c->next = NULL;
ListNode* b = malloc(sizeof(ListNode));
b->val = 2;
b->next = c;
ListNode* a = malloc(sizeof(ListNode));
a->val = 1;
a->next = b;
printf("%d\n", list_length(a));
---
All of this will run in a C main loop:
--- tests
int main()
{
@{test length algorithm};
}
---
Lastly, there are some boring headers to include.
--- includes
#include <stdio.h>
#include <stdlib.h>
---
We can also reference blocks in prose.
Note that @{algorithms} is `O(n)`.
## Formatting and whitespace
Outputing source code that looks the way you expect is important.
So, let's look at some formatting details.
We can include blocks by name.
Even though we have multiple includes here, it should all be one line,
because the block contents are a single line.
--- /whitespace/multiple-include.c
int array[] = { @{num 1}, @{num 2} };
---
--- num 1
1
---
--- num 2
2
---
If we include a block with multiple lines, it should include all of them.
The included lines should be padded with the whitespace of the include statement.
--- /whitespace/multiline-include.c
int nums[] = {
@{all numbers}
};
---
--- all numbers
1,
2,
3,
4,
5
---
Also, notice that block paths can include directory components
and these will be created automatically.
Blocks can be empty:
--- empty block
---
Files can be empty:
--- /touchfile
---
## Block operators
We can define blocks in parts by appending them together.
For example, every C program starts with a header.
--- /append.c
#include <stdio.h>
---
Then we have a main signature.
--- /append.c +=
int main(int argc, const char* argv[])
---
Then the body:
--- /append.c +=
{
printf("Hello, World!");
return 0;
}
---
Which one does @{/append.c} refer to?
We can also redefine blocks.
One usage of this is to progressively refine a piece of code.
Let's look at a classic function
--- /redefine.c
#include <assert.h>
@{factorial}
int main() {
return (factorial(4) != 24);
}
---
Here is a possible implementation
--- factorial
int factorial(int n)
{
if (n <= 0) return 1;
return n * factorial(n - 1);
}
---
This is pretty hard on the stack, so let's try a loop version:
--- factorial :=
int factorial(int n)
{
int acc = 1;
while (n > 0)
{
acc *= n;
--n;
}
return acc;
}
---
The block above should be using the final definition.
## Modifiers
Notice also that it's not necessary to use a file extension, or
for all the code in a file to be of the same type..
For example `makefile` and other shell scripts can be
authored.
--- /no-extension ---
#!/bin/sh
echo "Hello, World!"
---
Sometimes you have a block that's important for source, but too boring to include in the documentation.
You can use the modifier `noWeave` for these.
Here is one:
--- /hidden.c --- noWeave
int boring_function()
{
return 10;
}
----
But, you will notice it does not appear in the documentation.
References to @{/hidden.c} will not have references.
If you define a block which is never referenced, you will receive a warning.
If this is intended you can use the `noTangle` modifier:
--- /unused.lisp
(defun square (x) (* x x))
---
--- /unused.lisp += --- noTangle
(error "this will not be included")
---
## Escaping the include syntax "@{...}"
In code blocks and prose, the @ sign followed by an open and close brace is typically parsed as a code block reference. You can use a double @ sign to escape from that functionality. When weaving/tangling, the double "@@" gets replaced by a single "@".
--- Escape-includes
(defvar includes-regex "@@{\\(\w+\\)}")
---
--- /escaped.lisp
@{Escape-includes}
---