/*
   Copyright (c) 2009-2016  mingw-w64 project

   Contributing authors: Kai Tietz, Jonathan Yong

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <inttypes.h>
#include <stdint.h>

#include "m_token.h"
#include "m_ms.h"

static uMToken *m_combine (sMSCtx *c, uMToken *l, uMToken *r);
static uMToken *m_type (sMSCtx *c, const char *typname);
static uMToken *m_cv (sMSCtx *c, const char *cv);
static uMToken *m_coloncolon (sMSCtx *c, uMToken *l, uMToken *r);
static uMToken *m_element (sMSCtx *c, uMToken *el);
static uMToken *m_array (sMSCtx *c, uMToken *dim);
static uMToken *m_scope (sMSCtx *c, uMToken *n);
static uMToken *m_oper (sMSCtx *c, uMToken *n);
static uMToken *m_name (sMSCtx *c, const char *str);
static uMToken *m_colon (sMSCtx *c, const char *str);
static uMToken *m_opname (sMSCtx *c, const char *str);
static uMToken *m_rtti (sMSCtx *c, const char *str);
static uMToken *m_frame (sMSCtx *c, uMToken *u);
static uMToken *m_rframe (sMSCtx *c, uMToken *u);
static uMToken *m_ltgt (sMSCtx *c, uMToken *u);
static uMToken *m_throw (sMSCtx *c, uMToken *u);
static uMToken *m_lexical_frame (sMSCtx *c, uMToken *u);

static uMToken *get_decorated_name (sMSCtx *c);
static uMToken *get_string_literal_type (sMSCtx *c);
static uMToken *get_symbol_name (sMSCtx *c);
static uMToken *get_zbuf_name (sMSCtx *c, int updateCache);
static uMToken *get_dimension_signed (sMSCtx *c);
static uMToken *get_dimension (sMSCtx *c,int fSigned, int fNegate);
static uMToken *extract_name (sMSCtx *c, char term);
static uMToken *get_scope (sMSCtx *c);
static uMToken *get_template_name (sMSCtx *c, int fReadTerminator);
static uMToken *get_operator_name (sMSCtx *c, int fIsTemplate, int *pfReadTemplateArguments);
static uMToken *get_template_argument_list (sMSCtx *c);
static uMToken *get_lexical_frame(sMSCtx *c);
static uMToken *get_string_encoding (sMSCtx *c, int wantBody);
static uMToken *get_template_constant (sMSCtx *c);
static uMToken *get_data_type (sMSCtx *c);
static uMToken *get_indirect_data_type(sMSCtx *c, uMToken *superType, char prType, uMToken *cvType, int thisFlag);
static uMToken *get_primary_data_type (sMSCtx *c, uMToken *superType);
static uMToken *get_based_type (sMSCtx *c);
static uMToken *get_scoped_name (sMSCtx *c);
static uMToken *get_basic_data_type (sMSCtx *c, uMToken *superName);
static uMToken *get_pointer_reference_type (sMSCtx *c, uMToken *cvType, uMToken *superType, char ptrChar);
static uMToken *get_indirect_function_type (sMSCtx *c, uMToken *superType);
static uMToken *get_pointer_reference_data_type (sMSCtx *c, uMToken *superType,int isPtr);
static uMToken *get_ECSU_data_type (sMSCtx *c);
static uMToken *get_enum_size_type (sMSCtx *c);
static uMToken *get_this_type (sMSCtx *c);
static uMToken *get_calling_convention (sMSCtx *c);
static uMToken *get_throw_types (sMSCtx *c);
static uMToken *get_argument_types (sMSCtx *c);
static uMToken *get_return_type (sMSCtx *c);
static uMToken *get_array_type (sMSCtx *c, uMToken *superType);
static uMToken *get_argument_list (sMSCtx *c);
static uMToken *compose_decl (sMSCtx *c, uMToken *symbol);
static uMToken *get_vftable_type (sMSCtx *c, uMToken *superType);
static int get_number_of_dimensions (sMSCtx *c);
static int get_encoded_type (sMSCtx *);
static uMToken *get_vdisp_map_type (sMSCtx *c, uMToken *superType);
static uMToken *get_ext_data_type (sMSCtx *c, uMToken *superType);

uMToken *
libmangle_decode_ms_name (libmangle_gc_context_t *gc, const char *name)
{
  sMSCtx ctx;
  sCached ZNameList, ArgList, TempArgList;
  uMToken *ret = NULL;
  if (!name || *name == 0)
    return NULL;
  
  memset (&ctx, 0, sizeof (ctx));
  ctx.gc = gc;
  memset (&ZNameList, 0, sizeof (ZNameList));
  memset (&ArgList, 0, sizeof (ArgList));
  memset (&TempArgList, 0, sizeof (TempArgList));
  ctx.name = name;
  ctx.end = name + strlen (name);
  ctx.pos = ctx.name;

  ctx.pZNameList = &ZNameList;
  ctx.pArgList = &ArgList;
  ctx.pTemplateArgList = &TempArgList;

#if 0  
  fprintf(stderr,"decode_ms_name: %s\n", name);
#endif

  if (name[0] == '?')
    {
      if (name[1] == '@')
        {
          SKIP_CHAR(&ctx,2);
          ret = get_decorated_name (&ctx);
          /* CV: ??? */
        }
      else if (name[1] == '$')
        {
           if (!(ret = get_template_name (&ctx, 0)))
             ret = get_decorated_name (&ctx);
        }
      else
        ret = get_decorated_name (&ctx);
    }
  else
    ret = get_decorated_name (&ctx);

  if (!ret)
    {
      ret = gen_name (ctx.gc, eMST_unmangled, name);
    }

  return ret;
}

char *
libmangle_encode_ms_name (libmangle_gc_context_t *gc, uMToken *tok)
{
  return NULL;
}

static uMToken *
get_decorated_name (sMSCtx *c)
{
  uMToken *n = NULL;
  uMToken *d = NULL;
  int isudc;
  if (GET_CHAR (c) != '?')
    {
      if (GET_CHAR (c) == 0)
         c->err = 1;
      fprintf (stderr,"*** get_decorated_name %s empty\n", c->name);
      return NULL;
    }
  INC_CHAR (c);
  
  n = get_symbol_name (c);
  isudc = (n && (MTOKEN_FLAGS (n) & MTOKEN_FLAGS_UDC)) ? 1 : 0;
  if (c->err)
    return n;
  if (GET_CHAR (c) != 0 && GET_CHAR (c) != '@')
    {
      d = get_scope (c);
      if (d)
        {
	  if (c->fExplicitTemplateParams == 0)
	    n = m_coloncolon (c, d, n);
	  else
	    {
	      c->fExplicitTemplateParams = 0;
	      n = m_combine (c, n, d);
	      if (GET_CHAR (c) != '@')
	        {
		  d = get_scope (c);
		  n = m_coloncolon (c, d, n);
	        }
	    }
        }
    }
  if (!n)
    return n;
  if (isudc)
    MTOKEN_FLAGS (n) |= MTOKEN_FLAGS_UDC;
  if (MTOKEN_FLAGS (n) & MTOKEN_FLAGS_NOTE)
    return n;
  if (GET_CHAR (c) != 0)
    {
      if (GET_CHAR (c) != '@')
	return NULL;
      INC_CHAR (c);
    }
  return compose_decl (c, n);
}


static uMToken *
get_symbol_name (sMSCtx *c)
{
  if (GET_CHAR (c) != '?')
    return get_zbuf_name (c, 1);
  if (c->pos[1] == '$')
    return get_template_name (c, 1);
  INC_CHAR (c);
  return get_operator_name (c, 0, NULL);
}

static uMToken *
get_zbuf_name (sMSCtx *c, int updateCache)
{
  const char *ntmp;
  uMToken *dim, *ret = NULL, *n = NULL;
  
  if (GET_CHAR(c) >= '0' && GET_CHAR (c) <= '9')
    {
      ret = c->pZNameList->arr[GET_CHAR (c) - '0'];
      INC_CHAR (c);
      return ret;
    }
  if (GET_CHAR (c) == '?')
    {
      n = get_template_name (c, 0);
      if (GET_CHAR (c) == '@')
        INC_CHAR (c);
      if (updateCache && c->pZNameList->count < 10)
        {
          c->pZNameList->arr[c->pZNameList->count] = n;
          c->pZNameList->count += 1;
        }
      return n;
    }
  ntmp="template-parameter-";
  if (!strncmp(c->pos,"template-parameter-",19))
    SKIP_CHAR (c,19);
  else
    {
      ntmp="generic-type-";
      if (!strncmp(c->pos, "generic-type-", 13))
        SKIP_CHAR (c,13);
      else
        {
          n = extract_name (c, '@');
          if (updateCache && c->pZNameList->count < 10)
            {
              c->pZNameList->arr[c->pZNameList->count] = n;
              c->pZNameList->count += 1;
            }
          return n;
        }
    }
  dim=get_dimension_signed (c);
  n=chain_tok (gen_name (c->gc, eMST_templargname, ntmp), dim);
  if (updateCache && c->pZNameList->count < 10)
    {
      c->pZNameList->arr[c->pZNameList->count] = n;
      c->pZNameList->count += 1;
    }
  return n;
}

