view src/expression/expression.apd @ 1:4a9dcbd9e54f

-files of 0.13 beta -fixes so that it now compiles with the current dmd version
author marton@basel.hu
date Tue, 05 Apr 2011 20:44:01 +0200
parents
children
line wrap: on
line source

/*  Ddbg - Win32 Debugger for the D programming language
 *  Copyright (c) 2007 Jascha Wetzel
 *  All rights reserved. See LICENSE.TXT for details.
 */

APDProperties
{
    parser_type = lr
}

APDDeclaration
{
module expression.expression_apd;
    
import codeview.decl;
import codeview.codeview;
import expression.evaluationcontext;
import expression.datahandler;
import util;

//import internal.aaA;

	struct aaA
	{
	    aaA *left;
	    aaA *right;
	    hash_t hash;
	    /* key   */
	    /* value */
	}

        struct BB
	{
	    aaA*[] b;
	    size_t nodes;       // total number of aaA nodes
	}

import std.demangle;
import std.stdio;

import win32.winnt;

/**********************************************************************************************

**********************************************************************************************/
bool castArrayIndex(ubyte[] data, string type, out size_t index)
{
    assert(type.length>0);
    switch ( type[0] )
    {
        case 'a':
            if ( *cast(char*)data.ptr < 0 )
                throw new EvaluationException("Cannot access array with negative index");
        case 'h':
            index = *cast(ubyte*)data.ptr;
            return true;
        case 's':
            if ( *cast(short*)data.ptr < 0 )
                throw new EvaluationException("Cannot access array with negative index");
        case 't':
            index = *cast(ushort*)data.ptr;
            return true;
        case 'i':
            if ( *cast(int*)data.ptr < 0 )
                throw new EvaluationException("Cannot access array with negative index");
        case 'k':
            index = *cast(uint*)data.ptr;
            return true;
        case 'l':
            if ( *cast(long*)data.ptr < 0 )
                throw new EvaluationException("Cannot access array with negative index");
        case 'm':
            index = cast(size_t)*cast(ulong*)data.ptr;
            return true;
        default:
    }
    return false;
}

/**********************************************************************************************

**********************************************************************************************/
//    debug = parser;
unittest
{
    bool test(string expr)
    {
        SyntaxTree* root;
        bool success;
        try success = parse(expr, root);
        catch ( ParserException e ) {
            debug(parser) writefln("%s", e.toString);
            return false;
        }
        return success;
    }

    assert(test("a.bc"));
    assert(test("bc[def..ghij]"));
    assert(!test("\"a\\\"\\\u2f85qwe '\""));
    assert(test("a.bc[def..ghij].klmno.pqrstu[0..'a'][\"a\\\"\\u2f85qwe '\"]"));
    assert(!test("a.bc.[def..ghij]"));
    assert(!test("[def..ghij]"));
    assert(!test("a."));
    assert(test("asdf[0][i].qwer"));
    assert(test("cast(uint)asdf"));
    assert(test("(cast(mystruct)asdf).qwer"));
    assert(test("(cast(string[])asdf)[0][2..13]"));
    assert(test("(cast(char[string])asdf)[\"qwer\"]"));
    assert(!test("(cast(mystruct)asdf).(cast(char[][])qwer)[0]"));
    assert(test("cast(float)asdf.qwer"));
    assert(!test("cast.qwer"));
    assert(!test("cast(uint).qwer"));
    assert(!test("cast(asdf.qwer).qwer"));
    assert(test("(cast(string)mystruct.tzui)[0..4]"));
    assert(test("cast(Vec!(float,3))mystruct"));
    assert(test("cast(Vec!(float,3,\"asdf\",Vec!(float)))mystruct"));
    assert(test("vertices[inds[10193]].y"));
    assert(test("*cast(uint*)#eax"));
}

}

Whitespace
{
    regexp("[\\t\\n\\r ]+");
}


/**************************************************************************************************
    Expressions
**************************************************************************************************/
Expr(EvaluationContext ctx, out SymbolData symdata)
{
    Deref Cast DotChain
    {
        DotChain(ctx, symdata);
        Cast(ctx, symdata);
        Deref(ctx, symdata);
    }
}

