# This software carries the following license:
#
# Modified BSD license
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Steinar Knutsen

"""GuideHTML 1.2

Tool for generating HTML from files with syntax inspired by AmigaGuide.

@TITLE <text>

@EM <text>
@STRONG <text>
@TT <text>

@BR

@LINK <URL> <text>
@NAME <name> <text>

@P <text>
@H <lvl> <text>
@BLOCKQUOTE <text>
@ADDRESS <text>

@DL {
	<text>
		<text>
	...
}
	(In other words, every second element becomes <dt> and <dd>,
	respectively.)

@UL {
	<text>
	<text>
	...
}

@OL {
	<text>
	<text>
	...
}

@TIME <timespec>
	Inserts time of generation. The timespec is sent to strftime().
@CONCAT <text> <text>
	Concatenates the two input elements with no space between them.
@INCLUDE <filename>
	Inserts the contents of named file as a text unit.

{ <text> <text> ... } groups text elements into a new text element.

More or less everything is whitespace and case insensitive. It
is not necessary to write the entire tag names, only enough to identify
a tag unambiguisly is necessary.
"""

import shlex, sys, time, string
from DCLMatcher import DCLMatcher

def strip_apo(word):
	if len(word) >= 2:
		if word[0] == '"' and word[-1] == '"':
			return word[1:-1]
		elif word[0] == "'" and word[-1] == "'":
			return word[1:-1]
		else:
			return word
	else:
		return word
 
class message:
	pass

EndCurly = message()

commenters = ''
wordchars = ("!#$%&()*+,-./0123456789:;<=>?"
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz"
	"|~¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿"
	"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ")

class converter(shlex.shlex):
	def __init__(self, stream=sys.stdin, outstream = sys.stdout, tags = None):
		self.outstream = outstream
		if not tags:
			self.tags = DCLMatcher({
				"TITLE": self.__title,
				"EM": self.__em,
				"STRONG": self.__strong,
				"TT": self.__tt,
				"BR": self.__br,
				"LINK": self.__link,
				"NAME": self.__name,
				"P": self.__p,
				"H": self.__h,
				"BLOCKQUOTE": self.__blockquote,
				"ADDRESS": self.__address,
				"DL": self.__dl,
				"UL": self.__ul,
				"OL": self.__ol,
				"TIME": self.__time,
				"CONCAT": self.__concat,
				"INCLUDE": self.__include
			})
		else:
			self.tags = tags
		shlex.shlex.__init__(self, stream)
		self.wordchars = wordchars
		self.commenters = commenters
	def close(self):
	# Trigs the parsing of the input.
		logical = self.get_logical()
		document = []
		while len(logical) > 0:
			document.append(logical)
			logical = self.get_logical()
		self.outstream.write(string.join(document, ' '))

	def get_logical(self):
		token = self.get_token()
		if token == '{':
			return self.grouper()
		elif token == '@':
			return self.alpha()
		elif token == '}':
			return EndCurly
		else:
			return strip_apo(token)
		
	def alpha(self):
	# Tag look-up and unpleasant stuff like that.
		command = self.get_logical()
		# This will and should explode confronted with unknown tags.
		return self.tags[command]()

	def grouper(self):
	# Token grouping.
		tokens = []
		logical = self.get_logical()
		while logical != EndCurly:
			tokens.append(logical)
			logical = self.get_logical()
		return string.join(tokens, ' ')

	def __title(self):
		return "<title>%s</title>\n" % self.get_logical()
	def __em(self):
		return "<em>%s</em>" % self.get_logical()
	def __strong(self):
		return "<strong>%s</strong>" % self.get_logical()
	def __tt(self):
		return "<tt>%s</tt>" % self.get_logical()
	def __br(self):
		return "<br>"
	def __link(self):
		url = self.get_logical()
		text = self.get_logical()
		return '<a href="%s">%s</a>' % (url, text)
	def __name(self):
		name = self.get_logical()
		text = self.get_logical()
		return '<a name="%s">%s</a>' % (url, text)

	def __p(self):
		return "\n<p>\n%s\n" % self.get_logical()
	def __h(self):
		lvl = self.get_logical()
		header = self.get_logical()
		return "\n<h%s>%s</h%s>\n" % (lvl, header, lvl)
	def __blockquote(self):
		return "\n<blockquote>\n%s\n</blockquote>\n" % self.get_logical()
	def __address(self):
		return "\n<address>\n%s\n</address>\n" % self.get_logical()

	# Variable element number tags, dl, ul, ol.
	def isBeginCurly(self):
		if self.get_token() != '{':
			raise "{ expected at line %s" % self.lineno
	def __dl(self):
		sys.stdout.flush()
		self.isBeginCurly()
		dl = ['\n<dl>']
		item = self.get_logical()
		while item != EndCurly:
			assert item != ''
			definition = self.get_logical()
			dl.append("<dt>%s" % item)
			dl.append("<dd>%s" % definition)
			item = self.get_logical()
		dl.append('</dl>\n')
		return string.join(dl, '\n')
	def __ul(self):
		self.isBeginCurly()
		ul = ['\n<ul>']
		item = self.get_logical()
		while item != EndCurly:
			assert item != ''
			ul.append("<li>%s" % item)
			item = self.get_logical()
		ul.append('</ul>\n')
		return string.join(ul, '\n')
	def __ol(self):
		self.isBeginCurly()
		ol = ['\n<ol>']
		item = self.get_logical()
		while item != EndCurly:
			assert item != ''
			ol.append("<li>%s" % item)
			item = self.get_logical()
		ol.append('</ol>\n')
		return string.join(ol, '\n')

	def __time(self):
		return time.strftime(self.get_logical(), time.localtime(time.time()))
	def __concat(self):
		return self.get_logical() + self.get_logical()
	def __include(self):
		file = open(self.get_logical())
		text = file.read(-1)
		file.close()
		return text

if __name__ == '__main__':
	html = converter()
	html.close()