static uMToken *
get_dimension_signed (sMSCtx *c)
{
   if (GET_CHAR (c) == 0)
     {
       c->err=1;
       return NULL;
     }
   if (GET_CHAR (c) != '?')
     return get_dimension (c, 0, 0);
   INC_CHAR (c);
   return get_dimension (c, 0, 1/* be negative*/);
}

static uMToken *
get_dimension (sMSCtx *c, int fSigned, int fNegate)
{
  const char *non_tt_param=NULL;
  uint64_t v_r = 0ULL, v1;
  
  if (GET_CHAR (c) == 'Q')
    {
      INC_CHAR (c);
      non_tt_param="'non-type-template-parameter";
    }
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return NULL;
    }
  if (GET_CHAR (c) >= '0' && GET_CHAR (c) <= '9')
    {
      uint64_t v = (uint64_t) ((GET_CHAR (c)-'0') + 1);
      INC_CHAR (c);
      return gen_dim (c->gc, eMST_dim,v, non_tt_param, fSigned, fNegate);
    }

  while (GET_CHAR (c) != '@')
    {
      if (GET_CHAR (c) == 0)
        {
          c->err = 2;
          return NULL;
        }
      if (GET_CHAR (c) < 'A' || GET_CHAR (c) > 'P')
        {
          c->err = 1;
          return NULL;
        }
      v1=(uint64_t) (GET_CHAR (c) - 'A');
      v_r = v_r*10ULL + v1;
      INC_CHAR (c);
    }
  if (GET_CHAR (c) !='@')
    {
      c->err = 1;
      return NULL;
    }
  INC_CHAR (c);
  return gen_dim (c->gc, eMST_dim,v_r, non_tt_param, fSigned, fNegate);
}

static uMToken *
extract_name (sMSCtx *c, char term)
{
  uMToken *ret;
  char *txt;
  size_t len;
  const char *sv = c->pos;
  while (GET_CHAR (c) != 0 && GET_CHAR (c) != term)
    {
      INC_CHAR (c);
    }
  if (GET_CHAR (c) != '@')
    {
      c->err = 1;
      return NULL;
    }
  len = (size_t) (c->pos - sv);
  txt = (char *) malloc (len + 1);
  memcpy (txt, sv, len);
  txt[len] = 0;
  INC_CHAR (c);
  ret = m_name (c, txt);
  free (txt);
  return ret;
}

static uMToken *
get_scope (sMSCtx *c)
{
  uMToken *n = NULL;

  while (1)
  {
    if (GET_CHAR (c) == 0 || GET_CHAR (c) == '@')
      break;
    if (c->fExplicitTemplateParams != 0 && !c->fGetTemplateArgumentList)
      return n;
    if (GET_CHAR (c) == '?')
      {
        INC_CHAR (c);
        if (GET_CHAR (c) == '$')
          {
            DEC_CHAR (c);
	    n = m_coloncolon (c, get_zbuf_name (c, 1), n);
          }
        else if (GET_CHAR (c) == '%' || GET_CHAR (c) == 'A')
          {
            while (GET_CHAR (c) != '@')
              INC_CHAR (c);
            INC_CHAR (c);
            n = m_coloncolon (c, m_name (c, "anonymous_namespace"), n);
          }
        else if (GET_CHAR (c) == '?')
          {
            if (c->pos[1] == '_' && c->pos[2] == '?')
              {
                INC_CHAR (c);
		n = m_coloncolon (c, get_operator_name (c, 0,NULL), n);
                if (GET_CHAR (c) == '@')
                  INC_CHAR (c);
              }
            else
	      {
		n = m_coloncolon (c, gen_unary (c->gc, eMST_slashed, get_decorated_name (c)), n);
	      }
          }
        else if (GET_CHAR (c) == 'I')
          {
            INC_CHAR (c);
	    n = m_coloncolon (c, m_array (c, get_zbuf_name (c, 1)), n);
          }
        else
	  n = m_coloncolon (c, get_lexical_frame (c), n);
      }
    else
      n = m_coloncolon (c, get_zbuf_name (c, 1), n);
  }
  if (n)
    n = m_scope (c, n);
  if (GET_CHAR (c))
    {
      if (GET_CHAR (c) == '@')
        return n;
    }
  else
    {
      c->err = 2;
      return n;
    }
    return n;
}

static uMToken *
get_template_name (sMSCtx *c, int fReadTerminator)
{
  sCached rep1;
  sCached rep2;
  sCached rep3;
  sCached *svZName,*svArgList,*svTempArgList;
  uMToken *n = NULL;
  int fFlag = 0;
  
  if (GET_CHAR (c) !='?' || c->pos[1] != '$')
    return NULL;
  memset (&rep1, 0, sizeof (rep1));
  memset (&rep2, 0, sizeof (rep2));
  memset (&rep3, 0, sizeof (rep3));
  
  svTempArgList = c->pTemplateArgList;
  svArgList = c->pArgList;
  svZName = c->pZNameList;
  
  SKIP_CHAR(c,2);
  
  c->pArgList=&rep1;
  c->pZNameList=&rep2;
  c->pTemplateArgList=&rep3;

  if (GET_CHAR (c) == '?')
    {
      INC_CHAR (c);
      n = get_operator_name (c, 1, &fFlag);
    }
  else
    n = get_zbuf_name (c, 1);
  if (!n)
    c->fExplicitTemplateParams = 1;
  if (!fFlag)
    {
      n = get_template_argument_list (c);
      n = m_ltgt (c, n);
      if (fReadTerminator)
        INC_CHAR (c);
    }
  c->pArgList = svArgList;
  c->pZNameList = svZName;
  c->pTemplateArgList = svTempArgList;
  return n;
}