Deref(EvaluationContext ctx, ref SymbolData symdata)
{
    "*" Deref
    {
        if ( symdata is null )
            throw new EvaluationException("Invalid data for dereferencing");
        if ( symdata.type[0] != 'P' )
            throw new EvaluationException("Cannot dereference non-pointer of type "~demangleType(symdata.type));
        symdata.type = symdata.type[1..$];
        assert(symdata.type.length>0);
        ubyte[] data = symdata.getData(ctx);
        if ( data.length != size_t.sizeof ) {
            throw new EvaluationException("Invalid pointer data size = "~.toString(data.length));
        }
        symdata.ptr = (cast(size_t[])data)[0];
        symdata.len = ctx.codeView.sizeofMangled(symdata.type);
        symdata.defered_load = true;

        Deref(ctx, symdata);
    }

    epsilon;
}

DotChain(EvaluationContext ctx, out SymbolData symdata)
{
    Register
    {
        Register(ctx, symdata);
    }

    "("     !("(%d) Expression expected after (")
    Expr    !("(%d) ) expected")
    ")"
    RefExpr
    {
        Expr(ctx, symdata);
        RefExpr(ctx, null, null, "", symdata);
    }

    Ident
    RefExpr
    {
        string id;
        Ident(id);

        NamedSymbol[] symbols;
        NamedSymbol symbol = ctx.findSymbol(id, symbols);
        if ( symbol !is null )
            symdata = ctx.loadSymbolData(symbol);
        else
            debug DbgIO.println("symbol %s not found", id);
        RefExpr(ctx, symbol, symbols, id, symdata);
    }

    Lit
    { Lit(symdata); }
}

