Show ly.py syntax highlighted
# ly.py -- the lyterate programming thingy ;-)
# http://sf.net/projects/lyterate
# Copyright (c) 2002 Benja Fallenstein
# ATTENTION: This file is autogenerated from several .ly input files.
# Please, DO NOT EDIT this file as your changes will be lost the
# next time it's regenerated from the .ly source. Edit the source
# instead.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# XXX To be documented.
import rfc822
import string
import getopt
import sys
import os
import re
ly_version = '0.0.5pre1'
ly_versions_supported = [ly_version,
'0.0.4a', '0.0.4', '0.0.3', '0.0.2', '0.0.1']
class LiterateProgram:
def readFile(self, file, filename):
msg = rfc822.Message(file)
if msg.has_key('ly-version'):
if msg['ly-version'] not in ly_versions_supported:
print
print "File " + filename + " requests ",
print "Ly version '" + msg['ly-version'] + "',"
print "but the only versions supported are:"
print ly_versions_supported
print
print "Exiting now."
print
sys.exit(1)
lines = file.readlines()
lines = map(string.rstrip, lines)
chunks = splitList(lines, "")
self.readChunks(chunks, filename)
return weave(msg, chunks)
def __init__(self):
self.code = {}
self.filepaths = {}
def readChunks(self, paras, filename):
"""paras -- paragraphs"""
name = None
for para in paras:
if para == []:
# empty paragraph-- ignore
continue
first = para[0]
if first[:2] == "--":
if name != None:
self.chunk_read(name, chunk)
if not re.match(r'-- .*:', first):
print
print "A code chunk name in file "+filename+" does not"
print "have the correct form: it should be two dashes,"
print "a space, the chunk name and a colon. Instead, it is:"
print
print first
print
print "Exiting now."
print
sys.exit(1)
name = first[3:-1]
self.chunk_started(name, filename)
chunk = []
first = para[1]
para = para[1:]
ws = first[:lws_amount(first)]
if first[0] in string.whitespace:
for line in para:
if not line[:len(ws)] == ws:
print
print "I have found a line in one code chunk that"
print "is not indented like the first line in"
print "the code chunk. I'm sorry, but I'm completely"
print "stupid about that: all lines in a code chunk"
print "must be indented at least as much as the"
print "first line in the chunk, and the same mix"
print "of tabs and spaces must be used."
print
print "Here is the chunk in question (in file "+filename+"):"
print
print string.join(para, '\n')
print
print "The offending line is:"
print repr(line)
print
print "Here is the indentation of the first line"
print "in the chunk: " + repr(ws)
print
print "Exiting now."
print
sys.exit(1)
stripws = lambda s, ws=ws: s[len(ws):]
chunk += map(stripws, para)
if name != None:
self.chunk_read(name, chunk)
def chunk_read(self, name, lines):
#print "write: ", name
#print "\n".join(lines)
lines = self.filterInlineChunks(lines)
if not self.code.has_key(name):
self.code[name] = []
self.code[name] += lines
self.code[name] += ['']
def chunk_started(self, chunkname, lyname):
if chunkname[:6] == 'file "':
lydir = os.path.dirname(lyname)
tanglename = chunkname[6:-1]
tangledir = os.path.dirname(tanglename)
if tangledir != '':
# set no dir if the filename contains
# the dir itself
self.filepaths[tanglename] = ''
elif not self.filepaths.has_key(tanglename):
self.filepaths[tanglename] = lydir
else:
if self.filepaths[tanglename] != lydir:
print
print "Two ly files in different ",
print "directories declared the same ",
print "file to be tangled, " + tanglename + "."
print "I don't know whether to put it ",
print "to " + dirname + " or ",
print self.filepaths[tanglename] + "."
print
print "Exiting now."
print
sys.exit(1)
def filterInlineChunks(self, lines):
name = None
indent = None
chunk = None
result = []
for line in lines:
if indent != None:
if line.startswith(indent) and \
line[len(indent)] in string.whitespace:
chunk.append(line[len(indent):])
continue
else:
self.chunk_read(name,
strip_least_indent(chunk))
name = None
indent = None
chunk = None
m = re.match(r'(\s*)-- (.*):', line)
if m == None:
result.append(line)
else:
#print "Inline chunk: "+line
indent = m.groups()[0]
name = check_chunk_name(m.groups()[1])
chunk = []
result.append("%s-- %s." % (indent, name))
if name != None:
self.chunk_read(name, strip_least_indent(chunk))
return result
def tangleFiles(self):
files = {}
for key in self.code.keys():
if key[:6] != 'file "': continue
filename = key[6:-1]
path = self.filepaths[filename]
write_to = os.path.join(path, filename)
lines = self.tangleChunk(key)
files[write_to] = string.join(lines, "\n")
return files
def tangleChunk(self, chunkname):
"""Tangle a chunk into a list of lines."""
if not self.code.has_key('__runPython__ @ ' + chunkname):
if not self.code.has_key(chunkname):
print (
"The program attempted to tangle the chunk '%s',\n"
"but no definition for this chunk was found.\n"
"(Spelling error, maybe?)\n"
"Exiting now.\n"
"\n"
) % (chunkname,)
sys.exit(1)
chunk = self.code[chunkname]
elif not self.code.has_key(chunkname):
chunk = self.tangleActiveChunk(chunkname)
else:
raise TypeError("Both '"+chunkname+"' and "
"'__runPython__ @ "+chunkname+"' "
"are defined!")
result = []
for line in chunk:
stripped = string.lstrip(line)
if stripped[:2] != "--":
result.append(line)
continue
if not re.match(r'-- .*\.', stripped):
print (
"A chunk inclusion in chunk %s does not\n"
"have the correct form: it should be two dashes,\n"
"a space, the chunk name and a period. Instead, it is:\n"
"%s\n"
"Exiting now.\n"
"\n"
) % (chunkname, stripped)
sys.exit(1)
name = stripped[3:-1]
chunk = self.tangleChunk(name)
ws = line[:lws_amount(line)]
indent = lambda s, ws=ws: ws + s
chunk = map(indent, chunk)
result += chunk
parts = [string.strip(part) for part in chunkname.split('@')]
if len(parts) > 1 and parts[0] == 'quote-string':
# preprocess as quoted string
def quote(line):
line = '\\\\'.join(line.split('\\'))
line = '\\"'.join(line.split('"'))
return '"%s\\n"' % (line,)
result = [quote(line) for line in result]
return result
def tangleActiveChunk(self, chunkname):
lines = self.tangleChunk('__runPython__ @ '+chunkname)
indent = lambda s: '\t'+s
lines = map(indent, lines)
code = 'def script(self):\n'
code += string.join(lines, '\n')
code += '\n'
dict = {}
exec code in dict
script = dict['script']
return script(self)
def text_para(header, lines):
str = string.join(lines, "\n")
str = "<p>\n" + str
if ((not header.has_key('easy-tags')) or
string.lower(string.strip(header['easy-tags'])) != 'off'):
str = replace_easy_tags(header, str)
return str
def code_para(header, lines):
import re
defined_chunk_re=re.compile(r"^(--\s+(.+):)(\n|$)")
called_chunk_re=re.compile(r"([ \t]+--\s+)(.+)\.(\n|$)")
str = string.join(lines, "\n")
str = replace(str, "&", "&")
str = replace(str, "<", "<")
str = defined_chunk_re.sub(r'<a name="\2">\1</a>\n',str)
if header.has_key('link-chunks') and \
string.strip(string.lower(header['link-chunks'])) == 'on':
str = called_chunk_re.sub(
r'\1<a href="#\2">\2</a>.\n',str)
return "<p><pre>%s</pre>" % (str,)
def replace(str, what, by_what):
return string.join(string.split(str, what), by_what)
template = """<html>
<head>
<title>%s</title>
</head>
<body>
%s
</body>
</html>"""
def replace_easy_tags(header, str):
entities = {'<<': '<', '>>': '>', '&&': '&',
'~': ' '}
for (char, entity) in entities.items():
str = mask(str, char)
str = replace(str, char, entity)
str = unmask(str, char)
tags = {'|': 'code', '_': 'em', '*': 'strong'}
for (char, tag) in tags.items():
str = mask(str, char)
parts = string.split(str, char)
str = parts[0]
open = 0
for part in parts[1:]:
if not open:
str += "<" + tag + ">"
open = 1
else:
str += "</" + tag + ">"
open = 0
str += part
if open:
raise SyntaxError("Unbalanced easy tag "
+ char + " (" + tag + "):\n" + str)
str = unmask(str, char)
str = mask(str, "[")
str = string.join(string.split(str, '\255'), '\253')
str = string.join(string.split(str, '\254'), r'\\')
str = mask(str, "]")
result = ""
position = string.find(str, "[")
last_position = 0
while position >= 0:
result += str[last_position:position]
start = position
end = string.index(str, "]", position)
delim = string.rindex(str, "->", start, end)
link_text = str[start+1 : delim]
url = str[delim+2 : end]
link_text = string.rstrip(link_text)
url = string.lstrip(url)
result += '<a href="%s">%s</a>' % (url, link_text)
position = end + 1
last_position = position
position = string.find(str, "[", position)
result += str[last_position:]
str = result
str = unmask(str, "]")
str = string.join(string.split(str, '\253'), '\255')
str = unmask(str, "[")
return str
def mask(str, char):
if '\254' in str: raise "\\254 in str: HELP!!!"
if '\255' in str: raise "\\255 in str: HELP!!!"
str = replace(str, '\\\\', '\254')
str = replace(str, '\\' + char, '\255')
str = replace(str, char + char, '\255')
return str
def unmask(str, char):
str = replace(str, '\254', '\\\\')
str = replace(str, '\255', char)
return str
def lws_amount(s):
"""Return the amount of the whitespace at the beginning of a string."""
# tricky
return len(s) - len(string.lstrip(s))
def splitList(list, at):
"""Split a list wherever it contains a specified element."""
result = []
current = []
for el in list:
if el == at:
result.append(current)
current = []
else:
current.append(el)
result.append(current)
return result
def strip_least_indent(lines):
if not lines: return lines
n = lws_amount(lines[0])
for l in lines:
n = min(n, lws_amount(l))
return map(lambda s,n=n:s[n:], lines)
def check_chunk_name(name):
parts = name.split('@')
parts = map(string.strip, parts)
if parts[-1] == '':
import random
parts[-1] = str(random.random())
return ' @ '.join(parts)
pass
def process(filenames):
"""Process a list of .ly files, weaving and tangling.
filenames -- The names of the .ly files, without suffix.
"""
lp = LiterateProgram()
for name in filenames:
base, ext = os.path.splitext(name)
if ext == '.html':
print
print "Filename " + name + " given as an input file. ",
print "If I wrote the woven version of this input "
print "file there (which is a .html, too), the ",
print "input file would be overwritten, so I don't."
print
print "Exiting now."
print
sys.exit(1)
f = open(name)
woven = lp.readFile(f, name)
f.close()
g = open(base + '.html', 'w')
g.write(woven)
g.close()
tangled = lp.tangleFiles()
for (name, contents) in tangled.items():
g = open (name, 'w')
g.write(contents)
g.close()
tangled = lp.tangleFiles()
for (name, contents) in tangled.items():
g = open (name, 'w')
g.write(contents)
g.close()
def read_dir_into(dir, files):
def visit(arg, dirname, names, files=files):
list = map(lambda n,d=dirname: os.path.join(d,n), names)
list = filter(lambda f: os.path.splitext(f)[1] == '.ly',
list)
files.extend(list)
os.path.walk(dir, visit, None)
def weave(header, chunks):
paras = []
for chunk in chunks:
if chunk == []:
continue
first = chunk[0]
if first[:3] == "-- " or first[0] in string.whitespace:
paras.append(code_para(header, chunk))
else:
paras.append(text_para(header, chunk))
title = header["title"]
body = string.join(paras, "\n\n")
return template % (title, body)
if __name__ == '__main__':
options, files = getopt.getopt(sys.argv[1:], [])
if len(files) == 0:
files = ['./']
list = []
for file in files:
if not os.path.isdir(file):
list.append(file)
else:
read_dir_into(file, list)
process(list)
See more files for this project here