static uMToken *
get_operator_name (sMSCtx *c, int fIsTemplate, int *pfReadTemplateArguments)
{
  const char *svName;
  char ch = GET_CHAR (c);
  uMToken *n = NULL,*h = NULL;
  
  if (!ch)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  switch(ch)
    {
      case '0': case '1':
        if (fIsTemplate)
          {
            h = m_ltgt (c, get_template_argument_list (c));
            if (pfReadTemplateArguments)
              *pfReadTemplateArguments = 1;
            ch = GET_CHAR (c);
            if (ch == 0)
              return m_oper (c, h);
            INC_CHAR (c);
          }
        svName = c->pos;
        n = get_zbuf_name (c, 0);
        c->pos = svName;
        if (n && ch == '1')
          n=gen_unary (c->gc, eMST_destructor, n);
        n = chain_tok (n, h);
        return m_oper (c, n);
      case '2':
        return m_oper (c, m_opname (c, "operator new"));
      case '3':
        return m_oper (c, m_opname (c, "operator delete"));
      case '4':
        return m_oper (c, m_opname (c, "operator ="));
      case '5':
        return m_oper (c, m_opname (c, "operator >>"));
      case '6':
        return m_oper (c, m_opname (c, "operator <<"));
      case '7':
        return m_oper (c, m_opname (c, "operator !"));
      case '8':
        return m_oper (c, m_opname (c, "operator =="));
      case '9':
        return m_oper (c, m_opname (c, "operator !="));
      case 'A':
        return m_oper (c, m_opname (c, "operator []"));
      case 'B':
        n = m_opname (c,  "operator");
        MTOKEN_FLAGS(n) |= MTOKEN_FLAGS_UDC;
        n = m_oper (c, n);
        MTOKEN_FLAGS(n) |= MTOKEN_FLAGS_UDC;
        return n;
      case 'C':
        return m_oper (c, m_opname (c, "operator ->"));
      case 'D':
        return m_oper (c, m_opname (c, "operator *"));
      case 'E':
        return m_oper (c, m_opname (c, "operator ++"));
      case 'F':
        return m_oper (c, m_opname (c, "operator --"));
      case 'G':
        return m_oper (c, m_opname (c, "operator -"));
      case 'H':
        return m_oper (c, m_opname (c, "operator +"));
      case 'I':
        return m_oper (c, m_opname (c, "operator &"));
      case 'J':
        return m_oper (c, m_opname (c, "operator ->*"));
      case 'K':
        return m_oper (c, m_opname (c, "operator /"));
      case 'L':
        return m_oper (c, m_opname (c, "operator %"));
      case 'M':
        return m_oper (c, m_opname (c, "operator <"));
      case 'N':
        return m_oper (c, m_opname (c, "operator <="));
      case 'O':
        return m_oper (c, m_opname (c, "operator >"));
      case 'P':
        return m_oper (c, m_opname (c, "operator >="));
      case 'Q':
        return m_oper (c, m_opname (c, "operator ,"));
      case 'R':
        return m_oper (c, m_opname (c, "operator ()"));
      case 'S':
        return m_oper (c, m_opname (c, "operator ~"));
      case 'T':
        return m_oper (c, m_opname (c, "operator ^"));
      case 'U':
        return m_oper (c, m_opname (c, "operator |"));
      case 'V':
        return m_oper (c, m_opname (c, "operator &&"));
      case 'W':
        return m_oper (c, m_opname (c, "operator ||"));
      case 'X':
        return m_oper (c, m_opname (c, "operator *="));
      case 'Y':
        return m_oper (c, m_opname (c, "operator +="));
      case 'Z':
        return m_oper (c, m_opname (c, "operator -="));
      case '_':
        break;
      default:
        fprintf (stderr, " *** get_operator_name unknown '%c'\n", ch);
        return NULL;
    }
  ch = GET_CHAR (c);
  if (! ch)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  switch(ch)
    {
      case '_':
        ch = GET_CHAR (c);
        INC_CHAR (c);
        switch (ch)
          {
            case 'A':
              return m_oper (c, m_opname (c, "__man_vec_ctor"));
            case 'B':
              return m_oper (c, m_opname (c, "__man_vec_dtor"));
            case 'C':
              return m_oper (c, m_opname (c, "__ehvec_copy_ctor"));
            case 'D':
              return m_oper (c, m_opname (c, "__ehvec_copy_ctor_vb"));
          }
        fprintf (stderr, " *** get_operator_name unknown '__%c'\n", ch);
        return NULL;

      case '0':
        return m_oper (c, m_opname (c, "operator /="));
      case '1':
        return m_oper (c, m_opname (c, "operator %="));
      case '2':
        return m_oper (c, m_opname (c, "operator >>="));
      case '3':
        return m_oper (c, m_opname (c, "operator <<="));
      case '4':
        return m_oper (c, m_opname (c, "operator &="));
      case '5':
        return m_oper (c, m_opname (c, "operator |="));
      case '6':
        return m_oper (c, m_opname (c, "operator ^="));
      case '7':
        return m_oper (c, gen_name (c->gc, eMST_vftable, "$vftable"));
      case '8':
        return m_oper (c, gen_name (c->gc, eMST_vbtable, "$vbtable"));
      case '9':
        return m_oper (c, gen_name (c->gc, eMST_vcall, "vcall"));
      case 'A':
        return m_oper (c, m_opname (c,"typeof"));
      case 'B':
        return m_oper (c, m_opname (c,"local_static_guard"));
      case 'C':
        n = get_string_encoding (c, 1);
        MTOKEN_FLAGS(n) |= MTOKEN_FLAGS_NOTE;
        return n;
      case 'D':
        return m_oper (c, m_opname (c,"vbase_destructor"));
      case 'E':
        return m_oper (c, m_opname (c,"__vecDelDtor"));
      case 'F':
        return m_oper (c, m_opname (c,"__dflt_ctor_closure"));
      case 'G':
        return m_oper (c, m_opname (c, "__delDtor"));
      case 'H':
        return m_oper (c, m_opname (c, "__vec_ctor"));
      case 'I':
        return m_oper (c, m_opname (c, "__vec_dtor"));
      case 'J':
        return m_oper (c, m_opname (c, "__vec_ctor_vb"));
      case 'K':
        return m_oper (c, m_opname (c, "$vdispmap"));
      case 'L':
        return m_oper (c, m_opname (c, "__ehvec_ctor"));
      case 'M':
        return m_oper (c, m_opname (c, "__ehvec_dtor"));
      case 'N':
        return m_oper (c, m_name (c, "__ehvec_ctor_vb"));
      case 'O':
        return m_oper (c, m_opname (c, "__copy_ctor_closure"));
      case 'P':
        return gen_unary (c->gc, eMST_udt_returning, get_operator_name (c, 0, NULL));
      case 'Q':
        return m_oper (c, m_opname (c,  "operator 'EH'"));
      case 'R':
        ch = GET_CHAR (c);
        INC_CHAR (c);
        switch (ch)
          {
            case '0':
              h = m_rtti (c, "$type_descriptor");
              return m_oper (c, m_combine (c, get_data_type (c) , h));
            case '1':
              h = m_rtti (c, "base_class_descriptor");
              n = m_element (c, get_dimension_signed (c));
              n = chain_tok (n, m_element (c, get_dimension_signed (c)));
              n = chain_tok (n, m_element (c, get_dimension_signed (c)));
              n = chain_tok (n, m_element (c, get_dimension (c, 0, 0)));
	      n = m_frame (c, n);
              return m_oper (c, gen_binary (c->gc, eMST_assign, h, n));
            case '2':
              return m_oper (c, m_rtti (c, "base_class_array"));
            case '3':
	      return m_oper (c, m_rtti (c, "class_hierarchy_descriptor"));
            case '4':
              return m_oper (c, m_rtti (c, "complete_object_locator"));
          }
        DEC_CHAR (c);
        fprintf (stderr, " *** Unkown RTTI %c\n", ch);
        c->err = 2;
        return NULL;
      case 'S':
        return m_oper (c, m_opname (c, "$locvftable"));
      case 'T':
        return m_oper (c, m_opname (c, "__local_vftable_ctor_closure"));
      case 'U':
        n = m_opname (c, "operator new[]");
        return m_oper (c, n);
      case 'V':
        n = m_opname (c,  "operator delete[]");
        return m_oper (c, n);
      case 'W': /* omni callsig ??? */
      default:
        fprintf (stderr, " *** get_operator_name unknown '_%c'\n", ch);
        return NULL;
      case 'X':
        return m_oper (c, m_opname (c,  "__placement_delete_closure"));
      case 'Y':
        return m_oper (c, m_opname (c,  "__placement_arrayDelete_closure"));
      case '?':
        break;
    }
  ch = GET_CHAR (c);
  if (!ch)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  switch(ch)
    {
      case '0':
	m_combine (c, m_name (c, "using namespace"), get_string_encoding (c, 0));
        MTOKEN_FLAGS (n) |= MTOKEN_FLAGS_NOTE;
        return n;
    }
  fprintf (stderr, " *** get_operator_name unknown '__?%c'\n", ch);
  return NULL;
}

static uMToken *
get_template_argument_list (sMSCtx *c)
{
  uMToken *n = NULL;
  uMToken *h = NULL;
  int idx;
  int beFirst=1;
  c->fGetTemplateArgumentList = 1;
  do
    {
      if (GET_CHAR (c) == 0 || GET_CHAR (c) == '@')
        break;
      idx = (int) (GET_CHAR (c) - '0');
      if (GET_CHAR (c) >= '0' && GET_CHAR (c) <= '9')
        {
          h = c->pTemplateArgList->arr[idx];
          INC_CHAR (c);
        }
      else
        {
          const char *svPos = c->pos;
          if (GET_CHAR (c) =='X')
            {
              INC_CHAR (c);
              h = m_type (c, "void");
            }
          else if (GET_CHAR (c) == '$' && c->pos[1] != '$')
            {
              INC_CHAR (c);
              h = get_template_constant (c);
            }
          else if (GET_CHAR (c) == '?')
            {
              uMToken *sdim = get_dimension_signed (c);
              h = gen_binary (c->gc, eMST_templateparam, m_name (c, "template-parameter"), sdim);
            }
          else
            h = get_primary_data_type (c, NULL);
          if ((int)(c->pos-svPos)>1 &&
              c->pTemplateArgList->count < 10)
            {
              c->pTemplateArgList->arr[c->pTemplateArgList->count] = h;
              c->pTemplateArgList->count += 1;
            }
        }
      h = m_element (c, h);
      if (beFirst)
        {
          n = h;
          beFirst = 0;
        }
      else
        {
          n = chain_tok (n, h);
        }
    }
  while (c->err == 0);
  c->fGetTemplateArgumentList = 0;
  if (n)
    n = gen_unary (c->gc, eMST_template_argument_list, n);
  return n;
}

