/*
   Copyright (C) 2004 T. Scott Dattalo

This file is part of gpsim.

gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <iostream>
#include <iomanip>
#include <map>

#include "command.h"
#include "cmd_macro.h"

extern int parse_string(const char * str);
extern void add_string_to_input_buffer(const char *s,Macro *);
extern void start_new_input_stream(void);
class MacroLine {
  string text;
};

map <const string, Macro *> macro_map;

Macro::Macro(const char *_name)
{
  new_name(_name);

  if(verbose & 4)
    cout << "defining a new macro named: " << name() << endl;

}

//----------------------------------------
//add_argument(char *new_arg)
//
// Add a new argument to the macro. This is called when
// the macro is being defined.

void Macro::add_argument(const char *new_arg)
{
  if(new_arg)
    arguments.push_back(string(new_arg));


  if(verbose & 4) {
    cout << "defining a paramter named: " << new_arg << endl;
  }
}

//----------------------------------------
//add_body(char *new_line)
//
// Add a new line to the macro body. This is called when
// the macro is being defined. These same lines will get
// redirected to the lexer input stream when the macro
// is invoked.

void Macro::add_body(const char *new_line)
{
  if(!new_line)
    return;

  body.push_back(string(new_line));

  if(verbose & 4) {
    cout << "macro body: " << new_line << endl;
  }
}

//----------------------------------------
// invoke()
//
// Invoke a macro by copying its body to the lexer input 
// stream. 
void Macro::invoke()
{
  list <string> :: iterator si;

  start_new_input_stream();

  if(body.size()) {

    for(si = body.begin();
	si != body.end();
	++si)
      add_string_to_input_buffer( (*si).c_str(), this);
  }

  add_string_to_input_buffer("endm\n", this);

}
//----------------------------------------
// substituteParameter(const string &s, string &replaced)
//
// Given a string 's', this function will search to see if
// this is the name of one of the macro parameters. If it
// is, then the text that should replace that parameter is
// is returned in the string 'replaced'.
//
// This routine gets called when the lexer comes across an
// undefined identifier while it is trying to expand a macro.
//

int Macro::substituteParameter(const string &s, string &replaced)
{

  if(arguments.size()) {

    list <string> :: iterator asi;
    list <string> :: iterator psi;

    for(asi = arguments.begin(), psi = parameters.begin();
	asi != arguments.end();
	++asi, ++psi)
      if(*asi == s) {
	
	replaced = *psi;

	if(verbose&4)
	  cout << "Found match, replacing "<<*asi << " with " << *psi<<endl;
	return 1;
      }
  }

  return 0;
}

//----------------------------------------
int Macro::nParameters()
{
  return arguments.size();
}

//----------------------------------------
// prepareForInvocation()
//
// If this macro has been invoked before, then
// there is some historical garbage sitting in the parameter
// list. (Recall, the parameters will substitute the macro
// arguments).

void Macro::prepareForInvocation()
{
  parameters.clear();
}

//----------------------------------------
// add_parameter(char *s)
//
// Add a new macro parameter. This is called when a macro
// is invoked. The parameters are matched up with the macro
// arguments (i.e. the first parameter will substitute the
// first argument).

void Macro::add_parameter(const char *s)
{
  parameters.push_back(string(s));
}

//----------------------------------------
// print()
//
void Macro::print()
{
  cout << name() << " macro ";

  list <string> :: iterator si;

  if(arguments.size()) {

    for(si = arguments.begin();
	si != arguments.end();
	++si)
      cout << *si << " ";

  }

  cout << endl;


  if(body.size()) {

    for(si = body.begin();
	si != body.end();
	++si)
      cout << "  " << *si;

  }

  cout << "endm\n";

}

// hack....
static Macro *theMacro = 0;   // Used during macro definition

Macro *isMacro(const string &s)
{

  map<const string, Macro *>::iterator mi = macro_map.find(s);

  if(mi != macro_map.end())
    return (*mi).second;

  return 0;
}
//========================================================================

cmd_macro c_macro;


static cmd_options cmd_macro_options[] =
{
 {0,0,0}
};

cmd_macro::cmd_macro(void)
{ 
  name = "macro";

    brief_doc = string("macro definition and listing");

    long_doc = string ("\nListing Macros:\n\n"
		       "\tmacro -- display the names of the currently defined macros\n"
		       "\t         (use the symbol command to see a particular macro definition)\n"
		       "\nDefining Macros:\n"
		       "\nname macro [arg1, arg2, ...]\n"
		       "macro body\n"
		       "endm\n\n"
		       "Example:\n\n"
		       "s macro n, regs\n"
		       "echo Step and Show\n"
		       "step n\n"
		       "x regs\n"
		       "endm\n\n"
		       "Invoke by\n\n"
		       "gpsim> s 5, 1:10\n"
		       " (note that the parameters must be separated by commas)\n"
);

  op = cmd_macro_options; 
}


void cmd_macro::list(void)
{
  if(macro_map.size()) {
    map<const string, Macro *>::iterator mi;
    for (mi=macro_map.begin(); mi!=macro_map.end(); ++mi) 

      mi->second->print();
  } else
    cout << "No macros have been defined.\n";
}


void cmd_macro::define(const char *name)
{
  if(!name)
    return;

  map<const string, Macro *>::iterator mi = macro_map.find(string(name));

  if(mi != macro_map.end()) {
    cout << "macro '" << name << "' is already defined\n";
    return;
  }

  theMacro = new Macro(name);

  macro_map[theMacro->name()] = theMacro;
}

void cmd_macro::add_parameter(const char *parameter)
{
  if(!parameter || !theMacro)
    return;

  theMacro->add_argument(parameter);

}

void cmd_macro::add_body(const char *line)
{
  if(!line)
    return;

  theMacro->add_body(line);

}

void cmd_macro::end_define(const char *opt_name)
{
  if(verbose & 4) {
    GetUserInterface().GetConsole().Printf(
      "ending macro definition of '%s'\n", theMacro->name().c_str());
  }
  theMacro = 0;
}


syntax highlighted by Code2HTML, v. 0.9.1