// cdecl.gr
// demonstration of resolving C ident/type ambiguity
// cdecl solves it using reduction cancellation (cf. cdecl2)

verbatim [

#include <iostream.h>      // cout
#include "trace.h"         // trace
#include "locstr.h"        // LocString
#include "strhash.h"       // StringHash
#include "objlist.h"       // ObjList
#include "parssppt.h"      // ParseTreeAndTokens

#define D(msg) \
  trace("cdecl") << msg << endl
#define R0 return 0

]

// parsing action state
context_class ParseEnv : public UserActions {
public:
  StringRef intType;              // "int"
  ObjList<StringHash> types;      // stack of hashes which identify names of types

public:
  ParseEnv(StringTable &table);
  ~ParseEnv();

  void enterScope();
  void leaveScope();
  void addType(StringRef type);
  bool isType(StringRef name);
};


impl_verbatim [

// bit of a hack..
ParseTables *tables;

class CCLang;
UserActions *makeUserActions(StringTable &table, CCLang &lang)
{
  ParseEnv *ret = new ParseEnv(table);
  tables = ret->makeTables();
  return ret;
}

ParseTables *make_ParseEnv_tables();
ParseTables *makeParseTables()
{
  return tables;
}


ParseEnv::ParseEnv(StringTable &table)
{
  intType = table.add("int");
}

ParseEnv::~ParseEnv()
{}


static char const *identity(void *data)
{
  return (char const*)data;
}

void ParseEnv::enterScope()
{
  types.prepend(new StringHash(identity));
}

void ParseEnv::leaveScope()
{
  delete types.removeAt(0);
}

void ParseEnv::addType(StringRef type)
{
  StringHash *h = types.first();
  if (h->get(type)) {
    cout << "duplicate entry for " << type << " -- will ignore\n";
  }
  else {
    h->add(type, (void*)type);
  }
}

bool ParseEnv::isType(StringRef name)
{
  if (name == intType) {
    return true;
  }

  FOREACH_OBJLIST(StringHash, types, iter) {
    if (iter.data()->get(name)) {
      return true;
    }
  }
  return false;
}


#if 0
void doit(int argc, char **argv)
{
  traceAddSys("progress");
  //traceAddSys("parse-tree");

  SemanticValue treeTop;
  ParseTreeAndTokens tree(treeTop);
  ParseEnv env(tree);
  treeMain(tree, argc, argv);

  cout << "final parse result: " << treeTop << endl;
}

int main(int argc, char **argv)
{
  doit(argc, argv);

  //malloc_stats();

  return 0;
}
#endif // 0

] // end impl_verbatim


terminals {
  // grab the lexer's token list
  include("../../c/c.tok")

  token[StringRef] L2_NAME {
    fun dup(s) [ return s; ]
    fun del()  []
  }
}


nonterm[int] Start -> Stmts  [
  D("exiting scope");
  leaveScope();
  R0;
]

nonterm[int] Stmts {
  -> empty         [ D("entering scope"); enterScope(); R0; ]
  -> Stmts Stmt    [ R0; ]
}

nonterm[int] Stmt {
  -> t:TypeName "(" n:VarName ")" ";"   [    // declaration
       D("declaration of " << n << ", type " << t); R0;
     ]

  -> f:VarName "(" a:VarName ")" ";"    [    // function call
       D("call of " << f << " with arg " << a); R0;
     ]

  -> "typedef" t:TypeName n:L2_NAME ";" [    // typedef: introduce type name
       D("typedef of " << n << " as type " << t);
       addType(n);
       R0;
     ]

  -> "{" Stmts "}"                      [    // scope
       D("exiting scope"); leaveScope(); R0;
     ]
}

nonterm[StringRef] VarName {
  fun keep(n)      [ return !isType(n); ]

  -> n:L2_NAME     [ return n; ]
}

nonterm[StringRef] TypeName {
  fun keep(n)      [ return isType(n); ]

  -> n:L2_NAME     [ return n; ]

  // this is a little bit of a hack, but in some sense it's valid
  // to regard the global strings as another stringtable.. of course,
  // equality won't work, but I don't need it for the moment
  -> L2_INT        [ return intType; ]
}