static uMToken *
get_lexical_frame (sMSCtx *c)
{
  return m_lexical_frame (c, get_dimension (c, 0, 0));
}

static uMToken *
get_string_encoding (sMSCtx *c, int wantBody)
{
  uMToken *length = NULL;
  uMToken *crc = NULL, *h = NULL;
  uMToken *typ = NULL;
  const char *svName;
  size_t len;
  char *hp;

  if (GET_CHAR (c) != '@') return NULL;
  INC_CHAR (c);
  typ = get_string_literal_type (c);
  length = get_dimension (c, 0, 0);
  crc = get_dimension (c, 0, 0);
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return NULL;
    }
  svName = c->pos;
  while (GET_CHAR (c) != 0)
    {
      if (GET_CHAR (c) == '@')
        break;
      INC_CHAR (c);
  }
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return NULL;
    }
  len = (size_t) (c->pos - svName);
  hp = (char *) malloc (len + 1);
  memcpy (hp, svName, len);
  hp[len] = 0;
  INC_CHAR (c);
  h = m_name (c, hp);
  free (hp);
  if (wantBody)
    h = m_combine (c, typ, m_combine (c, h, m_array (c, length)));
  return h;
}

static uMToken *
get_template_constant (sMSCtx *c)
{
  char ch;
  uMToken *n = NULL;
  uMToken *exp;

  ch = GET_CHAR(c);
  if (!ch)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  if (ch =='E')
    return get_decorated_name (c);
  if (ch > 'E' && ch <= 'J')
    {
      if (ch >= 'H' && ch <= 'J')
        {
          exp = m_element (c, get_decorated_name (c));
          if (!n) n = exp;
          else chain_tok (n, exp);
        }
      switch(ch)
        {
          case 'G': case 'J':
            exp = m_element (c, get_dimension_signed (c));
	    if (!n) n = exp;
	    else chain_tok (n, exp);
          case 'F': case 'I':
            exp = m_element (c, get_dimension_signed (c));
	    if (!n) n = exp;
	    else chain_tok (n, exp);
          case 'H':
            exp = m_element (c, get_dimension_signed (c));
	    if (!n) n = exp;
	    else chain_tok (n, exp);
            break;
        }
      return m_frame (c, n);
    }
  if (ch == 'Q' || ch == 'D')
    {
      n = get_dimension_signed (c);
      if (ch == 'D')
        return gen_binary (c->gc, eMST_templateparam, m_name (c, "template-parameter"), n);
      return gen_binary (c->gc, eMST_nonetypetemplateparam, m_name (c, "none-type-template-parameter"), n);
    }
  if (ch == '0')
    return get_dimension_signed (c);
  if (ch == '1')
    {
      if (GET_CHAR (c) != '@')
        return m_combine (c, m_cv (c, "&"), get_decorated_name (c));
      INC_CHAR (c);
      return m_name (c, "NULL");
    }
  if (ch != '2')
    {
      fprintf (stderr, " *** get_template_constant unknown '%c'\n", ch);
      return NULL;
    }
  n = get_dimension_signed (c);
  exp = get_dimension_signed (c);
  return gen_binary (c->gc, eMST_exp, n, exp);
}

static uMToken *
get_data_type (sMSCtx *c)
{
  uMToken *n = NULL;
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return n;
    }
  if (GET_CHAR (c) == '?')
    {
      INC_CHAR (c);
      n = get_indirect_data_type (c, n, (char)0, NULL, 0);
      return get_primary_data_type (c, n);
    }
  if (GET_CHAR (c) != 'X')
    return get_primary_data_type (c, n);
  INC_CHAR (c);
  return m_combine (c, m_type (c, "void"), n);
}

static uMToken *
get_indirect_data_type (sMSCtx *c, uMToken *superType, char prType, uMToken *cvType, int thisFlag)
{
  uMToken *n = NULL, *n1 = NULL, *n2 = NULL;
  int state;

  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      if (thisFlag != 0)
        return NULL;
      if (!superType)
        {
          if (!cvType)
            return NULL;
          return cvType;
        }
      if (MTOKEN_FLAGS (superType) & MTOKEN_FLAGS_PTRREF)
        return superType;
      if (!cvType)
        return superType;
      return m_combine (c, cvType, superType);
    }
  if (GET_CHAR (c) == '$')
    {
      INC_CHAR (c);
      if (GET_CHAR (c) == 'A')
        {
          n = m_cv (c, "__gc");
          INC_CHAR (c);
        }
      else if (GET_CHAR (c) == 'B')
        {
          n = m_cv (c, "__pin");
          INC_CHAR (c);
        }
      else
        {
          state = (int)(GET_CHAR (c) - '3')*16;
          INC_CHAR (c);
          state += (int)GET_CHAR (c);
          INC_CHAR (c);
          n = gen_value (c->gc, eMST_gcarray, (uint64_t) state, 0, 4);
          if (superType)
            {
              if (!(MTOKEN_FLAGS (superType) & MTOKEN_FLAGS_ARRAY))
                superType = m_rframe (c, superType);
              n=m_combine (c, superType, n);
            }
          INC_CHAR (c);
          return n;
        }
    }
  state = (GET_CHAR (c) - (GET_CHAR (c) < 'A' ? 0x16 : 0x41));
  while (1)
    {
      if (state == 4)
        n1 = m_combine (c, n1, m_cv (c, "__ptr64"));
      else if (state == 5)
	n2 = m_combine (c, n2, m_cv (c, "__unaligned"));
      else if (state != 8)
        {
	  uMToken *ret = NULL;
          INC_CHAR (c);
          if (state > 31)
	    return NULL;
	  if (prType == '*')
	    ret = m_cv (c, "*");
	  else if (prType == '&')
	    ret = m_cv (c, "&");

	  ret = m_combine (c, n ,ret);
	  ret = m_combine (c, ret, n1);
	  ret = m_combine (c, n2, ret);
          if ((state & 0x10) != 0)
            {
              if (thisFlag != 0)
		return NULL;
              if (prType)
                {
                  if (GET_CHAR (c) == 0)
		    {
		      c->err = 2;
		      return ret;
		    }
                  else
		    {
		      if (ret)
			ret = gen_binary (c->gc, eMST_coloncolon , get_scope (c), ret);
		      else
			ret = get_scope (c);
		    }
                  if (GET_CHAR (c) == 0) c->err = 2;
                  else
                    {
		      char ch = GET_CHAR (c);
                      INC_CHAR (c);
                      if (ch != '@')
			return NULL;
                    }
                }
              else if (GET_CHAR (c) == 0)
		c->err = 2;
              else
                {
                  ret = get_scope (c);
                  if (GET_CHAR (c) != 0)
                    {
		      char ch = GET_CHAR (c);
                      INC_CHAR (c);
                      if (ch != '@')
			return NULL;
                    }
                  else
		    c->err = 2;
                }
            }
          if ((state&0xc)==0xc)
            {
              if (thisFlag != 0)
		return NULL;
              ret = m_combine (c, get_based_type (c), ret);
            }
          if ((state & 2) != 0)
	    ret = m_combine (c, m_cv (c, "volatile"), ret);
          if ((state & 1) != 0)
	    ret = m_combine (c, m_cv (c, "const"), ret);
          if (thisFlag != 0)
            {
	      if (!ret)
		ret = m_name (c, "");
	      MTOKEN_FLAGS(ret) |= MTOKEN_FLAGS_PTRREF;
              return ret;
            }
          if (!superType)
            {
              if (cvType)
		ret = m_combine (c, ret, cvType);
	      if (!ret)
		ret = m_name (c, "");
              MTOKEN_FLAGS(ret) |= MTOKEN_FLAGS_PTRREF;
              return ret;
            }
          if (MTOKEN_FLAGS(superType) & MTOKEN_FLAGS_PTRREF)
            {
              if (cvType)
                {
		  ret = m_combine (c, ret, cvType);
		  ret = m_combine (c, ret, superType);
                  MTOKEN_FLAGS(ret) |= MTOKEN_FLAGS_PTRREF;
                  return ret;
                }
            }
          if (!(MTOKEN_FLAGS(superType)&MTOKEN_FLAGS_ARRAY))
	    ret = m_combine (c, ret, superType);
          else
	    ret = superType;
          MTOKEN_FLAGS(ret) |= MTOKEN_FLAGS_PTRREF;
          return ret;
        }
      else
        {
          if (!n1)
            n1 = m_cv (c, "__restrict");
          else
            n1 = m_combine (c, n1, m_cv (c, "__restrict"));
        }
      INC_CHAR (c);
      state=GET_CHAR (c)-(GET_CHAR (c) < 'A' ? 0x16 : 0x41);
    }
}

