Beispiel #1
0
bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out,
                                              const wchar_t *prefix) const {
    bool errored = false;
    parse_error_list_t errors;

    // Use empty string for the prefix if it's NULL.
    if (!prefix) prefix = L"";  //!OCLINT(parameter reassignment)

    // Parse the string as an argument list.
    parse_node_tree_t tree;
    if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, &errors,
                                symbol_freestanding_argument_list)) {
        // Failed to parse.
        errored = true;
    }

    if (!errored) {
        // Get the root argument list and extract arguments from it.
        assert(!tree.empty());  //!OCLINT(multiple unary operator)
        tnode_t<grammar::freestanding_argument_list> arg_list(&tree, &tree.at(0));
        while (auto arg = arg_list.next_in_list<grammar::argument>()) {
            const wcstring arg_src = arg.get_source(arg_list_src);
            if (parse_util_detect_errors_in_argument(arg, arg_src, &errors)) {
                errored = true;
            }
        }
    }

    if (!errors.empty() && out != NULL) {
        out->assign(errors.at(0).describe_with_prefix(
            arg_list_src, prefix, false /* not interactive */, false /* don't skip caret */));
    }
    return errored;
}
Beispiel #2
0
static int primary_expr(ParserState* ps, Ast** expr) {
	Ast* base = ps->curr;
	if (literal_expr(ps, &base)) { *expr = base; }
	else if (curris(ps, PLParen)) {
		enterblk();
		if (expression(ps, expr) && ps->curr == 0) {
			exitblk();
			ast_swap(base, *expr);
			free(*expr);
			*expr = base;
			return 1;
		}
		else {
			exitblk();
			parerr(ps, "invlaid syntax");
		}
	}
	else if (obj_literal_expr(ps, expr)) {}
	else if (array_literal_expr(ps, expr)) {}
	else if (curris(ps, TkId)) { *expr = base; step(ps); }
	// else if (stepifis(ps, RNew)) {}
	else return 0;

	while (indexing(ps, expr, base)
		|| dot_id(ps, expr, base)
		|| arg_list(ps, base)) {
	}

	return 1;
}
Beispiel #3
0
/* EBNF: args -> arg-list | empty */
static TreeNode * args (void)
{ TreeNode * t = NULL;

  while (token == COMMENT) unexpectedTokenHandling();
  if (token != RPAREN)
    t = arg_list();
  return t;
}
Beispiel #4
0
//过程调用语句
//<过程调用语句>::=<标识符>[<实在参数表>]
AST_node stat_procedure(AST_node t)
{
	printf("-------------------Procedure Call-----------\n");
	t->ast_type = CALL;
	if (symbol == LPARENT)
	{
		//参数表
		arg_list(t);
	}
	printf("----------------End of Procedure Call---------\n");
	return t;
}
Beispiel #5
0
//
// visit_operation
//
int Provides_Svnt_Impl::visit_operation (AST_Operation * node)
{
  const char * local_name = node->local_name ()->get_string ();

  Operation_Return_Type return_type (this->hfile_, this->sfile_);
  node->return_type ()->ast_accept (&return_type);

  this->hfile_
    << " " << local_name << " (";

  this->sfile_
    << " " << this->servant_name_ << "::" << local_name << " (";

  // Build the signature
  int arg_count = node->argument_count ();
  if (arg_count == 0)
  {
    this->hfile_ << "void";
    this->sfile_ << "void";
  }
  else
  {
    Operation_Argument_List arg_list (this->hfile_, this->sfile_);
    arg_list.visit_scope (node);
  }

  this->hfile_ << ");";
  this->sfile_ << "){";

  // Start passthrough to impl
  if (arg_count != 0)
    this->sfile_ << "return ";

  this->sfile_ << "this->impl_->" << local_name << " (";

  // Build the passthrough args
  if (arg_count != 0)
  {
    Operation_Impl_Args impl_args (this->sfile_);
    impl_args.visit_scope (node);
  }

  this->sfile_ << ");}";

  return 0;
}
Beispiel #6
0
void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags,
                                    std::vector<completion_t> *output_arg_list) {
    assert(output_arg_list != NULL);

    // Parse the string as an argument list.
    parse_node_tree_t tree;
    if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, NULL /* errors */,
                                symbol_freestanding_argument_list)) {
        // Failed to parse. Here we expect to have reported any errors in test_args.
        return;
    }

    // Get the root argument list and extract arguments from it.
    assert(!tree.empty());  //!OCLINT(multiple unary operator)
    tnode_t<grammar::freestanding_argument_list> arg_list(&tree, &tree.at(0));
    while (auto arg = arg_list.next_in_list<grammar::argument>()) {
        const wcstring arg_src = arg.get_source(arg_list_src);
        if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) {
            break;  // failed to expand a string
        }
    }
}
int main()
{
	printf("  WI  \n");
	printf("------\n");
	printf("\n");

	std::vector<WCHAR> commandBuffer(1000 + 1, L'\0');
	std::vector<WCHAR> trashBuffer(1000 + 1, L'\0');
	std::vector<WCHAR> textBuffer(1024 + 1, L'\0');

	int lineNumber = 1;

	size_t linePositionInText = 0;

	for (;;)
	{
		printf("%d: ", lineNumber);

		std::fill(commandBuffer.begin(), commandBuffer.end(), L'\0');
		wscanf(L"%1000[a-zA-Z0-9?,.!()%:- ]", commandBuffer.data());

		std::fill(trashBuffer.begin(), trashBuffer.end(), L'\0');
		fgetws(trashBuffer.data(), trashBuffer.size(), stdin); //NORMAL MAY BE

		if (wcscmp(commandBuffer.data(), L":done") == 0)
			break;

		if (wcsncmp(commandBuffer.data(), L":line ", 6) == 0)
		{
			swscanf(commandBuffer.data() + 6, L"%d", &lineNumber);

			if (lineNumber < 0 || lineNumber > 1000) return -1;

			if (lineNumber == 0)
			{
				lineNumber = 1;
			}

			linePositionInText = 0;

			for (int i = 0; i < lineNumber && linePositionInText < textBuffer.size(); linePositionInText++)
			{
				if (textBuffer[linePositionInText] == L'\n')
				{
					i++;
				}
			}

			continue;
		}

		// replace arguments
		int cnt = 0;

		for (WCHAR* ptr2 = commandBuffer.data();;)
		{
			ptr2 = wcschr(ptr2, L'%');
			if (ptr2 == NULL)
				break;

			if (ptr2[1] == L'%')  //%%???
			{
				if (ptr2[2] == L'x' || ptr2[2] == L's' || ptr2[2] == L'i' || ptr2[2] == L'd' || ptr2[2] == L'u' || ptr2[2] == L'l') break;
				for (int i = 0; i < wcslen(ptr2); i++)
				{
					ptr2[i] = ptr2[i + 1];  //1????
				}

				ptr2--;

				if (ptr2 < commandBuffer.data()) break;  //!!!!
			}
			else if (ptr2[1] == L'l')
			{
				cnt++;

				ptr2[1] = L'd';
			}
			else
			{
				for (int i = wcslen(ptr2) - 1; i >= 0; i--)
				{
					ptr2[i + 1] = ptr2[i];
				}
			}

			if (ptr2 >= commandBuffer.data() + commandBuffer.size() - 1) break;	//!!
			ptr2 += 2;
		}

		std::vector<int> arg_list(cnt, lineNumber);

		int space = _vsnwprintf(NULL, 0, commandBuffer.data(), reinterpret_cast<va_list>(arg_list.data()));

		textBuffer.insert(textBuffer.begin() + linePositionInText, space + 1, L'\0');

		_vsnwprintf(&textBuffer[linePositionInText], space, commandBuffer.data(), reinterpret_cast<va_list>(arg_list.data()));

		textBuffer[linePositionInText + space] = L'\n';
		
		linePositionInText += space + 1;

		lineNumber++;
	}

	wprintf(L"\n\n");
	wprintf(L"THE FINAL TEXT\n");
	wprintf(L"==============\n");

	wprintf(L"%s\n\n", textBuffer.data());

	return -14;
}
Beispiel #8
0
int phypp_main(int argc, char* argv[]) {
    if (argc < 2) {
        print("usage: make_shifts <ob_filter> [options]");
        return 0;
    }

    vec1u exclude;
    std::string helper;

    read_args(argc-1, argv+1, arg_list(exclude, helper));

    if (helper.empty()) {
        error("please provide the name of (one of) the helper target you used to "
            "calibrate the shifts (helper=...)");
        return 1;
    }

    helper = tolower(helper);

    std::string scis = argv[1];

    vec1d cent_ra, cent_dec;
    file::read_table("centroid_helper.txt", 0, cent_ra, cent_dec);

    vec1u ids = uindgen(cent_ra.size())+1;
    vec1u idex = where(is_any_of(ids, exclude));
    inplace_remove(ids, idex);
    inplace_remove(cent_ra, idex);
    inplace_remove(cent_dec, idex);

    std::ofstream cmb("combine.sof");

    vec1d shx, shy;

    vec1d x0, y0;
    bool first_line = true;
    uint_t nexp = 0;

    for (uint_t i : range(ids)) {
        std::string dir = scis+align_right(strn(ids[i]), 2, '0')+"/";
        print(dir);

        vec1s files = dir+file::list_files(dir+"sci_reconstructed*-sci.fits");
        inplace_sort(files);

        // Find out which exposures contain the helper target from which the
        // shifts were calibrated
        vec1u ignore;
        for (uint_t k : range(files)) {
            std::string f = files[k];
            fits::generic_file fcubes(f);

            vec1s arms(24);
            bool badfile = false;
            for (uint_t u : range(24)) {
                if (!fcubes.read_keyword("ESO OCS ARM"+strn(u+1)+" NAME", arms[u])) {
                    note("ignoring invalid file '", f, "'");
                    note("missing keyword 'ESO OCS ARM"+strn(u+1)+" NAME'");
                    ignore.push_back(k);
                    badfile = true;
                    break;
                }
            }

            if (badfile) continue;

            arms = tolower(trim(arms));

            vec1u ida = where(arms == helper);
            if (ida.empty()) {
                ignore.push_back(k);
            } else if (x0.empty()) {
                // Get astrometry of IFUs from first exposure
                // NB: assumes the rotation is the same for all exposures,
                // which is anyway what kmos_combine does later on.
                fcubes.reach_hdu(ida[0]+1);
                fits::wcs astro(fcubes.read_header());
                fits::ad2xy(astro, cent_ra, cent_dec, x0, y0);
            }
        }

        inplace_remove(files, ignore);

        if (files.empty()) {
            warning("folder ", dir, " does not contain any usable file");
            continue;
        }

        for (std::string f : files) {
            cmb << f << " COMMAND_LINE\n";
            ++nexp;
        }

        double dox = x0[0] - x0[i], doy = y0[0] - y0[i];

        if (first_line) {
            // Ommit first line which has, by definition, no offset
            first_line = false;
        } else {
            shx.push_back(dox);
            shy.push_back(doy);
        }

        if (file::exists(dir+"helpers/shifts.txt")) {
            vec1d tx, ty;
            file::read_table(dir+"helpers/shifts.txt", 0, tx, ty);
            for (uint_t j : range(tx)) {
                shx.push_back(dox+tx[j]);
                shy.push_back(doy+ty[j]);
            }
        }
    }

    note("found ", nexp, " exposures");

    cmb.close();

    auto truncate_decimals = vectorize_lambda([](double v, uint_t nd) {
        return long(v*e10(nd))/e10(nd);
    });

    shx = truncate_decimals(shx, 2);
    shy = truncate_decimals(shy, 2);

    file::write_table("shifts.txt", 10, shx, shy);

    return 0;
}
Beispiel #9
0
/*****************************************************************************
 *
 *  Given a mangled name in "src", unmangle it if necessary, putting the
 *  result in "dest", copying at most "maxlen" characters, including
 *  trailing null.  It is assumed that dest is bigger than src, and big
 *  enough for any function name + class name.
 *
 *  Returns zero if the name is not a legal mangled name.
 *
 */