RefExpr(EvaluationContext ctx, NamedSymbol symbol, NamedSymbol[] symbols, string prefix, ref SymbolData symdata)
{
    //-------------------------------------------------------------------------
    // . Operator
    //-------------------------------------------------------------------------
    "." Ident RefExpr
    {
        string id;
        Ident(id);
        prefix ~= "."~id;

        if ( symdata is null )
        {
            symbol = ctx.findSymbol(prefix, symbols);
            if ( symbol !is null )
                symdata = ctx.loadSymbolData(symbol);
        }
        else
        {
			switch ( symdata.type[0] )
			{
				//---------------------------------------------------------------------------------
				// primitive types
				case 'v': case 'b': case 'x': case 'g': case 'h': case 's':	case 't': case 'i': case 'k': case 'l': case 'm': case 'f':
				case 'd': case 'e': case 'o': case 'p': case 'j': case 'q': case 'r': case 'c': case 'a': case 'u': case 'w':
					throw new EvaluationException(
						"Type mismatch - unexpected . operator in expression of type "~demangleType(symdata.type)
					);
				//---------------------------------------------------------------------------------
                // array properties
				case 'G':
                    switch ( id )
                    {
                        case "length":
                            symdata.type = symdata.type[1..$];
                            size_t len = parseNumber(symdata.type);
                            symdata.type = "k";
                            symdata.data = (cast(ubyte*)&len)[0..size_t.sizeof].dup;
                            symdata.defered_load = false;
                            break;
                        case "ptr":
                            // TODO: can this always be guaranteed?
                            assert(symdata.defered_load);
                            size_t ptr = symdata.ptr;
                            symdata.data = (cast(ubyte*)&ptr)[0..size_t.sizeof];
                            symdata.defered_load = false;
                            symdata.type = symdata.type[1..$];
                            parseNumber(symdata.type);
                            symdata.type = "P"~symdata.type;
                            break;
                        default:
                            throw new EvaluationException(
                                "Type mismatch - invalid property "~id~" after expression of type "~demangleType(symdata.type)
                            );
                    }
                    break;
                case 'A':
                    switch ( id )
                    {
                        case "length":
                            ubyte[] data = symdata.getData(ctx);
                            symdata.type = "k";
                            size_t len = (cast(size_t[])data)[0];
                            symdata.data = (cast(ubyte*)&len)[0..size_t.sizeof].dup;
                            symdata.defered_load = false;
                            break;
                        case "ptr":
                            ubyte[] data = symdata.getData(ctx);
                            symdata.type = "k";
                            size_t len = (cast(size_t[])data)[1];
                            symdata.data = (cast(ubyte*)&len)[0..size_t.sizeof].dup;
                            symdata.defered_load = false;
                            break;
                        default:
                            throw new EvaluationException(
                                "Type mismatch - invalid property "~id~" after expression of type "~demangleType(symdata.type)
                            );
                    }
                    break;
                case 'H':
                    switch ( id )
                    {
                        case "length":
                            ubyte[] data = symdata.getData(ctx);
                            symdata.ptr = *cast(size_t*)data.ptr;
                            symdata.len = BB.sizeof;
                            symdata.defered_load = true;
                            data = symdata.getData(ctx);
                            BB* bb = cast(BB*)data.ptr;

                            symdata.type = "k";
                            symdata.data = (cast(ubyte*)&bb.nodes)[0..size_t.sizeof].dup;
                            symdata.defered_load = false;
                            break;
                        default:
                            throw new EvaluationException(
                                "Type mismatch - invalid property "~id~" after expression of type "~demangleType(symdata.type)
                            );
                    }
                    break;
				//---------------------------------------------------------------------------------
				// references to structs/classes
				case 'P':
                    symdata.type = symdata.type[1..$];
					if ( symdata.type[0] != 'C' && symdata.type[0] != 'S' )
					{
                        throw new EvaluationException(
                            "Type mismatch - unexpected . operator after expression of type "~demangleType("P"~symdata.type)
                        );
					}

                    ubyte[] data = symdata.getData(ctx);
                    if ( symdata.data.length <= 0 )
                        throw new EvaluationException("Subexpression evaluated to empty data");
					symdata.ptr = (cast(uint[])data)[0];
					symdata.len = ctx.codeView.sizeofMangled(symdata.type);
					symdata.defered_load = true;

					if ( symdata.ptr == 0 )
						throw new EvaluationException("pointer is null");
                    // fall through to struct handling

				//---------------------------------------------------------------------------------
				// classes/structs
				case 'C':
				case 'S':
                    symdata.type = symdata.type[1..$];
                    string	scu_name = demangleNameSkip(symdata.type);
                    assert ( symdata.type.length <= 0 );
                    if ( (scu_name in ctx.codeView.UDTsByName) is null )
                        throw new EvaluationException("unknown type \""~scu_name~"\"");

                    LeafClassStruc  lcs = cast(LeafClassStruc)ctx.codeView.UDTsByName[scu_name];
                    LeafUnion       lu;
                    LeafFieldList   lfl;
                    uint            type_length;

                    if ( lcs !is null && lcs.field-0x1000 < ctx.codeView.type_strings.length ) {
                        Leaf[] fields = ctx.codeView.type_strings[lcs.field-0x1000];
                        lfl = cast(LeafFieldList)fields[0];
                        type_length = lcs.length.getUint();
                    }
                    else
                    {
                        lu = cast(LeafUnion)ctx.codeView.UDTsByName[scu_name];
                        if ( lu !is null && lu.field-0x1000 < ctx.codeView.type_strings.length ) {
                            Leaf[] fields = ctx.codeView.type_strings[lu.field-0x1000];
                            lfl = cast(LeafFieldList)fields[0];
                            type_length = lu.length.getUint();
                        }
                    }
                    if ( lfl is null )
                        throw new EvaluationException("invalid struct/class debug symbols");

                    size_t  size,
                            offset;
                    if ( !ctx.findMember(id, lfl, size, offset, symdata.type) )
                        throw new EvaluationException("struct \""~scu_name~"\" has no element \""~id~"\"");
                    assert ( symdata.type.length > 0 );
                    debug(eval) DbgIO.println("member: type=%s size=%d offset=%d", symdata.type, size, offset);

                    if ( !symdata.loadByteSlice(offset, offset+size) )
                        throw new EvaluationException("invalid offset into struct for member \""~id~"\"");
					break;
				//---------------------------------------------------------------------------------
				// unsupported
				default:
					throw new EvaluationException("yet unsupported type "~demangleType(symdata.type)~" in expression");
			}
        }

        RefExpr(ctx, symbol, symbols, prefix, symdata);
    }

    //-------------------------------------------------------------------------
    // [] Operator
    //-------------------------------------------------------------------------
    "["     !("(%d) Arguments expected after [")
    Args    !("(%d) ] expected")
    "]"
    RefExpr
    {
        if ( symdata is null )
            throw new EvaluationException("Unknown symbol "~prefix);

        SymbolData  argdata;
        size_t      start, end;
        Args(ctx, argdata, start, end);

        bool isIndex = argdata is null;
        if ( !isIndex && castArrayIndex(argdata.getData(ctx), argdata.type, start) ) {
            isIndex = true;
            end = start+1;
        }

        switch ( symdata.type[0] )
        {
            //---------------------------------------------------------------------------------
            // primitive types
            case 'v': case 'b': case 'x': case 'g': case 'h': case 's':	case 't': case 'i': case 'k': case 'l': case 'm': case 'f':
            case 'd': case 'e': case 'o': case 'p': case 'j': case 'q': case 'r': case 'c': case 'a': case 'u': case 'w':
            // pointers, classes/structs
            case 'P': case 'C': case 'S':
                throw new EvaluationException(
                    "Type mismatch - unexpected [] operator after expression of type "~demangleType(symdata.type)
                );
            //---------------------------------------------------------------------------------
            // static arrays
            case 'G':
                if ( !isIndex )
                    throw new EvaluationException("Cannot access static array with index type "~demangleType(argdata.type));

                symdata.type = symdata.type[1..$];
                size_t count = parseNumber(symdata.type);
                assert(symdata.type.length>0);

                if ( end-start > 1 )
                {
                    if ( end > count )
                        throw new EvaluationException("Array index out of bounds: "~.toString(start)~".."~.toString(end)~" ($="~.toString(count)~")");
                    size_t size = ctx.codeView.sizeofMangled(symdata.type);
                    if ( symdata.defered_load ) {
                        symdata.ptr += start * size;
                        symdata.len = (end-start) * size;
                    }
                    else
                        symdata.data = symdata.data[start*size .. end*size];
                    symdata.type = "G"~.toString(end-start)~symdata.type;
                }
                else
                {
                    if ( !symdata.loadElementSlice(start, end, ctx.codeView) )
                        throw new EvaluationException("Array index out of bounds: "~.toString(start)~".."~.toString(end)~" ($="~.toString(count)~")");
                }
                break;
            //---------------------------------------------------------------------------------
            // dynamic arrays
            case 'A':
                if ( !isIndex )
                    throw new EvaluationException("Cannot access dynamic array with index type "~demangleType(argdata.type));

                ubyte[] data = symdata.getData(ctx);
                if ( data.length < 8 )
                    throw new EvaluationException("Subexpression evaluated to empty data");

                if ( end-start > 1 )
                {
                    if ( end > (cast(size_t[])data)[0] )
                        throw new EvaluationException("Array index out of bounds: "~.toString(start)~".."~.toString(end)~" ($="~.toString(symdata.len)~")");
                    size_t size = ctx.codeView.sizeofMangled(symdata.type[1..$]);
                    (cast(size_t[])data)[1] += start * size,
                    (cast(size_t[])data)[0] = end-start;
                }
                else
                {
                    symdata.type = symdata.type[1..$];
                    assert(symdata.type.length>0);
                    symdata.ptr = (cast(size_t[])data)[1];
                    symdata.len = (cast(size_t[])data)[0] * ctx.codeView.sizeofMangled(symdata.type);
                    symdata.defered_load = true;

                    if ( !symdata.loadElementSlice(start, end, ctx.codeView) )
                        throw new EvaluationException("Array index out of bounds: "~.toString(start)~".."~.toString(end)~" ($="~.toString(symdata.len)~")");
                }
                break;
            //---------------------------------------------------------------------------------
            // associative arrays
            case 'H':
                int i;
                for ( i = 1; i < symdata.type.length && std.string.find(lowercase, symdata.type[i]) < 0; ++i ) {}
                string		key_type	= symdata.type[1..i+1],
                            val_type	= symdata.type[i+1..$];
                size_t      val_tsize   = ctx.codeView.sizeofMangled(val_type);
                TypeInfo	key_ti		= TypeInfoFromMangled(key_type);

                aaA* loadaaA(void* ptr)
                {
                    if ( ptr is null )
                        return null;
                    ubyte[] data;
                    data.length = aaA.sizeof+key_ti.tsize+val_tsize;
                    if ( data.length != ctx.process.readProcessMemory(cast(uint)ptr, data.ptr, data.length) )
                        return null;
                    aaA* a = cast(aaA*)data.ptr;
                    if ( std.string.find(lowercase, key_type[0]) < 0 )
                    switch ( key_type[0] )
                    {
                        case 'A':
                            ubyte[] tmp = *cast(ubyte[]*)(a+1);
                            if ( tmp.length > ctx.process.MEMCHECK_MIN && ctx.process.isInvalidMem(cast(size_t)tmp.ptr, tmp.length) )
                                return null;
                            data = new ubyte[tmp.length];
                            if ( data.length != ctx.process.readProcessMemory(cast(size_t)tmp.ptr, data.ptr, data.length) )
                                return null;
                            *cast(ubyte[]*)(a+1) = data;
                            break;
                        case 'P':
                            break;
                        default:
                            throw new EvaluationException("only basic, pointer and array key types are supported, yet");
                    }
                    return a;
                }

                ubyte[] findKey(size_t ptr, hash_t hash, ubyte[] key_data)
                {
                    ubyte[] data;
                    data.length = BB.sizeof;
                    if ( data.length != ctx.process.readProcessMemory(ptr, data.ptr, data.length) )
                        return null;
                    BB* bb = cast(BB*)data.ptr;
                    size_t blen = bb.b.length*size_t.sizeof;
                    debug(eval) DbgIO.println("%d nodes, bb.b.length=%d", bb.nodes, bb.b.length);

                    if ( blen > ctx.process.MEMCHECK_MIN && ctx.process.isInvalidMem(cast(size_t)bb.b.ptr, blen) )
                        return null;
                    data = new ubyte[blen];
                    if ( data.length != ctx.process.readProcessMemory(cast(uint)bb.b.ptr, data.ptr, data.length) )
                        return null;
                    bb.b = cast(aaA*[])data;

                    uint i = hash % bb.b.length;
                    debug(eval) DbgIO.println("start index=%d", i);
                    aaA* a = loadaaA(bb.b[i]);
                    while ( a !is null )
                    {
                        if ( hash == a.hash )
                        {
                            debug(eval) DbgIO.println("equal 0x%x", a.hash);
                            auto cmp = key_ti.compare(key_data.ptr, a+1);
                            debug(eval) DbgIO.println("%s: %d == %d -> %d", key_ti, *cast(uint*)key_data.ptr, *cast(uint*)(a+1), cmp);
                            if ( cmp == 0 )
                                return ((cast(ubyte*)(a+1))+key_ti.tsize)[0 .. val_tsize];
                            a = cmp < 0 ? loadaaA(a.left) : loadaaA(a.right);
                        }
                        else {
                            debug(eval) DbgIO.println("not equal 0x%x", a.hash);
                            a = hash < a.hash ? loadaaA(a.left) : loadaaA(a.right);
                        }
                    }
                    return null;
                }

                if ( key_type != argdata.type )
                    throw new EvaluationException("Invalid key type for associative array. Expected "~demangleType(key_type)~" found "~demangleType(argdata.type));
                TypeInfo ti = TypeInfoFromMangled(argdata.type);
                hash_t index_hash = ti.getHash(argdata.data.ptr);

                debug(eval) DbgIO.println("searching, hash=0x%x", index_hash);
                ubyte[] data = symdata.getData(ctx);
                symdata.data = findKey((cast(size_t[])data)[0], index_hash, argdata.getData(ctx));
                symdata.type = val_type;
                break;
            //---------------------------------------------------------------------------------
            // unsupported
            default:
                throw new EvaluationException("yet unsupported type "~demangleType(symdata.type)~" in expression");
        }

        RefExpr(ctx, symbol, null, null, symdata);
    }

    //-------------------------------------------------------------------------
    // end of refexpr
    //-------------------------------------------------------------------------
    epsilon
    {
        if ( symdata is null )
            throw new EvaluationException("Unknown symbol "~prefix);
    }
}