static uMToken *
get_primary_data_type (sMSCtx *c, uMToken *superType)
{
  uMToken *superName = NULL;
  uMToken *cvType = NULL;
  switch(GET_CHAR (c))
    {
      case 0:
	c->err = 2;
	return superType;
      case 'B':
	cvType = m_cv (c, "volatile");
	/* fall through */
      case 'A':
	superName = superType;
	if (!superName)
	  superName = m_name (c, "");
	MTOKEN_FLAGS (superName) |= MTOKEN_FLAGS_PTRREF;
	INC_CHAR (c);
	return get_pointer_reference_type (c, cvType, superName, '&');
      case '$':
	if (c->pos[1] == '$')
	  {
	    SKIP_CHAR (c, 2);
	    break;
	  }
	if (c->pos[1] == 0)
	  {
	    c->err = 2;
	    return NULL;
	  }
	fprintf (stderr, " *** get_primary_data_type '$%c' unknown\n", c->pos[1]);
	return NULL;
      default:
	return get_basic_data_type (c, superType);
    }
  switch(GET_CHAR (c))
    {
      case 0:
	c->err = 2;
	return superType;
      case 'A':
	INC_CHAR (c);
	return get_indirect_function_type (c, superType);
      case 'B':
	INC_CHAR (c);
	return get_pointer_reference_data_type (c, superType, 1);
      case 'C':
	INC_CHAR (c);
	return get_basic_data_type (c, get_indirect_data_type (c, superType, (char)0, superName, 0));
    }
  fprintf (stderr, " *** get_primary_data_type '$$%c' unknown\n", GET_CHAR (c));
  return NULL;
}

static uMToken *
get_based_type (sMSCtx *c)
{
  uMToken *n = m_cv (c, "__based");
  uMToken *p = NULL;
  char ch;

  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return gen_binary (c->gc, eMST_based, n, NULL);
    }
  ch = GET_CHAR (c);
  INC_CHAR (c);
  switch(ch)
    {
      case '0':
	p = m_type (c, "void");
	break;
      case '2':
	p = get_scoped_name (c);
	break;
      case '5':
	fprintf (stderr, " *** get_based_type unknown '%c'\n", ch);
	return NULL;
      default:
	fprintf (stderr, " *** get_based_type unknown '%c' (ignored)\n", ch);
	break;
    }
  return gen_binary (c->gc, eMST_based, n, p);
}

static uMToken *
get_scoped_name (sMSCtx *c)
{
  uMToken *n = NULL;
  n = get_zbuf_name (c, 1);
  if (n && GET_CHAR (c) != 0)
    {
      if (GET_CHAR (c) =='@')
        {
	  INC_CHAR (c);
	  return n;
        }
      n = m_coloncolon (c, get_scope (c), n);
    }
  if (GET_CHAR (c) == '@')
    {
      INC_CHAR (c);
      return n;
    }
  if (GET_CHAR (c) != 0)
      return n;
  c->err = 2;
  return n;
}

static uMToken *
get_basic_data_type (sMSCtx *c, uMToken *superName)
{
  uMToken *bTyp = NULL;
  uMToken *cvName = NULL;
  uMToken *arType = NULL;
  uMToken *tmp = NULL;
  char svChar,svChar1;
  int flags;

  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return superName;
    }
  svChar1 = GET_CHAR (c);
  INC_CHAR (c);
  flags=~0;
  switch (svChar1)
    {
     case 'M':
       bTyp = m_type (c, "float");
       break;
     case 'C':
       bTyp = m_type (c, "signed char");
       break;
     case 'D':
       bTyp = m_type (c, "char");
       break;
     case 'E':
       bTyp = m_type (c, "unsigned char");
       break;
     case 'F':
       bTyp = m_type (c, "short");
       break;
     case 'G':
       bTyp = m_type (c, "unsigned short");
       break;
     case 'H':
       bTyp = m_type (c, "int");
       break;
     case 'I':
       bTyp = m_type (c, "unsigned int");
       break;
     case 'J':
       bTyp = m_type (c, "long");
       break;
     case 'K':
       bTyp = m_type (c, "unsigned long");
       break;
     case 'N':
       bTyp = m_type (c, "double");
       break;
     case 'O':
       bTyp = m_type (c, "long double");
       break;
     case 'P':
       return get_pointer_reference_type (c, bTyp, superName, '*');
     case 'Q':
       if (!superName)
	 bTyp = m_cv (c, "const");
       return get_pointer_reference_type (c, bTyp, superName, '*');
     case 'R':
       if (!superName)
	 bTyp = m_cv (c, "volatile");
       return get_pointer_reference_type (c, bTyp, superName, '*');
     case 'S':
       if (!superName)
         {
	   bTyp = m_cv (c, "const");
	   bTyp = m_combine (c, bTyp, m_cv (c, "volatile"));
         }
       return get_pointer_reference_type (c, bTyp, superName, '*');
     case '_':
       svChar = GET_CHAR (c);
       INC_CHAR (c);
       switch(svChar)
         {
           case 'N':
	     bTyp = m_type (c, "bool");
	     break;
	   case 'O':
	     if (!superName)
	       superName = m_name (c, "");
	     cvName=superName;
	     MTOKEN_FLAGS (cvName) |= MTOKEN_FLAGS_ARRAY;
	     arType = get_pointer_reference_type (c, bTyp, cvName, 0);
	     if (!(MTOKEN_FLAGS (arType)&MTOKEN_FLAGS_ARRAY))
	       arType = m_combine (c, arType, m_array (c, NULL));
	     return arType;
	   case 'W':
	     bTyp = m_type (c, "wchar_t");
	     break;
	   case 'X':
	   case 'Y':
	     DEC_CHAR (c);
	     if (!(bTyp = get_ECSU_data_type (c)))
	       return NULL;
	     break;
	   case 'D':
	     bTyp = m_type (c, "__int8");
	     break;
	   case 'E':
	     bTyp = m_type (c, "unsigned __int8");
	     break;
	   case 'F':
	     bTyp = m_type (c, "__int16");
	     break;
	   case 'G':
	     bTyp = m_type (c, "unsigned __int16");
	     break;
	   case 'H':
	     bTyp = m_type (c, "__int32");
	     break;
	   case 'I':
	     bTyp = m_type (c, "unsigned __int32");
	     break;
	   case 'J':
	     bTyp = m_type (c, "__int64");
	     break;
	   case 'K':
	     bTyp = m_type (c, "unsigned __int64");
	     break;
	   case 'L':
	     bTyp = m_type (c, "__int128");
	     break;
	   case 'M':
	     bTyp = m_type (c, "unsigned");
	     break;
	   case '$':
	     bTyp = get_basic_data_type (c, superName);
	     return m_combine (c, m_cv (c, "__w64"), bTyp);
	   default:
	     fprintf (stderr, " *** get_basic_data_type unknown '_%c' (ignored).\n", svChar);
	     bTyp = m_type (c, "UNKNOWN");
	     break;
         }
       break;
     default:
       DEC_CHAR (c);
       bTyp = get_ECSU_data_type (c);
       if (!bTyp)
	 return bTyp;
       break;
    }
  if (superName)
    bTyp = m_combine (c, bTyp, superName);
  return bTyp;
}

static uMToken *
get_pointer_reference_type (sMSCtx *c, uMToken *cvType, uMToken *superType, char ptrChar)
{
  uMToken *n = NULL;
  if (ptrChar == '&')
    n = m_cv (c, "&");
  else if (ptrChar == '*')
    n = m_cv (c, "*");
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      if (cvType)
        {
	  if (!n)
	    n = cvType;
	  else
	    n = m_combine (c, n, cvType);
        }
      if (superType)
	n = m_combine (c, n, superType);
      return n;
    }
  if (GET_CHAR (c) < '6' || GET_CHAR (c) > '9')
    {
      if (GET_CHAR (c) != '_')
	return get_pointer_reference_data_type (c,
	  get_indirect_data_type (c, superType, ptrChar, cvType, 0),(ptrChar=='*' ? 1 : 0));
    }
  if (cvType)
    {
      if (!superType || !(MTOKEN_FLAGS (superType)&MTOKEN_FLAGS_PTRREF))
        {
	  if (!n)
	    n = cvType;
	  else
	    n = m_combine (c, n, cvType);
        }
    }
  if (superType)
    {
      if (!n)
	n = superType;
      else
	n = m_combine (c, n, superType);
    }
  return get_indirect_function_type (c, n);
}