umKind  unmangle(char   *   src,
         char   *   dest,
         unsigned   maxlen,
         char   *   classP,
         char   *   nameP,
         int        doArgs)
{
    char    *   srcP;           /* for scanning src */
    char    *   dstP;           /* for scanning dest */

    char    *   mainName;       /* the main name */
    char    *   className;      /* qualifying name, if present */

    char    *   dstClass;

    unsigned    len;
    int     avail;          /* chars left in dest */

    char        cfunc;          /* it is a const function */
    char        vfunc;          /* it is a volatile function */

    char        argBuff[MAXARGSLEN];    /* for arg tables */
    char        name[MAXNOTRUNC];

    umKind      kind;
    int     thunk;

    if  (src == 0)
        return  UM_NOT_MANGLED;

    if  (*src != '@')
    {
        strcpy(dest, src);
        return  UM_NOT_MANGLED;
    }

    if  (dest)
    {
        char         *  tmpp = dest;
        char    near *  endp = (char near *)tmpp + maxlen;

        do
        {
            *tmpp++ = 0;
        }
        while   ((char near *)tmpp < endp);
    }

    outerClass = 0;

    strncpy(name, src, MAXNOTRUNC-1); name[MAXNOTRUNC-1] = 0;

    /* Setup arg buffer variables */

    argBuffNext = argBuff;
    argBuffFree = MAXARGSLEN;

    /* See if this is a pascal (up-cased) mangled name */

    for (srcP = name+1; *srcP; srcP++)
    {
        if  (*srcP >= 'a' && *srcP <= 'z')
            goto NOT_PASCAL;
    }

    /* Entire name is uppercase, assume it's been pascal-ized */

    for (srcP = name+1; *srcP; srcP++)
        *srcP = tolower(*srcP);

NOT_PASCAL:

    /* Find the second '@' or '$' separator */

    srcP = name + 1;
    if  (srcP[0] == '$' && (srcP[1] == 'b' || srcP[1] == 'o'))
        srcP += 3;

    while   (*srcP)
    {
        if  (*srcP == '@' || *srcP == '$')
            break;

        if  (*srcP == '%')
        {
            srcP = skipTemplateName(srcP);
            continue;
        }

        srcP++;
    }

    /* Can't be mangled if second '@' or '$' is missing */

    if  (*srcP == 0)
    {
        /* Special check: truncated template class name */

        if  (src[0] == '@' && src[1] == '%')
        {
            isTemplateName = 0;

            if  (!setjmp(jmpb))
            {
                if  (dest )
                    copyClassName(dest,  src+1, maxlen);
                if  (nameP)
                    copyClassName(nameP, src+1, maxlen);

                return UM_NOT_MANGLED;
            }

            /* Is this a template name? */

            if  (isTemplateName)
            {
                if  (dest )
                    strcat(dest,  "...>");
                if  (nameP)
                    strcat(nameP, "...>");

                return UM_NOT_MANGLED;
            }
        }

    NOT_MANGLED:

        if  (dest )
            strcpy(dest,  src);
        if  (nameP)
            strcpy(nameP, src);

        return UM_NOT_MANGLED;
    }

    /* See if this is a member function */

    if  (*srcP == '@')
    {
        char    *   tmpP;

        /* See if this is a nested class member */

        for (tmpP = srcP + 1; *tmpP; tmpP++)
        {
            if  (*tmpP == '@')
            {
                /* Remember last '@' separator */

                srcP = tmpP;
            }
            else if (*tmpP == '$')
            {
                break;
            }
            else if (*tmpP == '%')
            {
                /* This is a template class name */

                tmpP = skipTemplateName(tmpP);
            }
        }

        *srcP++ = 0;            /* null-terminate class name */
        className = name + 1;

        while   (isdigit(*srcP))    /* 'huge' class? */
            srcP++;

        /* Now pointing to the main name */

        mainName = srcP;

        /* find '$' separator */

        if  (srcP[0] == '$' && (srcP[1] == 'b' || srcP[1] == 'o'))
            srcP += 3;

        while   (*srcP && *srcP != '$')
            ++srcP;

        if  (*srcP == 0)
        {
            /* Must be a static member */

            kind = UM_STATIC_DM;
            goto COPYIT;
        }
    }
    else
    {
        className = 0;          /* not a member function */
        mainName = name + 1;

        if  (mainName == srcP)
        {
            /* 'main' name appears to be empty - is this a thunk? */

            if  (strncmp(srcP, "$vc1$", 5))
                goto NOT_MANGLED;

            srcP += 5;
            kind  = UM_THUNK;
            thunk = 1;

            goto COPYIT;
        }
    }

    /* null-terminate "main" name and point to encoding of type definition */

    *srcP++ = 0;

    /* Now figure out what kind of beast this is */

    if  (mainName[0] == '$')        /* check for special names */
    {
        mainName += 2;

        if  (mainName[-1] == 'b')   /* ctor, dtor, or operator */
        {
            if  (strcmp(mainName, "ctr") == 0)
                kind = UM_CONSTRUCTOR;
            else if (strcmp(mainName, "dtr") == 0)
                kind = UM_DESTRUCTOR;
            else
                kind = UM_OPERATOR;
        }
        else
            kind = UM_CONVERSION;   /* type conversion */
    }
    else
        kind = UM_MEMBER_FN;        /* "ordinary" name */

    /* Copy class and main name to caller's buffers (if applicable) */

COPYIT:

    dstClass = 0;

    if  (classP)
        copyClassName(classP, className, maxlen);

    if  ( nameP)
    {
        if  (dest == 0)
        {
            dstP   = nameP;
            doArgs = 0;
            avail  = 63;

            if  (setjmp(jmpb))
                goto trunc_exit;

            goto DONAME;
        }

        strcpy( nameP,  mainName);
    }

    if  (dest == 0)
        return  kind;

    /* Setup destination pointer, and remaining dest. buffer length */

    dstP  = dest;
    avail = maxlen - 5;     /* Leave some extra room at the end */

    /* Prepare the quick exit route */

    if  (setjmp(jmpb))
    {
trunc_exit:
        /* Make sure there's room for the "..." */

        if  (maxlen)
            dest[maxlen - 4] = 0;

        strcat(dstP, "...");
        return  kind;
    }

    /* Setup arg buffer variables */

    argBuffNext = argBuff;
    argBuffFree = MAXARGSLEN;

    /* emit qualifying name, if any */

#ifdef  IN_MOJO
    if  (className && !(doArgs & DoArgsNoClassPrefix))
#else
    if  (className)
#endif
    {
        dstClass = dstP;

        len  = copyClassName(dstP, className, avail);
        dstP   += len;
        *dstP++ = ':';
        *dstP++ = ':';
        avail   -= len + 2;
    }

    /* emit the main name */

DONAME:

    switch  (kind)
    {
    case    UM_DESTRUCTOR:
        *dstP++ = '~';

    case    UM_CONSTRUCTOR:
        if  (dstClass)
        {
            char    *   temp = dstP - 2;

            if  (temp[-1] == ':')
                temp--;

            *temp = 0;
            strcpy(dstP, lastClassName(dstClass));
            dstP += strlen(dstP);
            *temp = ':';
        }
        else
        {
            dstP += copyClassName(dstP, className, avail);
        }
        break;

    case    UM_OPERATOR:
        strcpy(dstP, "operator ");
        dstP += 9;
        strcpy(dstP, translate_op(mainName));
        break;

    case    UM_CONVERSION:
        strcpy(dstP, "operator ");
        dstP  += 9;
        avail -= 9;
        arg_type(&mainName, &dstP, &avail, 0);
        kind = UM_CONVERSION;
        break;

    case    UM_MEMBER_FN:
    case    UM_STATIC_DM:
        strcpy(dstP, mainName);
        break;

    case    UM_THUNK:
        convertThunkName(dstP, srcP, thunk);
        break;
    }

#ifdef  IN_MOJO
    if  (!(doArgs & DoArgsArguments))
        return  kind;
#else
    if  (!doArgs)
        return  kind;
#endif

    /* Point to end of destination and figure available space */

    dstP += strlen(dstP);
    avail = dest + maxlen - dstP - 1;
    if  (avail < 0)
        avail = 0;

    /* check for a const/volatile function */

    cfunc = vfunc = 0;

    for (;;)
    {
        if  (*srcP == 'x')
            cfunc = 1;
        else if (*srcP == 'w')
            vfunc = 1;
        else
            break;
        ++srcP;
    }

    /* handle argument list */

    if  (*srcP == 'q')
    {
        ++srcP;
        arg_list(&srcP, &dstP, &avail);
    }

    /* tack on const/volatile function modifier */

    if  (cfunc)
    {
        if  (avail < 6)
            goto trunc_exit;
        strcat(dstP, " const");
    }

    if  (vfunc)
    {
        if  (avail < 9)
            goto trunc_exit;
        strcat(dstP, " volatile");
    }

    return  kind;
}
Beispiel #10
0
static  void    near    arg_type(char **src, char **dest, unsigned *avail, char *outer)
{
    char    *   p;
    char    *   s = *src;
    char    *   d = *dest;
    unsigned    len;
    char    *   spec_ptr = 0;

    for (;;)                /* emit type qualifiers */
    {
        switch  (*s)
        {
        case 'u':
            if  (s[1] == 'p')
                spec_ptr = " huge ";
            else if (s[1] == 'r')
                spec_ptr = " _seg ";
            else
                p = "unsigned ";
            break;

        case 'z':
            p = "";
            break;

        case 'x':
            p = "const ";
            break;

        case 'w':
            p = "volatile ";
            break;

        default:
            goto    DONE_QUALS;
        }

        s++;                /* skip qualifier char */

        if  (!spec_ptr)
        {
            len = strlen(p);
            if  (*avail >= len)
            {
                strcpy(d, p);
                d += len;
                *avail -= len;
            }
            else
                *avail = 0;
            *d = 0;
        }
    }

DONE_QUALS:

    switch  (*s)                /* check for built-in type */
    {
        case 'v': p = "void";       break;
        case 'c': p = "char";       break;
        case 'b': p = "wchar_t";    break;
        case 's': p = "short";      break;
        case 'i': p = "int";        break;
        case 'l': p = "long";       break;
        case 'f': p = "float";      break;
        case 'd': p = "double";     break;
        case 'g': p = "long double";    break;
        case 'e': p = "...";        break;
        default:
            goto    NOT_BUILT_IN;
    }
    len = strlen(p);
    if  (*avail >= len)
    {
        strcpy(d, p);
        d += len;
        *avail -= len;
    }
    else
        *avail = 0;
    s++;

    goto    DONE;

NOT_BUILT_IN:

    if  (isdigit(*s))           /* enum or class name */
    {
        class_name(&s, &len);

        if  (*avail >= len)
        {
            unsigned    len;

            len = copyClassName(d, s, *avail);
            d += len;
            *avail -= len;
        }
        else
            *avail = 0;

        s += len + 1;
    }
    else if (*s == 'p' || *s == 'r' || *s == 'm' || *s == 'n')
    {
        /* ptr or ref to type */

        short       is_func;
        short       is_ref = 0;
        unsigned    len;

        char        cfunc;      /* it is a const function */
        char        vfunc;      /* it is a volatile function */

        char    *   p;

        if  (!spec_ptr)
        {
            is_ref = (*s == 'r' || *s == 'm');
            if  (*s == 'p' || *s == 'r')
                spec_ptr = " near";
            else
                spec_ptr = " far";
        }

        /* look-ahead to see if this is a const/volatile function */

        p = ++s;
        cfunc = vfunc = 0;

        for (;;)
        {
            if  (*p == 'x')
                cfunc = 1;
            else if (*p == 'w')
                vfunc = 1;
            else
                break;
            ++p;
        }

        is_func = 0;

        if  (*p == 'q')
        {
            is_func++;      /* if not, ignore cfunc/vfunc */
            spec_ptr++;     /* omit leading blank */
            s = p;
        }

        arg_type(&s, &d, avail, spec_ptr);

        if  (is_func)
        {
            if  (cfunc && *avail > 6)
            {
                strcpy(d, " const");
                d      += 6;
                *avail -= 6;
            }

            if  (vfunc && *avail > 9)
            {
                strcpy(d, " volatile");
                d      += 9;
                *avail -= 9;
            }
        }
        else
        {
            if  (! isalnum(d[-1]))
                ++spec_ptr; /* omit leading blank */

            len = strlen(spec_ptr);
            if  (*avail > len)
            {
                strcpy(d, spec_ptr);
                d += len;
                *d++ = is_ref ? '&' : '*';
                *avail -= len + 1;
            }
        }
    }
    else if (*s == 'a')         /* array of type */
    {
        char dims[90];
        int i = 0;
        do
        {
            dims[i++] = '[';
            if  (*++s == '0')
                ++s;        /* 0 size means unpsecified */
            while(*s && *s != '$')  /* collect size, up to '$' */
                dims[i++] = *s++;
            if (*s) ++s;
                dims[i++] = ']';
        }
            while (*s == 'a');  /* collect all dimensions */
        dims[i] = '\0';
        arg_type(&s, &d, avail, 0);
        if  (*avail >= i + 2)
        {
            strcpy(d, dims);
            d += i;
            *avail -= i;
        }
        else if (*avail >= 2)
        {
            *d++ = '[';
            *d++ = ']';
            *d = '\0';
            *avail -= 2;
        }
        else
            *avail = 0;
    }
    else if (*s == 'q')         /* function type */
    {
        /*
         *  We want the return type first, but find it last.
         *  So we emit all but the return type, get the return
         *  type, then shuffle to get them in the right place.
         *
         */

        char    *   start = d;

        ++s;
        if  (*avail >= 3)
        {
            *d++ = '(';
            *avail -= 1;

            if  (outer)
            {
                if  (outerClass)
                {
                    len = copyClassName(d, outer, *avail);

                    d      += len;
                    *avail -= len;

                    if  (*avail >= 2)
                    {
                        strcpy(d, "::");
                        d      += 2;
                        *avail -= 2;
                    }
                }
                else
                {
                    unsigned    len = strlen(outer);

                    if  (*avail >= len+2)
                    {
                        strcpy(d, outer);
                        d      += len;
                        *avail -= len;
                    }
                }
            }

            *d++ = '*';
            *d++ = ')';
            *avail -= 2;
            *d = '\0';
        }
        else
            *avail = 0;
        arg_list(&s, &d, avail);
        if  (*s == '$')     /* flags the return type */
        {
            unsigned    ret_len;
            char    *   ret_type;

            ++s;
            ret_type = d;
            arg_type(&s, &d, avail, 0); /* return type */
            ret_len = strlen(ret_type);
            if  (ret_len < 64)      /* check length */
            {
                char        ret_buff[64];

                strcpy(ret_buff, ret_type);
                memmove(start + ret_len, start, ret_type-start);
                memmove(start, ret_buff, ret_len);
            }
        }
    }
    else if (*s == 'M')         /* member pointer type */
    {
        char    *   classPtr;
        unsigned    classLen;

        int     memberFn;
        char    *   outerPtr;

        /* Extract and save the position of the class name */

        classPtr = ++s;
        class_name(&classPtr, &classLen);
        s += classLen + 1;

        /* Is this a pointer to member function? */

        memberFn = 0;
        outerPtr = 0;

        if  (*s == 'q')
        {
            memberFn++;

            /* Use 'class::' for the 'outer' parameter */

            outerPtr   = classPtr;
            outerClass = 1;
        }

        /* Now decode the 'pointed-to' type */

        arg_type(&s, &d, avail, outerPtr);

        outerClass = 0;

        if  (!memberFn)
        {
            if  (*avail)
            {
                *d++ = ' ';
                *avail -= 2;
            }

            len =  copyClassName(d, classPtr, *avail);

            d      += len;
            *avail -= len;

            if  (*avail >= 3)
            {
                strcpy(d, "::*");
                d      += 3;
                *avail -= 3;
            }
        }
    }
    else if (*s)
    {
        longjmp(jmpb, 1);   /* malformed string, presumably truncated */
    }

DONE:

    *d = '\0';
    *src = s;
    *dest = d;
    return;
}