Args(EvaluationContext ctx, out SymbolData argdata, out size_t start, out size_t end)
{
    Expr
    ".."    !("(%d) Second slice argument expected")
    Expr=Expr2
    {
        SymbolData arg1, arg2;
        Expr(ctx, arg1);
        Expr2(ctx, arg2);

        bool isIndex = castArrayIndex(arg1.getData(ctx), arg1.type, start);
        isIndex = isIndex && castArrayIndex(arg2.getData(ctx), arg2.type, end);
        if ( !isIndex )
            throw new EvaluationException("Slices may only have integer indeces, not "~demangleType(arg1.type)~".."~demangleType(arg2.type));
    }

    Expr
    { Expr(ctx, argdata); }
}


/**************************************************************************************************
    Registers
**************************************************************************************************/
Register(EvaluationContext ctx, out SymbolData symdata)
{
    RegName
    {
        string  name;
        ubyte   width;
        RegName(name, width);

		CONTEXT threadCtx;
		uint context_flags;
        context_flags |= CONTEXT_FULL;
        context_flags |= CONTEXT_FLOATING_POINT;
        context_flags |= CONTEXT_EXTENDED_REGISTERS;
		if ( !ctx.getContext(threadCtx, context_flags) )
			throw new EvaluationException("Couldn't get main thread's context");

        ubyte* ptr;
        switch ( name[1..$] )
        {
            case "ax":
            case "eax":
            case "al":  ptr = cast(ubyte*)&threadCtx.Eax;   break;
            case "ah":  ptr = (cast(ubyte*)&threadCtx.Eax)+1;   break;
            case "bx":
            case "ebx":
            case "bl":  ptr = cast(ubyte*)&threadCtx.Ebx;   break;
            case "bh":  ptr = (cast(ubyte*)&threadCtx.Ebx)+1;   break;
            case "cx":
            case "ecx":
            case "cl":  ptr = cast(ubyte*)&threadCtx.Ecx;   break;
            case "ch":  ptr = (cast(ubyte*)&threadCtx.Ecx)+1;   break;
            case "dx":
            case "edx":
            case "dl":  ptr = cast(ubyte*)&threadCtx.Edx;   break;
            case "dh":  ptr = (cast(ubyte*)&threadCtx.Edx)+1;   break;
            case "edi":
            case "di":  ptr = cast(ubyte*)&threadCtx.Edi;   break;
            case "esi":
            case "si":  ptr = cast(ubyte*)&threadCtx.Esi;   break;
            case "ebp":
            case "bp":  ptr = cast(ubyte*)&threadCtx.Ebp;   break;
            case "esp":
            case "sp":  ptr = cast(ubyte*)&threadCtx.Esp;   break;
            case "eip": ptr = cast(ubyte*)&threadCtx.Eip;   break;
            case "efl": ptr = cast(ubyte*)&threadCtx.EFlags;    break;
            case "cs":  ptr = cast(ubyte*)&threadCtx.SegCs; break;
            case "ds":  ptr = cast(ubyte*)&threadCtx.SegDs; break;
            case "es":  ptr = cast(ubyte*)&threadCtx.SegEs; break;
            case "fs":  ptr = cast(ubyte*)&threadCtx.SegFs; break;
            case "gs":  ptr = cast(ubyte*)&threadCtx.SegGs; break;
            default:
                int i = name[$-1]-'0';
                if ( name[0..2] == "st" )
                    ptr = cast(ubyte*)&((cast(real[])threadCtx.FloatSave.RegisterArea)[i]);
                else if ( name[0..2] == "mm" )
                    ptr = cast(ubyte*)&((cast(long[])threadCtx.ExtendedRegisters)[4+i*2]);
                else if ( name[0..3] == "xmm" )
                    ptr = cast(ubyte*)&((cast(long[])threadCtx.ExtendedRegisters)[20+i*2]);
                else
                    assert(0);
        }

        symdata = new SymbolData;
        switch ( width )
        {
            case 8:
                symdata.type = "h";
                symdata.data = ptr[0..1].dup;
                break;
            case 16:
                symdata.type = "t";
                symdata.data = ptr[0..2].dup;
                break;
            case 32:
                symdata.type = "k";
                symdata.data = ptr[0..4].dup;
                break;
            case 64:
                symdata.type = "m";
                symdata.data = ptr[0..8].dup;
                break;
            case 80:
                symdata.type = "e";
                symdata.data = ptr[0..10].dup;
                break;
            case 128:
                symdata.type = "G4k";
                symdata.data = ptr[0..16].dup;
                break;
            default:
                assert(0, "unhandled register width "~.toString(width));
        }
    }
}

