/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __GRAMMAR_HH__
#define __GRAMMAR_HH__

#include "funcdata.hh"

namespace ghidra {

class GrammarToken {
  friend class GrammarLexer;
public:
  enum {
    openparen = 0x28,
    closeparen = 0x29,
    star = 0x2a,
    comma = 0x2c, 
    semicolon = 0x3b,
    openbracket = 0x5b,
    closebracket = 0x5d,
    openbrace = 0x7b,
    closebrace = 0x7d,
    
    badtoken = 0x100,
    endoffile = 0x101,
    dotdotdot = 0x102,
    scoperes = 0x103,

    integer = 0x104,
    charconstant = 0x105,
    identifier = 0x106,
    stringval = 0x107,
  };
private:
  uint4 type;
  union tokenvalue {
    uintb integer;
    string *stringval;
  };
  tokenvalue value;
  int4 lineno;			// Line number containing this token
  int4 colno;			// Column where this token starts
  int4 filenum;			// Which file were we in
  void set(uint4 tp);
  void set(uint4 tp,char *ptr,int4 len);
  void setPosition(int4 file,int4 line,int4 col) { filenum=file; lineno=line; colno=col; }
public:
  GrammarToken(void);
  uint4 getType(void) const { return type; }
  uintb getInteger(void) const { return value.integer; }
  string *getString(void) const { return value.stringval; }
  int4 getLineNo(void) const { return lineno; }
  int4 getColNo(void) const { return colno; }
  int4 getFileNum(void) const { return filenum; }
};

class GrammarLexer {
  map<int4,string> filenamemap;	// All files ever seen
  map<int4,istream *> streammap;
  vector<int4> filestack;	// Stack of current files
  int4 buffersize;		// maximum characters in buffer
  char *buffer;			// Current line being processed
  int4 bufstart;		// Next character to process
  int4 bufend;			// Next open position in buffer
  int4 curlineno;
  istream *in;			// Current stream
  bool endoffile;
  uint4 state;			// State of parser
  string error;
  enum {
    start,
    slash,
    dot1,
    dot2,
    dot3,
    scoperes1,
    scoperes2,
    punctuation,
    endofline_comment,
    c_comment,
    doublequote,
    doublequoteend,
    singlequote,
    singlequoteend,
    singlebackslash,
    number,
    identifier
  };
  void bumpLine(void);
  uint4 moveState(char lookahead);
  void establishToken(GrammarToken &token,uint4 val);
  void setError(const string &err) { error = err; }
public:
  GrammarLexer(int4 maxbuffer);
  ~GrammarLexer(void);
  void clear(void);
  istream *getCurStream(void) { return in; }
  void pushFile(const string &filename,istream *i);
  void popFile(void);
  void getNextToken(GrammarToken &token);
  void writeLocation(ostream &s,int4 line,int4 filenum);
  void writeTokenLocation(ostream &s,int4 line,int4 colno);
  const string &getError(void) const { return error; }
};

class TypeDeclarator;		// Forward declaration

class TypeModifier {
public:
  enum {
    pointer_mod,
    array_mod,
    function_mod,
    struct_mod,
    enum_mod
  };
  virtual ~TypeModifier(void) {}
  virtual uint4 getType(void) const=0;
  virtual bool isValid(void) const=0;
  virtual Datatype *modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const=0;
};

class PointerModifier : public TypeModifier {
  uint4 flags;
public:
  PointerModifier(uint4 fl) { flags = fl; }
  virtual uint4 getType(void) const { return pointer_mod; }
  virtual bool isValid(void) const { return true; }
  virtual Datatype *modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const;
};

class ArrayModifier : public TypeModifier {
  uint4 flags;
  int4 arraysize;
public:
  ArrayModifier(uint4 fl,int4 as) { flags=fl; arraysize = as; }
  virtual uint4 getType(void) const { return array_mod; }
  virtual bool isValid(void) const { return (arraysize>0); }
  virtual Datatype *modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const;
};

class FunctionModifier : public TypeModifier {
  vector<TypeDeclarator *> paramlist;
  bool dotdotdot;
public:
  FunctionModifier(const vector<TypeDeclarator *> *p,bool dtdtdt);
  void getInTypes(vector<Datatype *> &intypes,Architecture *glb) const;
  void getInNames(vector<string> &innames) const;
  bool isDotdotdot(void) const { return dotdotdot; }
  virtual uint4 getType(void) const { return function_mod; }
  virtual bool isValid(void) const;
  virtual Datatype *modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const;
};

class TypeDeclarator {
  friend class CParse;
  vector<TypeModifier *> mods;
  Datatype *basetype;
  string ident;			// variable identifier associated with type
  string model;			// name of model associated with function pointer
  uint4 flags;			// Specifiers qualifiers
  int4 numBits;			// Number of bits associated with declaration (0=unspecified)
public:
  TypeDeclarator(void) { basetype=(Datatype *)0; flags=0; numBits=0; }
  TypeDeclarator(const string &nm) { ident=nm; basetype=(Datatype *)0; flags=0; numBits=0; }
  ~TypeDeclarator(void);
  Datatype *getBaseType(void) const { return basetype; }
  int4 numModifiers(void) const { return mods.size(); }
  int4 getNumBits(void) const { return numBits; }
  void setNumBits(int4 val) { numBits = val; }
  const string &getIdentifier(void) const { return ident; }
  ProtoModel *getModel(Architecture *glb) const;
  bool getPrototype(PrototypePieces &pieces,Architecture *glb) const;
  bool hasProperty(uint4 mask) { return ((flags&mask)!=0); }
  Datatype *buildType(Architecture *glb) const;
  bool isValid(void) const;
};

struct TypeSpecifiers {
  Datatype *type_specifier;
  string function_specifier;
  uint4 flags;
  TypeSpecifiers(void) { type_specifier = (Datatype *)0; flags = 0; }
};

struct Enumerator {
  string enumconstant;		// Identifier associated with constant
  bool constantassigned;	// True if user specified explicit constant
  uintb value;			// The actual constant
  Enumerator(const string &nm) { constantassigned = false; enumconstant = nm; }
  Enumerator(const string &nm,uintb val) { constantassigned = true; enumconstant=nm; value=val; }
};

class CParse {
public:
  enum {
    f_typedef = 1,
    f_extern = 2,
    f_static = 4,
    f_auto = 8,
    f_register = 16,
    f_const = 32,
    f_restrict = 64,
    f_volatile = 128,
    f_inline = 256,
    f_struct = 512,
    f_union = 1024,
    f_enum = 2048
  };
  enum {
    doc_declaration,
    doc_parameter_declaration
  };
private:
  Architecture *glb;
  map<string,uint4> keywords;
  GrammarLexer lexer;
  int4 lineno,colno,filenum;	// Location of last token
  list<TypeDeclarator *> typedec_alloc;
  list<TypeSpecifiers *> typespec_alloc;
  list<vector<uint4> *> vecuint4_alloc;
  list<vector<TypeDeclarator *> *> vecdec_alloc;
  list<string *> string_alloc;
  list<uintb *> num_alloc;
  list<Enumerator *> enum_alloc;
  list<vector<Enumerator *> *> vecenum_alloc;

