#!/usr/bin/env python2

import re,inspect,sys,getopt
from tree_classes import *

scriptname = inspect.getfile(inspect.currentframe())
bison_start = False
debug = False
infilename  = 'parser.ypp'
outfilename = 'tree_gen.h'
lineno = 0
last_class = ""
classes = ClassContainer()

helpmessage = """NAME
	%s - generate AST from Bison syntax

SYNOPSIS
	%s
	%s -i <inputfile> -o <outputfile>

DESCRIPTION
	Generates a header file containing the classes needed to create an Abstract Syntax Tree.

	-h, --help
		Display this help message and exit.

	-i, --infile=
		Indicates input file, defaults to '%s'. Cannot be used without -o.

	-o, --outfile=
		Indicates output file, defaults to '%s'. Cannot be used without -i.

	-d, --debug
		Show debug info during generation.
""" % (scriptname,scriptname,scriptname,infilename,outfilename)

# Check whether a word is valid as property.
def valid_prop(word):
	if word is None: return False
	return word.islower() and word != "empty" and word != "error" and word != r'%prec'

# Find the necessary necessary names and parameters
def proc_line(line):
	global bison_start, lineno, last_class, classes
	lineno += 1
	if re.match(r"%%\W+", line): bison_start = not bison_start
	elif bison_start == True:
		match = re.search(r'(\w*)(?=\s+:)',line)
		ret = None
		if match:
			ret = Class(str(match.group(0)))
			last_class = str(match.group(0))
		else:
			ret = classes.getByName(last_class)
			if ret is None: return
		start_index = 0
		try:
			start_index = line.index("|")
		except ValueError:
			try:
				start_index = line.index(":")
			except ValueError:
				return
		end_index = 0
		try:
			end_index = line.index("{")
		except ValueError:
			raise SyntaxError("Incorrect Bison syntax!\n\nMissing \"{\" in line %d!" % lineno)

		words = [word for word in line[start_index:end_index].split() if valid_prop(word)] # check for the correct words

		for word in words:
			ret.addProperty(word)

		c = Constructor(ret)
		for word in words:
			c.addArg(word)

		ret.addConstructor(c)

		classes.addClass(ret, debug)

def parse(isMain=False):
	global scriptname, bison_start, debug, infilename, outfilename, lineno, last_class, classes
	
	if isMain:
		try:
			opts, args = getopt.getopt(sys.argv[1:],"hi:o:d",["help","infile=","outfile=", "debug"])
		except getopt.GetoptError:
			print helpmessage
			sys.exit(2)
		for opt, arg in opts:
			if opt in ("-h", "--help"):
				print helpmessage
				sys.exit()
			elif opt in ("-i", "--infile"):
				infilename = arg
			elif opt in ("-o", "--outfile"):
				outfilename = arg
			elif opt in ("-d", "--debug"):
				debug = True

	with open(infilename) as infile:
		map(proc_line,infile)

	# nondets = [x for x in nondets if x]

	header_macro = outfilename.replace(".", "_").upper()

	with open(outfilename,'w') as outfile:
		print >> outfile, """#ifndef %s
#define %s

/*
 *	AUTOGENERATED FILE (created by %s)
 *	DO NOT EDIT!
 *
 *	Manual edits will be overwritten next compilation!
 *
 */\n""" % (header_macro,header_macro,scriptname)
		print >> outfile, str(classes)
		print >> outfile, "\n#endif"

	print "Generated %d classes in %s from %s!" % (classes.count(), outfilename, infilename)

if __name__ == '__main__':
	parse(True)