RegName(out string name, out ubyte width)
{
    regexp("#(e[abcd]x|e[ds]i|e[bs]p|eip|efl)")
    { name = _ST_match; width = 32; }

    regexp("#([abcd]x|[ds]i|[bs]p|(cdefgs)s)")
    { name = _ST_match; width = 16; }

    regexp("#[abcd][hl]")
    { name = _ST_match; width = 8; }

    regexp("#st[0-7]")
    { name = _ST_match; width = 8*real.sizeof; }

    regexp("#mm[0-7]")
    { name = _ST_match; width = 64; }

    regexp("#xmm[0-7]")
    { name = _ST_match; width = 128; }
}


/**************************************************************************************************
    Casts
**************************************************************************************************/
Cast(EvaluationContext ctx, ref SymbolData symdata)
{
    "cast"  !("(%d) ( expected")
    "("     !("(%d) Type expected")
    Type    !("(%d) ) expected")
    ")"
    {
        symdata.type = "";
        Type(ctx, symdata.type);
        if ( symdata.type.length <= 0 )
            throw new EvaluationException("Unknown type in cast");
    }

    epsilon;
}

Type(EvaluationContext ctx, ref string mangled)
{
    BaseType QuantList
    {
        string  unmangled;
        BaseType(ctx, unmangled);

		switch ( unmangled )
		{
			case "void":	mangled = "v";	break;
			case "bool":	mangled = "b";	break;
			case "byte":	mangled = "g";	break;
			case "ubyte":	mangled = "h";	break;
			case "char":	mangled = "a";	break;
			case "wchar":	mangled = "u";	break;
			case "dchar":	mangled = "w";	break;
			case "short":	mangled = "s";	break;
			case "ushort":	mangled = "t";	break;
			case "int":		mangled = "i";	break;
			case "uint":	mangled = "k";	break;
			case "long":	mangled = "l";	break;
			case "ulong":	mangled = "m";	break;
			case "float":	mangled = "f";	break;
			case "double":	mangled = "d";	break;
			case "real":	mangled = "e";	break;
			default:
				Leaf lf;
				if ( (unmangled in ctx.codeView.UDTsByName) is null )
				{
					foreach ( un, l; ctx.codeView.UDTsByName )
					{
						if ( std.string.find(un, "."~unmangled) > 0 ) {
							lf = l;
							break;
						}
					}
				}
				else
					lf = ctx.codeView.UDTsByName[unmangled];

				if ( lf !is null )
				{
					string fqn;
					switch ( lf.leaf_index )
					{
						case LF_CLASS_16t:
							mangled = "C";
							fqn = (cast(LeafClassStruc)lf).name;
							break;
						case LF_STRUCTURE_16t:
							mangled = "S";
							fqn = (cast(LeafClassStruc)lf).name;
							break;
						case LF_ENUM_16t:
							mangled = "E";
							fqn = (cast(LeafEnum)lf).name;
							break;
						default:
							assert(0);
					}
					string[] parts = std.string.split(fqn, ".");
					foreach ( p; parts ) {
						mangled ~= .toString(p.length);
						mangled ~= p;
					}
				}
                else
                    throw new EvaluationException("Unknown BaseType "~unmangled);
				break;
		}

		QuantList(ctx, mangled);
    }
}