static uMToken *
get_indirect_function_type (sMSCtx *c, uMToken *superType)
{
  uMToken *retType = NULL;
  uMToken *n1 = NULL, *n2 = NULL;
  int flag;
  int cidx;
  char ch = GET_CHAR (c);
  if (ch == 0)
    {
      c->err = 2;
      return superType;
    }
  if (ch != '_' && (ch < '6' || ch > '9'))
    return NULL;
  cidx = (int) (ch - '6');
  INC_CHAR (c);
  if (ch == '_')
    {
      ch = GET_CHAR (c);
      if (ch == 0)
        {
	  c->err = 2;
	  return superType;
        }
      if (ch < 'A' || ch > 'D')
	return NULL;
      cidx=(int)(ch - 'A') + 4;
      INC_CHAR (c);
    }
  n2 = superType;
  flag = cidx & 2;
  if (flag)
    {
      ch = GET_CHAR (c);
      if (ch == 0)
        {
	  c->err = 2;
	  return n2;
        }
      n2 = m_coloncolon (c, get_scope (c), n2);
      if (GET_CHAR (c) == 0)
        {
	  c->err = 2;
	  return n2;
        }
      if (GET_CHAR (c) != '@')
	return NULL;
      INC_CHAR (c);
      n1 = get_this_type (c);
    }
  if (cidx & 4)
    n2 = m_combine (c, get_based_type (c), n2);
  n2 = m_combine (c, get_calling_convention (c), n2);
  if (superType)
    n2 = m_rframe (c, n2);
  retType = get_return_type (c);
  n2 = m_combine (c, n2, m_rframe (c, get_argument_types (c)));
  if (flag)
    n2 = m_combine (c, n2, n1);
  n2 = m_combine (c, n2, get_throw_types (c));
  return m_combine (c, retType, n2);
}

static uMToken *
get_pointer_reference_data_type (sMSCtx *c, uMToken *superType,int isPtr)
{
  uMToken *n = NULL;
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return superType;
    }
  
  if (isPtr && GET_CHAR (c) == 'X')
    {
      INC_CHAR (c);
      n = m_type (c, "void");
      if (superType)
	n = m_combine (c, n, superType);
      return n;
    }
  if (GET_CHAR (c) == 'Y')
    {
      INC_CHAR (c);
      return get_array_type (c, superType);
    }
  if (GET_CHAR (c) != '_')
    return get_basic_data_type (c, superType);
  if (c->pos[1] != 'Z')
    return get_basic_data_type (c, superType);
  SKIP_CHAR (c, 2);
  n = m_cv (c, "__box");
  return m_combine (c, n, get_basic_data_type (c, superType));
}

static uMToken *
get_ECSU_data_type (sMSCtx *c)
{
  char ch = GET_CHAR (c);
  uMToken *n = NULL;

  if (!ch)
    {
      c->err = 2;
      return m_type (c, "no-ecsu");
    }
  INC_CHAR (c);
  switch (ch)
    {
      default:
	fprintf (stderr, " *** get_ECSU_data_type unknown %c\n", ch);
	n = m_type (c, "unknown ecsu");
	break;
      case 'T':
	n = m_type (c, "union");
	break;
      case 'U':
	n = m_type (c, "struct");
	break;
      case 'V':
	n = m_type (c, "class");
	break;
      case 'W':
	n = m_type (c, "enum");
	get_enum_size_type (c);
	break;
      case 'X':
	n = m_type (c, "coclass");
	break;
      case 'Y':
	n = m_type (c, "cointerface");
	break;
    }
  return gen_binary (c->gc, eMST_ecsu, n, get_scoped_name (c));
}

static uMToken *
get_string_literal_type (sMSCtx *c)
{
  uMToken *n = NULL;
  char ch = GET_CHAR (c);
  if (ch == 0)
    {
      c->err = 2;
      return NULL;
    }
  if (ch == '_')
    {
      INC_CHAR (c);
      n = m_cv (c, "const"); 
    }
  ch = GET_CHAR (c);
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  switch (ch)
    {
      case '0':
	return m_combine (c, n, m_type (c, "char"));
      case '1':
	return m_combine (c, n, m_type (c, "wchar_t"));
    }
  fprintf (stderr, " *** get_string_literal_type unknown '_%c'\n", ch);
  return NULL;
}

static uMToken *
get_enum_size_type (sMSCtx *c)
{
  uMToken *n = NULL;
  switch (GET_CHAR (c))
    {
      case 0:
	c->err = 2;
	return NULL;
      case '0':
	n = m_type (c, "char");
	break;
      case '1':
	n = m_type (c, "unsigned char");
	break;
      case '2':
	n = m_type (c, "short");
	break;
      case '3':
	n = m_type (c, "unsigned short");
	break;
      case '4':
	n = m_type (c, "int");
	break;
      case '5':
	n = m_type (c, "unsigned int");
	break;
      case '6':
	n = m_type (c, "long");
	break;
      case '7':
	n = m_type (c, "unsigned long");
	break;
      default:
	fprintf (stderr, " *** get_enum_size_type unknown ,%c'\n", GET_CHAR (c));
	return NULL;
    }
  INC_CHAR (c);
  return n;
}

static uMToken *
get_this_type (sMSCtx *c)
{
    return get_indirect_data_type (c, NULL, (char)0, NULL, 1);
}

static uMToken *
get_calling_convention (sMSCtx *c)
{
  char ch = GET_CHAR (c);

  if (ch == 0)
    {
      c->err = 2;
      return NULL;
    }
  INC_CHAR (c);
  switch(ch)
    {
      case 'A': case 'B':
	return m_cv (c, "__cdecl");
      case 'C': case 'D':
	return m_cv (c, "__pascal");
      case 'E': case 'F':
	return m_cv (c, "__thiscall");
      case 'G': case 'H':
	return m_cv (c, "__stdcall");
      case 'I': case 'J':
	return m_cv (c, "__fastcall");
      case 'K': case 'L':
	return m_cv (c, "");
      case 'M':
	return m_cv (c, "__clrcall");
    }
  fprintf (stderr, " *** get_calling_convention ,%c' unknown.\n", ch);
  return NULL;
}

static uMToken *
get_throw_types (sMSCtx *c)
{
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return m_throw (c, m_rframe (c, NULL));
    }
  if (GET_CHAR (c) == 'Z')
    {
      INC_CHAR (c);
      return m_name (c, "");
    }
  return m_throw (c, m_rframe (c, get_argument_types (c)));
}

static uMToken *
get_argument_types (sMSCtx *c)
{
  char ch = GET_CHAR (c);
  uMToken *n = NULL;

  if (ch == 'X')
    {
      INC_CHAR (c);
      return m_element (c, m_type (c, "void"));
    }
  if (ch == 'Z')
    {
      INC_CHAR (c);
      return m_element (c, m_type (c, "..."));
    }
  n = get_argument_list (c);
  if (!n || c->err)
    return n;
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return n;
    }
  if (GET_CHAR (c) == '@')
    {
      INC_CHAR (c);
      return n;
    }
  if (GET_CHAR (c) == 'Z')
    {
      INC_CHAR (c);
      return chain_tok (n, m_element (c, m_type (c, "...")));
    }
  fprintf (stderr, " *** get_argument_types unknown ,%c'\n", GET_CHAR (c));
  return NULL;
}

static uMToken *
get_return_type (sMSCtx *c)
{
  if (GET_CHAR (c) == '@')
    {
      INC_CHAR (c);
      return m_name (c, "");
    }
  return get_data_type (c);
}

