#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Literasc, a simple literate programming tool for C, C++, and Turtle. # Copyright 2012 David Robillard # # Unlike many LP tools, this tool uses normal source code as input, there is no # tangle/weave and no special file format. The literate parts of the program # are written in comments, which are emitted as paragraphs of regular text # interleaved with code. Asciidoc is both the comment and output syntax. import os import re import sys def format_text(text): "Format a text (comment) fragment and return it as a marked up string" return "\n\n" + re.sub("\n *", "\n", text.strip()) + "\n\n" def format_code(lang, code): if code.strip() == "": return code head = "[source,%s]" % lang sep = "-" * len(head) + "\n" return head + "\n" + sep + code.strip("\n") + "\n" + sep def format_c_source(filename, file): output = "=== %s ===\n" % os.path.basename(filename) chunk = "" prev_c = 0 in_comment = False in_comment_start = False n_stars = 0 code = "" for line in file: code += line # Skip initial license comment if code[0:2] == "/*": code = code[code.find("*/") + 2 :] for c in code: if prev_c == "/" and c == "*": in_comment_start = True n_stars = 1 elif in_comment_start: if c == "*": n_stars += 1 else: if n_stars > 1: output += format_code("c", chunk[0 : len(chunk) - 1]) chunk = "" in_comment = True else: chunk += "*" + c in_comment_start = False elif in_comment and prev_c == "*" and c == "/": if n_stars > 1: output += format_text(chunk[0 : len(chunk) - 1]) else: output += format_code( "c", "/* " + chunk[0 : len(chunk) - 1] + "*/" ) in_comment = False in_comment_start = False chunk = "" elif in_comment_start and c == "*": n_stars += 1 else: chunk += c prev_c = c return output + format_code("c", chunk) def format_ttl_source(filename, file): output = "=== %s ===\n" % os.path.basename(filename) in_comment = False chunk = "" for line in file: is_comment = line.strip().startswith("#") if in_comment: if is_comment: chunk += line.strip().lstrip("# ") + " \n" else: output += format_text(chunk) in_comment = False chunk = line else: if is_comment: output += format_code("turtle", chunk) in_comment = True chunk = line.strip().lstrip("# ") + " \n" else: chunk += line if in_comment: return output + format_text(chunk) else: return output + format_code("turtle", chunk) def gen(out, filenames): for filename in filenames: file = open(filename) if not file: sys.stderr.write("Failed to open file %s\n" % filename) continue if filename.endswith(".c") or filename.endswith(".h"): out.write(format_c_source(filename, file)) elif filename.endswith(".ttl") or filename.endswith(".ttl.in"): out.write(format_ttl_source(filename, file)) elif filename.endswith(".txt"): for line in file: out.write(line) out.write("\n") else: sys.stderr.write( "Unknown source format `%s'" % (filename[filename.find(".") :]) ) file.close() if __name__ == "__main__": if len(sys.argv) < 2: sys.stderr.write("Usage: %s FILENAME...\n" % sys.argv[1]) sys.exit(1) gen(sys.argv[1:])