//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// main.cc --
//
// Author           : Richard C. Bilson
// Created On       : Fri May 15 23:12:02 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue Jul  5 15:23:11 2016
// Update Count     : 209
//

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstdio>
#include <getopt.h>
#include "Parser/Parser.h"
#include "Parser/ParseNode.h"
#include "Parser/LinkageSpec.h"
#include "SynTree/Declaration.h"
#include "SynTree/Visitor.h"
#include "GenPoly/Lvalue.h"
#include "GenPoly/Specialize.h"
#include "GenPoly/Box.h"
#include "GenPoly/CopyParams.h"
#include "CodeGen/Generate.h"
#include "CodeGen/FixNames.h"
#include "ControlStruct/Mutate.h"
#include "Tuples/Mutate.h"
#include "Tuples/FunctionChecker.h"
#include "SymTab/Mangler.h"
#include "SymTab/Indexer.h"
#include "SymTab/Validate.h"
#include "ResolvExpr/AlternativePrinter.h"
#include "ResolvExpr/Resolver.h"
#include "MakeLibCfa.h"
#include "InitTweak/Mutate.h"
#include "InitTweak/GenInit.h"
#include "InitTweak/FixInit.h"
#include "InitTweak/FixGlobalInit.h"
//#include "Explain/GenProlog.h"
//#include "Try/Visit.h"

#include "Common/SemanticError.h"
#include "Common/UnimplementedError.h"

#include "../config.h"

using namespace std;

#define OPTPRINT(x) \
	if ( errorp ) std::cerr << x << std::endl;

static void parse( FILE * input, LinkageSpec::Type t, bool shouldExit = false );
static void dump( std::list< Declaration * > & translationUnit, std::ostream & out = std::cout );

bool
	astp = false,
	bresolvep = false,
	bboxp = false,
	ctorinitp = false,
	exprp = false,
	expraltp = false,
	grammarp = false,
	libcfap = false,
	nopreludep = false,
	noprotop = false,
	parsep = false,
	resolvep = false,									// used in AlternativeFinder
	symtabp = false,
	treep = false,
	validp = false,
	errorp = false,
	codegenp = false;

enum { Ast, Bbox, Bresolver, CtorInitFix, Expr, ExprAlt, Grammar, LibCFA, Nopreamble, Parse, Prototypes, Resolver, Symbol, Tree, Validate, };

static struct option long_opts[] = {
	{ "ast", no_argument, 0, Ast },
	{ "before-box", no_argument, 0, Bbox },
	{ "before-resolver", no_argument, 0, Bresolver },
	{ "ctorinitfix", no_argument, 0, CtorInitFix },
	{ "expr", no_argument, 0, Expr },
	{ "expralt", no_argument, 0, ExprAlt },
	{ "grammar", no_argument, 0, Grammar },
	{ "libcfa", no_argument, 0, LibCFA },
	{ "no-preamble", no_argument, 0, Nopreamble },
	{ "parse", no_argument, 0, Parse },
	{ "no-prototypes", no_argument, 0, Prototypes },
	{ "resolver", no_argument, 0, Resolver },
	{ "symbol", no_argument, 0, Symbol },
	{ "tree", no_argument, 0, Tree },
	{ "validate", no_argument, 0, Validate },
	{ 0, 0, 0, 0 }
};