static int
get_number_of_dimensions (sMSCtx *c)
{
  int ret = 0;
  if (GET_CHAR (c))
    return 0;
  if (GET_CHAR (c) >= '0' && GET_CHAR (c) <= '9')
    {
      ret = (int) (GET_CHAR (c) - '0') + 1;
      INC_CHAR (c);
      return ret;
    }
  while (GET_CHAR (c) != '@')
    {
      if (GET_CHAR (c) == 0)
	return 0;
      if (GET_CHAR (c) < 'A' || GET_CHAR (c) > 'P')
	return -1;
      ret <<= 4;
      ret += (int) (GET_CHAR (c) - 'A');
      INC_CHAR (c);
    }
  if (GET_CHAR (c) == '@')
    {
      INC_CHAR (c);
      return ret;
    }
  return -1;
}

static uMToken *
get_array_type (sMSCtx *c, uMToken *superType)
{
  uMToken *n = NULL, *h = NULL;
  int dims;

  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      if (superType)
	return m_combine (c, m_rframe (c, superType), m_array (c, NULL));
      return m_array (c, NULL);
    }
  dims = get_number_of_dimensions (c);
  if ( dims < 0)
    dims=0;
  if (!dims)
    {
      c->err = 2;
      return get_basic_data_type (c, m_array (c, NULL));
    }
  if (superType && (MTOKEN_FLAGS(superType)&MTOKEN_FLAGS_ARRAY))
    h = m_array (c, NULL);
  do {
    n = m_array (c, get_dimension (c, 0, 0));
    if (!h)
      h = n;
    else
      h = m_combine (c, h, n);
  } while (--dims != 0);
  if (superType)
    {
      if (!(MTOKEN_FLAGS(superType)&MTOKEN_FLAGS_ARRAY))
	superType = m_rframe (c, superType);
      h = m_combine (c, superType, h);
    }
  n = get_primary_data_type (c, h);
  MTOKEN_FLAGS (n) |= MTOKEN_FLAGS_ARRAY;
  return n;
}

static uMToken *
get_argument_list (sMSCtx *c)
{
  uMToken *n = NULL, *h = NULL;
  int idx;
  
  if (c->err)
    return NULL;
  do {
    h = NULL;
    if (GET_CHAR (c) == '@' || GET_CHAR (c) == 'Z')
      return n;
    if (GET_CHAR (c) == 0)
      {
	c->err = 2;
	return n;
      }
    idx= (int) (GET_CHAR (c) - '0');
    if (idx < 0 || idx > 9)
      {
	const char *svName = c->pos;
	h = get_primary_data_type (c, NULL);
	if ((size_t) (c->pos - svName)>1 && c->pArgList->count < 10)
	  {
	    c->pArgList->arr[c->pArgList->count]=h;
	    c->pArgList->count += 1;
	  }
      }
    else
      {
	INC_CHAR (c);
	h = c->pArgList->arr[idx];
      }
    h = m_element (c, h);
    n = chain_tok (n, h);
  } while (c->err != 2);
  return n;
}

static uMToken *
get_vdisp_map_type (sMSCtx *c, uMToken *superType)
{
  uMToken *n = superType;
  uMToken *h = get_scope (c);
  h = m_combine (c, m_name (c, "for"), h);
  h = m_frame (c, h);
  n = m_combine (c, n, h);
  if (GET_CHAR (c) =='@')
    INC_CHAR (c);
  return n;
}

static uMToken *
get_ext_data_type (sMSCtx *c, uMToken *superType)
{
  uMToken *dt = NULL,*n = NULL;
  dt = get_data_type (c);
  n = get_indirect_data_type (c, NULL, (char)0, NULL, 0);
  if (superType)
    n = m_combine (c, n, superType);

  return m_combine (c, dt, n);
}

static uMToken *getVCallThunkType(sMSCtx *c)
{
  if (GET_CHAR (c) == 0)
    {
      c->err = 2;
      return NULL;
    }
  if (GET_CHAR (c) != 'A')
    {
      fprintf (stderr, " *** getVCallThunkType unknown '%c'\n", GET_CHAR (c));
      return NULL;
    }
  INC_CHAR (c);
  return m_cv (c, "{flat}");
}

static uMToken *
get_vftable_type (sMSCtx *c, uMToken *superType)
{
  uMToken *n = superType;
  if (c->err || GET_CHAR (c) == 0)
    {
      c->err = 2;
      return n;
    }
  n = m_combine (c, get_indirect_data_type (c, NULL, (char)0, NULL, 0), n);
  if (c->err == 2 || !n)
    return n;
  if (GET_CHAR (c) != '@')
    {
      n = m_combine (c, n, m_name (c, "{for "));
      while (c->err == 0)
        {
	  if (GET_CHAR (c) ==0 || GET_CHAR (c) =='@')
	    break;
	  n = m_combine (c, n, m_lexical_frame (c, get_scope (c)));
	  if (GET_CHAR (c) == '@')
	    INC_CHAR (c);
	  if (c->err == 0 && GET_CHAR (c) != '@')
	    n = m_combine (c, n, m_name (c, "s "));
        }
      if (c->err == 0)
        {
	  if (GET_CHAR (c) == 0)
	    c->err = 2;
	  n = m_combine (c, n, m_name (c, "}"));
        }
      if (GET_CHAR (c) != '@')
	return n;
    }
  INC_CHAR (c);
  return n;
}

static uMToken *
compose_decl (sMSCtx *c, uMToken *symbol)
{
  uMToken *n = NULL;
  int et = get_encoded_type (c);
  int nIsUDC = (symbol && (MTOKEN_FLAGS (symbol) & MTOKEN_FLAGS_UDC)) ? 1 : 0;
  if (et==0xffff)
    return NULL;
  if (et==0xfffe)
    {
      c->err = 2;
      return symbol;
    }
  if (et==0xfffd)
    return symbol;
  if ((et&0x8000)==0)
    {
      n = symbol;
      if ((et&0x7c00)==0x6800)
	return get_vftable_type (c, n);
      if ((et&0x7c00)==0x7000)
	return get_vftable_type (c, n);
      if ((et&0x7c00)==0x6000)
        {
	  uMToken *ll = m_element (c, get_dimension (c, 0, 0));
	  ll = m_frame (c, ll);
	  return m_combine (c, n, ll);
        }
      if ((et&0x7c00)==0x7c00)
	return get_vdisp_map_type (c, n);
      if ((et&0x7c00)==0x7800)
	return n;
      n = get_ext_data_type (c, n);
      if ((et&0x6000)!=0)
        {
	  if ((et&0x1000))
	    n = m_combine (c, m_colon (c, "[thunk]"), n);
	  return n;
        }
      n = m_combine (c, m_cv (c, "static"), n);
      if ((et&0x700) == 0x400 || (et&0x700) == 0x500)
	n = m_combine (c, m_cv (c, "virtual"), n);
      switch ((et&0x1800))
        {
          case 0x800:
	    n = m_combine (c, m_colon (c, "private"), n);
	    break;
	  case 0x1000:
	    n = m_combine (c, m_colon (c, "protected"), n);
	    break;
	  case 0x0:
	    n = m_combine (c, m_colon (c, "public"), n);
	    break;
        }
      if ((et&0x400))
	n = m_combine (c, m_colon (c, "[thunk]"), n);
      return n;
    }
  if ((et&0x1f00)==0x1000 || (et&0x1f00)==0x1400)
    {
      n = symbol;
      if ((et&0x6000)!=0 || (et&0x7f00)==0x1400)
	n = m_combine (c, n, m_name (c, "local_static_destructor_helper"));
      n = get_ext_data_type (c, n);
      symbol = NULL;
   }
  else if ((et&0x1f00)==0x1500 || (et&0x1f00)==0x1600)
    {
      n = symbol;
      symbol = NULL;
      if ((et&0x1f00)==0x1500) 
        n = m_combine (c, n, m_name (c, "template_static_data_member_constructor_helper"));
      else if ((et&0x1f00)==0x1600)
	n = m_combine (c, n, m_name (c, "template_static_data_member_destructor_helper"));
    }
  else
    {
      if ((et&0x4000)!=0)
	n = get_based_type (c);
      if ((et&0x1800)==0x1800)
        {
	  uMToken *hh = NULL;
	  hh = m_element (c, get_dimension (c, 0, 0));
	  hh = chain_tok (hh, m_element (c, getVCallThunkType (c)));
	  n = m_combine (c, symbol,
	    m_frame (c, hh));
	  n = m_combine (c, get_calling_convention (c), n);
        }
      else
        {
	  uMToken *h = NULL;
	  uMToken *n1 = NULL;
	  uMToken *n2 = NULL;
	  uMToken *n3 = NULL;
	  if ((et&0x1000)!=0 || (et&0x1c00)==0xc00)
	    {
	      if ((et&0x1f00)==0xd00)
		n1 = get_dimension (c, 1, 0);
	      n2 = get_dimension (c, 1, 0);
	    }
	  if (((et&0x1800)==0x800) && (et&0x700)!=0x200)
	    n3 = get_this_type (c);
	  n = m_combine (c, get_calling_convention (c), n);
	  if (symbol)
	    n = m_combine (c, n, symbol);

	  if (nIsUDC)
	    n = m_combine (c, n, get_return_type (c));
	  h = get_return_type (c);
	  if (((et&0x1800)!=0x800 ? (et&0x1000)!=0 : (et&0x400)!=0))
	    {
	      if (((et&0x1800)==0x800) && (et&0x700)==0x500)
	        {
		  n2 = chain_tok (
		    m_element (c, n1),
		    m_element (c, n2));
		  n2 = m_frame (c, n2);
		  n = m_combine (c, n, m_combine (c, m_name (c, "vtordisp"), n2));
	        }
	      else
	        {
		  n2 = m_frame (c, m_element (c, n2));
		  n = m_combine (c, n, m_combine (c, m_name (c, "adjustor"), n2));
	        }
	    }
	  n = m_combine (c, n, m_rframe (c, get_argument_types (c)));
	  if (((et&0x1800)==0x800) && (et&0x700)!=0x200)
	    n = m_combine (c, n, n3);
	  n = m_combine (c, n, get_throw_types (c));
	  if (h)
	    n = m_combine (c, h, n);
        }
    }
  if ((et&0x1800)!=0x800) {
    if ((et&0x1000))
      n = m_combine (c, m_colon (c, "[thunk]"), n);
    return n;
  }
  switch ((et&0x700))
    {
      case 0x200:
	n = m_combine (c, m_cv (c, "static"), n);
	break;
      case 0x100:
      case 0x400:
      case 0x500:
	n = m_combine (c, m_cv (c, "virtual"), n);
	break;
    }
  switch ((et&0xc0))
    {
      case 0x40:
	n = m_combine (c, m_colon (c, "private"), n);
	break;
      case 0x80:
	n = m_combine (c, m_colon (c, "protected"), n);
	break;
      case 0x0:
	n = m_combine (c, m_colon (c, "public"), n);
	break;
    }
  if ((et&0x400))
    n = m_combine (c, m_colon (c, "[thunk]"), n);
  return n;
}

