#!/usr/bin/python

#---------------------- faust2md -----------------------
# Usage: `faust2md [-t 4] [-c] [-f] foo.dsp > foo.md`
#
# Ultra simple automatic documentation system for Faust.
# Creates a markdown file by extracting the comments from
# a faust file. The option -t n can be used to change the
# default (4) tab setting. The option -c can be used to
# include the faust code itself into the generated doc.
# And the option -f can be used to include a YAML front
# matter with the name of the file and the date.
#
# The format of a comment is :
#	//------------ foo(x,y) ----------------
#	//  markdown text....
#	//  markdown text....
#	//---------------------------------------
# everything else is considered faust code.
# The translation is the following:
#   # foo(x,y)
#	markdown text....
#	markdown text....
#--------------------------------------------------------


import sys, re, datetime, string, getopt

# Outdent a comment line by n characters in
# order to remove the prefix "//   "
def outdent(line, n):
	if len(line) <= n:
		return "\n"
	else:
		return line[n:]

# Match the first line of a comment
# of type "//--- foo(x,y) ----"
# at least 3 - are needed
def matchBeginComment(line):
	return re.search(r'^\s*//-{3,}\s*([^-]+)-{3,}', line)

# Match the last line of a comment
# of type "//-----------------"
# or a blank line
def matchEndComment(line):
	return re.search(r'^\s*((//-{3,})|(\s*))$', line)

# Compute the indentation of a line,
# that is the position of the first word character
# after "//   "
def indentation(line):
	match = re.search(r'(^\s*//\s*\w)', line)
	if match:
		return len(match.group(1))-1
	else:
		return 0

# Indicates if a line is a comment
def isComment(line):
	match = re.search(r'^\s*//', line)
	if match:
		return 1
	else:
		return 0

# Measure the indentation of a md-comment line
# that is the len of the prefix '//   '
def indentation(line):
	match = re.search(r'(^\s*//\s*\w)', line)
	if match:
		return len(match.group(1))-1
	else:
		return 0

# Print the front matter of the file
def frontMatter(file):
	print '---'
	print 'file:', file
	print 'date:', datetime.date.today()
	print '---'
	print ''

#
# THE PROGRAM STARTS HERE
#

tabsize 	= 4		# tabsize used for expanding tabs
codeflag	= 0		# 0: no source code; 1: print also source code
frontflag	= 0		# 0: no front matter; 1: print front matter
mode 		= 0		# 0: in code; 1: in md-comment
idt 		= 0		# indentation retained to outdent comment lines

# Analyze command line arguments
try:
	opts, args = getopt.getopt(sys.argv[1:], "t:cf")
	if not args:
		raise getopt.error, "At least one file argument required"
except getopt.error, msg:
	print msg
	print "usage:", sys.argv[0], "[-t tabsize] [-c] [-f] file ..."
	sys.exit(1)

for optname, optvalue in opts:
	if optname == '-t':
		tabsize = int(optvalue)
	if optname == '-c':
		codeflag = 1
	if optname == '-f':
		frontflag = 1

# Process all the files and print the documentation on the standard output
for file in args:
	with open(file) as f:
		if frontflag: frontMatter(file)
		for text in f:
			line = string.expandtabs(text, tabsize)
			if isComment(line)==0:
				if mode==1:
					# we are closing a md-comment
					print ''
					mode = 0
				if codeflag:
					print '\t',line,
			else:
				if mode==0:	# we are in code
					match = matchBeginComment(line)
					if match:
						mode=1	# we just started a md-comment
						idt = 0	# we have to measure the indentation
						print ''
						print '#', match.group(1)
					else:
						# it is a comment but not a md-comment
						# therefore it is part of the code
						if codeflag:
							print '\t',line,
				else:
					# we are in a md-comment
					if idt==0:
						# we have to measure the indentation
						idt = indentation(line)
					# check end of md-comment
					match = matchEndComment(line)
					if match:
						# end of md-comment switch back to mode O
						mode = 0
					else:
						# lien of content of md-comment
						# we print it unindented
						print outdent(line,idt),