BaseType(EvaluationContext ctx, out string type)
{
    FQNIdent TplParams
    {
        FQNIdent(type);
        string str;
        TplParams(ctx, str);
        type ~= str;
    }
}

QuantList(EvaluationContext ctx, ref string mangled)
{
    Quantifier QuantList
    {
        Quantifier(ctx, mangled);
        QuantList(ctx, mangled);
    }

    epsilon;
}

Quantifier(EvaluationContext ctx, ref string mangled)
{
    "*"
    { mangled = "P"~mangled; }

    "[" "]"
    { mangled = "A"~mangled; }

    "["
    Integer  !("(%d) ] expected")
    "]"
    {
        ulong len;
        Integer(len);
        mangled = "G"~.toString(len)~mangled;
    }

    "["     !("(%d) ], type or size expected")
    Type    !("(%d) ] expected")
    "]"
    {
        string index_mangled;
        Type(ctx, index_mangled);
        mangled = "H"~index_mangled~mangled;
    }
}

TplParams(EvaluationContext ctx, out string str)
{
    "!"         !("(%d) ( expected")
    "("         !("(%d) Template parameters expected")
    TplParam
    TplParams2  !("(%d) ) expected")
    ")"
    {
        string tmp;
        TplParam(ctx, tmp);
        str = "!("~tmp;
        TplParams2(ctx, tmp);
        str ~= tmp~")";
    }

    epsilon;
}