int main( int argc, char *argv[] ) {
	FILE *input;
	std::ostream *output = &std::cout;
	int long_index;
	std::list< Declaration * > translationUnit;
	const char *filename = NULL;

	opterr = 0;											// prevent getopt from printing error messages

	int c;
	while ( (c = getopt_long( argc, argv, "abBcefglnpqrstvyzD:F:", long_opts, &long_index )) != -1 ) {
		switch ( c ) {
		  case Ast:
		  case 'a':										// dump AST
			astp = true;
			break;
		  case Bresolver:
		  case 'b':										// print before resolver steps
			bresolvep = true;
			break;
		  case 'B':										// print before resolver steps
			bboxp = true;
			break;
		  case CtorInitFix:
		  case 'c':
			ctorinitp = true;
			break;
		  case Expr:
		  case 'e':										// dump AST after expression analysis
			exprp = true;
			break;
		  case ExprAlt:
		  case 'f':										// print alternatives for expressions
			expraltp = true;
			break;
		  case Grammar:
		  case 'g':										// bison debugging info (grammar rules)
			grammarp = true;
			break;
		  case LibCFA:
		  case 'l':										// generate libcfa.c
			libcfap = true;
			break;
		  case Nopreamble:
		  case 'n':										// do not read preamble
			nopreludep = true;
			break;
		  case Prototypes:
		  case 'p':										// generate prototypes for preamble functions
			noprotop = true;
			break;
		  case Parse:
		  case 'q':										// dump parse tree
			parsep = true;
			break;
		  case Resolver:
		  case 'r':										// print resolver steps
			resolvep = true;
			break;
		  case Symbol:
		  case 's':										// print symbol table events
			symtabp = true;
			break;
		  case Tree:
		  case 't':										// build in tree
			treep = true;
			break;
		  case 'v':										// dump AST after decl validation pass
			validp = true;
			break;
		  case 'y':
			errorp = true;
			break;
		  case 'z':
			codegenp = true;
			break;
		  case 'D':										// ignore -Dxxx
			break;
		  case 'F':										// source file-name without suffix
			filename = optarg;
			break;
		  case '?':
			cout << "Unknown option: '" << (char)optopt << "'" << endl;
			exit( EXIT_FAILURE );
		  default:
			abort();
		} // switch
	} // while

	try {
		// choose to read the program from a file or stdin
		if ( optind < argc ) {
			input = fopen( argv[ optind ], "r" );
			if ( ! input ) {
				std::cout << "Error: can't open " << argv[ optind ] << std::endl;
				exit( EXIT_FAILURE );
			} // if
			// if running cfa-cpp directly, might forget to pass -F option (and really shouldn't have to)
			if ( filename == NULL ) filename = argv[ optind ];
			// prelude filename comes in differently
			if ( libcfap ) filename = "prelude.cf";
			optind += 1;
		} else {
			input = stdin;
			// if running cfa-cpp directly, might forget to pass -F option. Since this takes from stdin, pass
			// a fake name along
			if ( filename == NULL ) filename = "stdin";
		} // if

		if ( optind < argc ) {
			output = new ofstream( argv[ optind ] );
		} // if

		Parser::get_parser().set_debug( grammarp );

		// read in the builtins, extras, and the prelude
		if ( ! nopreludep ) {							// include gcc builtins
			// -l is for initial build ONLY and builtins.cf is not in the lib directory so access it here.
			FILE * builtins = fopen( libcfap | treep ? "builtins.cf" : CFA_LIBDIR "/builtins.cf", "r" );
			if ( builtins == NULL ) {
				std::cerr << "Error: can't open builtins.cf" << std::endl;
				exit( EXIT_FAILURE );
			} // if
			parse( builtins, LinkageSpec::Compiler );

			// read the extra prelude in, if not generating the cfa library
			FILE * extras = fopen( libcfap | treep ? "extras.cf" : CFA_LIBDIR "/extras.cf", "r" );
			if ( extras == NULL ) {
				std::cerr << "Error: can't open extras.cf" << std::endl;
				exit( EXIT_FAILURE );
			} // if
			parse( extras, LinkageSpec::C );

			if ( ! libcfap ) {
				// read the prelude in, if not generating the cfa library
				FILE * prelude = fopen( treep ? "prelude.cf" : CFA_LIBDIR "/prelude.cf", "r" );
				if ( prelude == NULL ) {
					std::cerr << "Error: can't open prelude.cf" << std::endl;
					exit( EXIT_FAILURE );
				} // if

				parse( prelude, LinkageSpec::Intrinsic );
			} // if
		} // if

		parse( input, libcfap ? LinkageSpec::Intrinsic : LinkageSpec::Cforall, grammarp );

		if ( parsep ) {
			Parser::get_parser().get_parseTree()->printList( std::cout );
			Parser::get_parser().freeTree();
			return 0;
		} // if

		buildList( Parser::get_parser().get_parseTree(), translationUnit );

		Parser::get_parser().freeTree();
		if ( astp ) {
			dump( translationUnit );
			return 0;
		} // if

		// add the assignment statement after the initialization of a type parameter
		OPTPRINT( "validate" )
		SymTab::validate( translationUnit, symtabp );
		if ( symtabp ) {
			return 0;
		} // if

		if ( expraltp ) {
			ResolvExpr::AlternativePrinter printer( std::cout );
			acceptAll( translationUnit, printer );
			return 0;
		} // if

		if ( validp ) {
			dump( translationUnit );
			return 0;
		} // if

		OPTPRINT( "mutate" )
		ControlStruct::mutate( translationUnit );
		OPTPRINT( "fixNames" )
		CodeGen::fixNames( translationUnit );
		OPTPRINT( "fixGlobalInit" );
		InitTweak::fixGlobalInit( translationUnit, filename, libcfap || treep );
		OPTPRINT( "tweakInit" )
		InitTweak::genInit( translationUnit );

		if ( libcfap ) {
			// generate the bodies of cfa library functions
			LibCfa::makeLibCfa( translationUnit );
		} // if

		if ( bresolvep ) {
			dump( translationUnit );
			return 0;
		} // if

		OPTPRINT( "resolve" )
		ResolvExpr::resolve( translationUnit );
		if ( exprp ) {
			dump( translationUnit );
			return 0;
		}

		OPTPRINT( "fixInit" )
		// fix ObjectDecl - replaces ConstructorInit nodes
		InitTweak::fix( translationUnit );
		if ( ctorinitp ) {
			dump ( translationUnit );
			return 0;
		}

		OPTPRINT( "copyParams" );
		GenPoly::copyParams( translationUnit );
		OPTPRINT( "convertSpecializations" )
		GenPoly::convertSpecializations( translationUnit );
		OPTPRINT( "convertLvalue" )
		GenPoly::convertLvalue( translationUnit );

		if ( bboxp ) {
			dump( translationUnit );
			return 0;
		}
		OPTPRINT( "box" )
		GenPoly::box( translationUnit );

		// print tree right before code generation
		if ( codegenp ) {
			dump( translationUnit );
			return 0;
		} // if

		CodeGen::generate( translationUnit, *output, ! noprotop );

		if ( output != &std::cout ) {
			delete output;
		} // if
	} catch ( SemanticError &e ) {
		if ( errorp ) {
			std::cerr << "---AST at error:---" << std::endl;
			dump( translationUnit, std::cerr );
			std::cerr << std::endl << "---End of AST, begin error message:---\n" << std::endl;
		}
		e.print( std::cerr );
		if ( output != &std::cout ) {
			delete output;
		} // if
		return 1;
	} catch ( UnimplementedError &e ) {
		std::cout << "Sorry, " << e.get_what() << " is not currently implemented" << std::endl;
		if ( output != &std::cout ) {
			delete output;
		} // if
		return 1;
	} catch ( CompilerError &e ) {
		std::cerr << "Compiler Error: " << e.get_what() << std::endl;
		std::cerr << "(please report bugs to " << std::endl;
		if ( output != &std::cout ) {
			delete output;
		} // if
		return 1;
	} // try

	deleteAll( translationUnit );
	return 0;
} // main

static void parse( FILE * input, LinkageSpec::Type linkage, bool shouldExit ) {
	Parser::get_parser().set_linkage( linkage );
	Parser::get_parser().parse( input );

	fclose( input );
	if ( shouldExit || Parser::get_parser().get_parseStatus() != 0 ) {
		exit( Parser::get_parser().get_parseStatus() );
	} // if
}

static bool notPrelude( Declaration * decl ) {
	return ! LinkageSpec::isBuiltin( decl->get_linkage() );
}

static void dump( std::list< Declaration * > & translationUnit, std::ostream & out ) {
	std::list< Declaration * > decls;
	if ( noprotop ) {
		filter( translationUnit.begin(), translationUnit.end(),
				std::back_inserter( decls ), notPrelude );
	} else {
		decls = translationUnit;
	}

	printAll( decls, out );
	deleteAll( translationUnit );
}


// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End:  //
