-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathnoweb.py.txt
executable file
·226 lines (116 loc) · 5.58 KB
/
noweb.py.txt
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
This executable document first appeared as a blog post on
http://jonaquino.blogspot.com/2010/04/nowebpy-or-worlds-first-executable-blog.html
I have recently been interested in the old idea of
[literate programming](http://en.wikipedia.org/wiki/Literate_programming).
Basically, you have a document that describes in detail how a program works, and
it has embedded chunks of code. It allows you to see the thoughts of the programmer
as he explains how he writes the program using prose. A tool is provided that you
can use to extract the working program from chunks of code in the document.
Here's the thing: *what you are reading right now is a literate program*.
Yes, you can copy this blog post into a file and feed it into the tool, and it
will spit out a program. Q: Where do I get the tool? A: That's the program that
this document spits out. This document will produce a script that you can use to
extract code from [noweb](http://en.wikipedia.org/wiki/Noweb)-format literate programs.
Why do we need to make a new tool if the [noweb](http://en.wikipedia.org/wiki/Noweb)
tool already exists? Because the noweb tool is hard to install. It's not super-hard,
but most people don't want to spend time trying to compile it from source. There
are Windows binaries but you have to [get](http://web.archive.org/web/*/http://www.literateprogramming.com/noweb/nowebinstall.html)
them from the Wayback Machine.
Anyway, the noweb tool doesn't seem to do very much, so why not write a little
script to emulate it?
And that is what we will do now.
# DOWNLOAD
If you are just interested in the noweb.py script produced by this document,
you can [download](http://github.com/JonathanAquino/noweb.py/raw/master/noweb.py) it from GitHub.
# USAGE
The end goal is to produce a Python script that will take a literate program
as input (noweb format) and extract code from it as output. For example,
noweb.py -Rhello.php hello.noweb > hello.php
This will read in a file called hello.noweb and extract the code labelled "hello.php".
We redirect the output into a hello.php file.
# READING IN THE FILE
In a literate program, there are named chunks of code interspersed throughout
the document. Take the chunk of code below. The name of it is "Reading in the file".
The chunk ends with an @ sign.
Let's start by reading in the file given on the command line. We'll build up
a map called "chunks", which will contain the chunk names and the lines of each chunk.
<<Reading in the file>>=
file = open(filename)
chunkName = None
chunks = {}
OPEN = "<<"
CLOSE = ">>"
for line in file:
match = re.match(OPEN + "([^>]+)" + CLOSE + "=", line)
if match:
chunkName = match.group(1)
# If chunkName exists in chunks, then we'll just add to the existing chunk.
if not chunkName in chunks:
chunks[chunkName] = []
else:
match = re.match("@", line)
if match:
chunkName = None
elif chunkName:
chunks[chunkName].append(line)
@
# PARSING THE COMMAND-LINE ARGUMENTS
Now that we have a map of chunk names to the lines of each chunk, we need to know
which chunk name the user has asked to extract. In other words, we need to parse
the command-line arguments given to the script:
noweb.py -Rhello.php hello.noweb
For simplicity, we'll assume that there are always two command-line arguments:
in this example, "-Rhello.php" and "hello.noweb". So let's grab those.
<<Parsing the command-line arguments>>=
filename = sys.argv[-1]
outputChunkName = sys.argv[-2][2:]
@
# RECURSIVELY EXPANDING THE OUTPUT CHUNK
So far, so good. Now we need a recursive function to expand any chunks found
in the output chunk requested by the user. Take a deep breath.
<<Recursively expanding the output chunk>>=
def expand(chunkName, indent):
chunkLines = chunks[chunkName]
expandedChunkLines = []
for line in chunkLines:
match = re.match("(\s*)" + OPEN + "([^>]+)" + CLOSE + "\s*$", line)
if match:
expandedChunkLines.extend(expand(match.group(2), indent + match.group(1)))
else:
expandedChunkLines.append(indent + line)
return expandedChunkLines
@
# OUTPUTTING THE CHUNKS
The last step is easy. We just call the recursive function and output the result.
<<Outputting the chunks>>=
for line in expand(outputChunkName, ""):
print line,
@
And we're done. We now have a tool to extract code from a literate programming document.
Try it on this blog post!
# APPENDIX I: GENERATING THE SCRIPT
To generate noweb.py from this document, you first need a tool to extract the
code from it. You can use the original [noweb](http://www.cs.tufts.edu/~nr/noweb/)
tool, but that's a bit cumbersome to install, so it's easier to use the
Python script [noweb.py](http://github.com/JonathanAquino/noweb.py/raw/master/noweb.py).
Then you can generate noweb.py from noweb.py.html as follows:
noweb.py -Rnoweb.py noweb.py.txt > noweb.py
# APPENDIX II: SUMMARY OF THE PROGRAM
Here's how the pieces we have discussed fit together:
<<noweb.py>>=
#! /usr/local/bin/python
#
# noweb.py
# By Jonathan Aquino ([email protected])
#
# This program extracts code from a literate programming document in "noweb" format.
# It was generated from noweb.py.txt, itself a literate programming document.
# For more information, including the original source code and documentation,
# see http://jonaquino.blogspot.com/2010/04/nowebpy-or-worlds-first-executable-blog.html
#
import sys, re
<<Parsing the command-line arguments>>
<<Reading in the file>>
<<Recursively expanding the output chunk>>
<<Outputting the chunks>>
@