TplParams2(EvaluationContext ctx, out string str)
{
    "," !("(%d) Template parameter expected")
    TplParam
    TplParams2
    {
        TplParam(ctx, str);
        string tmp;
        TplParams2(ctx, tmp);
        str ~= tmp;
    }

    epsilon;
}

TplParam(EvaluationContext ctx, out string str)
{
    Type    { Type(ctx, str); }
    StrLit  { StrLit(str); }
}


/**************************************************************************************************
    Terminals
**************************************************************************************************/
Lit(out SymbolData litdata)
{
    Sign Integer IntegerSuffix
    {
        litdata = new SymbolData;
        IntegerSuffix(litdata.type);
        ulong val;
        Integer(val);

        if ( val > 0x7fffffffffffffff )
            litdata.type = "m";
        else if ( val > 0xffffffff )
            litdata.type = litdata.type[0]=='i'||litdata.type[0]=='l'?"l":"m";
        else if ( val > 0x7fffffff && litdata.type[0] == 'i' )
            litdata.type = "k";

        litdata.data = (cast(ubyte*)&val)[0 .. (litdata.type[0] == 'k' || litdata.type[0] == 'i')?uint.sizeof:ulong.sizeof].dup;
        Sign(litdata);
    }

    // String
    regexp("\"(([^\"\\\\]*(\\\\(['\"\\?\\\\abfnrtv]|(x[0-9a-fA-F]{2})|(u[0-9a-fA-F]{4})|(U[0-9a-fA-F]{8})))?)*)\"")
    {
        litdata = new SymbolData;
        litdata.type = "Aa";
        litdata.data = cast(ubyte[])_ST_match;
    }

    // Char
    regexp("'(([^'\\\\]|(\\\\(['\"\\?\\\\abfnrtv]|(x[0-9a-fA-F]{2})|(u[0-9a-fA-F]{4})|(U[0-9a-fA-F]{8})))))'")
    {
        litdata = new SymbolData;
        litdata.type = "a";
        litdata.data = cast(ubyte[])_ST_match[0..1];
    }
}