static int
get_encoded_type (sMSCtx *c)
{
  int ret;

  for(;;)
    {
      ret = 0;
      if (GET_CHAR (c) == '_')
        {
	  INC_CHAR (c);
	  ret = 0x4000;
        }
      if (GET_CHAR (c) >= 'A' && GET_CHAR (c) <= 'Z')
        {
	  int chc = (int) (GET_CHAR (c) - 'A');
	  INC_CHAR (c);
	  if ((chc & 1) == 0)
	    ret |= 0x8000;
	  else
	    ret |= 0xa000;
	  if (chc >= 0x18)
	    return ret;
	  ret |= 0x800;
	  switch((chc&0x18))
	    {
	      case 0:
		ret |= 0x40;
		break;
	      case 8:
		ret |= 0x80;
		break;
	      case 0x10:
		break;
	    }
	  switch((chc&6))
	    {
	      case 0:
		return ret;
	      case 2:
		return ret|0x200;
	      case 4:
		return ret|0x100;
	      case 6:
		return ret|0x400;
	    }
	  return 0xffff;
        }
      if (GET_CHAR (c) != '$')
        {
	  INC_CHAR (c);
	  switch(c->pos[-1])
	    {
	      case '0':
		return 0x800;
	      case '1':
		return 0x1000;
	      case '2':
		return 0;
	      case '3':
		return 0x4000;
	      case '4':
		return 0x2000;
	      case '5':
		return 0x6000;
	      case '6':
		return 0x6800;
	      case '7':
		return 0x7000;
	      case '8':
		return 0x7800;
	      case '9':
		return 0xfffd;
	      case 0:
		DEC_CHAR (c);
		return 0xfffe;
	    }
	  DEC_CHAR (c);
	  return 0xffff;
        }
      INC_CHAR (c);
      switch(GET_CHAR (c))
        {
          case 'A':
	    INC_CHAR (c);
	    return ret|0x9000;
	  case 'B':
	    INC_CHAR (c);
	    return ret|0x9800;
	  case 'C':
	    INC_CHAR (c);
	    return ret|0x7c00;
	  case 'D':
	    INC_CHAR (c);
	    return ret|0x9100;
	  case 'E':
	    INC_CHAR (c);
	    return ret|0x9200;
	  case 0:
	    INC_CHAR (c);
	    return 0xfffe;
	  case '0':
	    INC_CHAR (c);
	    return ret|0x8d40;
	  case '1':
	    INC_CHAR (c);
	    return ret|0xad40;
	  case '2':
	    INC_CHAR (c);
	    return ret|0x8d80;
	  case '3':
	    INC_CHAR (c);
	    return ret|0xad80;
	  case '4':
	    INC_CHAR (c);
	    return ret|0x8d00;
	  case '5':
	    INC_CHAR (c);
	    return ret|0xad00;
	  case '$':
	    if (c->pos[1] == 'P')
	      INC_CHAR (c);
	    break;
	  default:
	    return 0xffff;
        }
      INC_CHAR (c);
      switch(GET_CHAR (c))
        {
          case 'F': case 'G': case 'H': case 'I': case 'L': case 'M':
	    INC_CHAR (c);
	    break;
	  case 'J': case 'K': case 'N': case 'O':
	    INC_CHAR (c);
	    if (GET_CHAR (c) < '0' || GET_CHAR (c) > '9')
	      {
		INC_CHAR (c);
		return 0xffff;
	      }
	    {
	      int skip = (GET_CHAR (c) - '0') + 1;
	      SKIP_CHAR (c, skip);
	    }
	    break;
	  default:
	    INC_CHAR (c);
	    return ret;
        }
    }
}

static uMToken *
m_combine (sMSCtx *c, uMToken *l, uMToken *r)
{
  if (!l && !r)
    return NULL;
  if (!l)
    return r;
  if (!r)
    return l;
  return gen_binary (c->gc, eMST_combine, l, r);
}

static uMToken *
m_type (sMSCtx *c, const char *typname)
{
  return gen_name (c->gc, eMST_type, typname);
}

static uMToken *
m_cv (sMSCtx *c, const char *cv)
{
  return gen_name (c->gc, eMST_cv, cv);
}

static uMToken *
m_coloncolon (sMSCtx *c, uMToken *l, uMToken *r)
{
  if (!l)
    return r;
  if (!r)
    return l;
  return gen_binary (c->gc, eMST_coloncolon, l, r);
}

static uMToken *
m_element (sMSCtx *c, uMToken *el)
{
  return gen_unary (c->gc, eMST_element, el);
}

static uMToken *
m_array (sMSCtx *c, uMToken *dim)
{
  return gen_unary (c->gc, eMST_array, dim);
}

static uMToken *
m_scope (sMSCtx *c, uMToken *n)
{
  return gen_unary (c->gc, eMST_scope, n);
}

static uMToken *
m_oper (sMSCtx *c, uMToken *n)
{
  return gen_unary (c->gc, eMST_oper, n);
}

static uMToken *
m_name (sMSCtx *c, const char *str)
{
  return gen_name (c->gc, eMST_name, str);
}

static uMToken *
m_colon (sMSCtx *c, const char *str)
{
  return gen_name (c->gc, eMST_colon, str);
}

static uMToken *
m_opname (sMSCtx *c, const char *str)
{
  return gen_name (c->gc, eMST_opname, str);
}

static uMToken *
m_rtti (sMSCtx *c, const char *str)
{
  return gen_name (c->gc, eMST_rtti, str);
}

static uMToken *
m_frame (sMSCtx *c, uMToken *u)
{
  return gen_unary (c->gc, eMST_frame, u);
}

static uMToken *
m_rframe (sMSCtx *c, uMToken *u)
{
  return gen_unary (c->gc, eMST_rframe, u);
}

static uMToken *
m_ltgt (sMSCtx *c, uMToken *u)
{
  return gen_unary (c->gc, eMST_ltgt, u);
}

static uMToken *
m_throw (sMSCtx *c, uMToken *u)
{
  return gen_unary (c->gc, eMST_throw, u);
}

static uMToken *
m_lexical_frame (sMSCtx *c, uMToken *u)
{
  return gen_unary (c->gc, eMST_lexical_frame, u);
}