  vector<TypeDeclarator *> *lastdecls;
  int4 firsttoken;		// Message to parser indicating desired object
  string lasterror;
  void setError(const string &msg);
  int4 lookupIdentifier(const string &nm);
  bool runParse(uint4 doctype);
public:
  CParse(Architecture *g,int4 maxbuf);
  ~CParse(void);
  void clear(void);
  vector<TypeDeclarator *> *mergeSpecDecVec(TypeSpecifiers *spec);
  vector<TypeDeclarator *> *mergeSpecDecVec(TypeSpecifiers *spec,vector<TypeDeclarator *> *declist);
  TypeDeclarator *mergeSpecDec(TypeSpecifiers *spec);
  TypeDeclarator *mergeSpecDec(TypeSpecifiers *spec,TypeDeclarator *dec);
  TypeSpecifiers *addSpecifier(TypeSpecifiers *spec,string *str);
  TypeSpecifiers *addTypeSpecifier(TypeSpecifiers *spec,Datatype *tp);
  TypeSpecifiers *addFuncSpecifier(TypeSpecifiers *spec,string *str);
  TypeDeclarator *mergePointer(vector<uint4> *ptr,TypeDeclarator *dec);
  TypeDeclarator *newDeclarator(string *str);
  TypeDeclarator *newDeclarator(void);
  TypeSpecifiers *newSpecifier(void);
  vector<TypeDeclarator *> *newVecDeclarator(void);
  vector<uint4> *newPointer(void);
  TypeDeclarator *newArray(TypeDeclarator *dec,uint4 flags,uintb *num);
  TypeDeclarator *newFunc(TypeDeclarator *dec,vector<TypeDeclarator *> *declist);
  Datatype *newStruct(const string &ident,vector<TypeDeclarator *> *declist);
  Datatype *oldStruct(const string &ident);
  Datatype *newUnion(const string &ident,vector<TypeDeclarator *> *declist);
  Datatype *oldUnion(const string &ident);
  Enumerator *newEnumerator(const string &ident);
  Enumerator *newEnumerator(const string &ident,uintb val);
  vector<Enumerator *> *newVecEnumerator(void);
  Datatype *newEnum(const string &ident,vector<Enumerator *> *vecenum);
  Datatype *oldEnum(const string &ident);
  uint4 convertFlag(string *str);

  void clearAllocation(void);
  int4 lex(void);

  bool parseFile(const string &filename,uint4 doctype);
  bool parseStream(istream &s,uint4 doctype);

  const string &getError(void) const { return lasterror; }
  void setResultDeclarations(vector<TypeDeclarator *> *val) { lastdecls = val; }
  vector<TypeDeclarator *> *getResultDeclarations(void) { return lastdecls; }
};

extern Datatype *parse_type(istream &s,string &name,Architecture *glb);
extern void parse_protopieces(PrototypePieces &pieces,istream &s,Architecture *glb);
extern void parse_C(Architecture *glb,istream &s);

// Routines to parse interface commands

extern void parse_toseparator(istream &s,string &name);
extern Address parse_machaddr(istream &s,int4 &defaultsize,const TypeFactory &typegrp,bool ignorecolon=false);
extern Address parse_varnode(istream &s,int4 &size,Address &pc,uintm &uq,const TypeFactory &typegrp);
extern Address parse_op(istream &s,uintm &uq,const TypeFactory &typegrp);

} // End namespace ghidra
#endif