StrLit(out string str)
{
    // Integer
    Integer
    {
        ulong val;
        Integer(val);
        str = .toString(val);
    }

    // String
    regexp("\"(([^\"\\\\]*(\\\\(['\"\\?\\\\abfnrtv]|(x[0-9a-fA-F]{2})|(u[0-9a-fA-F]{4})|(U[0-9a-fA-F]{8})))?)*)\"")
    { str = _ST_match; }

    // Char
    regexp("'(([^'\\\\]|(\\\\(['\"\\?\\\\abfnrtv]|(x[0-9a-fA-F]{2})|(u[0-9a-fA-F]{4})|(U[0-9a-fA-F]{8})))))'")
    { str = _ST_match; }
}

Ident(out string id)
{
    regexp("[a-zA-Z_][_a-zA-Z0-9]*")
    { id = _ST_match; }
}

FQNIdent(out string id)
{
    Ident
    { Ident(id); }

    FQNIdent "." Ident
    {
        FQNIdent(id);
        string tmp;
        Ident(tmp);
        id ~= "."~tmp;
    }
}

/**************************************************************************************************
    Integers
**************************************************************************************************/
Integer(out ulong val)
{
    // hex
    regexp("0[xX][0-9a-fA-F_]+")
    {
        foreach ( b; _ST_match[2..$] )
        {
            val <<= 4;
            if ( b > 'F' )
                val |= b-'W';
            else if ( b > '9' )
                val |= b-'7';
            else
                val |= b-'0';
        }
    }

    // binary
    regexp("0[bB][01_]+")
    {
        foreach ( b; _ST_match[2..$] )
        {
            val <<= 1;
            if ( b == '1' )
                val |= 1;
        }
    }

    // octal
    regexp("0[0-7_]+")
    {
        foreach ( b; _ST_match[1..$] ) {
            val <<= 3;
            val |= b-'0';
        }
    }

    // decimal
    regexp("0|([1-9][0-9_]*)")
    {
        foreach ( b; _ST_match ) {
            val *= 10;
            val += b-'0';
        }
    }
}

IntegerSuffix(out string type)
{
    "L" "u"
    { type = "m"; }

    "L" "U"
    { type = "m"; }

    "u" "L"
    { type = "m"; }

    "U" "L"
    { type = "m"; }

    "L"
    { type = "l"; }

    regexp("[uU]")
    { type = "k"; }

    epsilon
    { type = "i"; }
}

Sign(ref SymbolData symdata)
{
    "-"
    {
        switch ( symdata.type[0] )
        {
            case 'i':
                *cast(int*)symdata.data.ptr = 0-*cast(int*)symdata.data.ptr;
                break;
            case 'k':
                *cast(uint*)symdata.data.ptr = 0-*cast(uint*)symdata.data.ptr;
                break;
            case 'l':
                *cast(long*)symdata.data.ptr = 0-*cast(long*)symdata.data.ptr;
                break;
            case 'm':
                *cast(ulong*)symdata.data.ptr = 0-*cast(ulong*)symdata.data.ptr;
                break;
            default:
                assert(0);
        }
    }

    epsilon;
}