view ddata.d @ 2:ee1232043257

< <= :/
author Saaa
date Wed, 07 Oct 2009 13:56:33 +0200
parents 8e817772ed3d
children
line wrap: on
line source

module ddata.ddata;

//debug
//import std.stdio;
import std.string;

import std2.conv : to;
import std2.traits : isNumeric;

class DDataException : Exception
{
    this(char[] msg)
    {
    	super(msg);
    }
    this( uint loc, char c, char[] msg)
    {
    	super( format( `parsing failed at string[`, loc-1, `] = '`,c,`'` ,msg));
    }
}


private enum TOKEN{ BOF, SQUARE_L, SQUARE_R, COMMA, ECOMMA, VALUE, EOF}

private template Depth(T: T[]) { const Depth = 1 + Depth!(T); }
private template Depth(T)  { const Depth = 0; }

private template BaseType(T: T[]) { alias BaseType!(T) BaseType; }
private template BaseType(T) { alias T BaseType; }

private template indexAssign(T: T[])
{
	void indexAssign(ref T array,  BaseType!(T) value, int[] indices) 
	{
		static if( is( typeof(array[0]) == typeof(value)))
		{
			array[indices[0]] = value;
		}
		else
		{
			indexAssign!(T)( array[indices[0]], value, indices[1..$]);
		}
	}
}

private string ctToString(int i) 
{
  if (!i) return "0";
  string res;
  while (i) {
    res = "0123456789"[i%10] ~ res;
    i /= 10;
  }
  return res;
}

private string indexString(int len) 
{
  string res;
  for (int i = 0; i < len; ++i)
    res ~= "[index["~ctToString(i)~"]] ";
  return res;
}

private string casesString(int depth, TOKEN type)
{
  string res;
  res ~= `switch( depth ){`;

  for (int i = 0; i < depth; ++i)
  {
	  switch(type)
	  {
		  case TOKEN.COMMA:
			  res ~=`case `~ctToString(i)~`:`~
			      	`if(temp`~ indexString(i) ~`.length<=index[`~ctToString(i)~`] )temp`~ indexString(i) ~`.length=temp`~ indexString(i) ~`.length*2;`~
			      	`break;`;
		  break;
		  
		  case TOKEN.SQUARE_L:
			  res ~=`case `~ctToString(i)~`:`~
	    			`temp`~ indexString(i) ~`.length=4;`~
	    			`break;`;
		  break;
		  
		  case TOKEN.SQUARE_R:
			  res ~=`case `~ctToString(i)~`:`~
  					`temp`~ indexString(i) ~`.length=index[`~ctToString(i)~`];`~
  					`break;`;		  
		  break;
		  
		  default:
			  assert(false);
		  break;
	  }
  }
  
  res ~= `default:assert(false);break;}`;
  return res;
}


bool toBool(char[] s) 
{
	if(s == `true`) 
	{
		return true;
	}
	else if(s == `false`) 
	{
		return false;
	}
	else
	{
		throw new Exception(`Cannot convert "`~s~`" to bool`);
	}
	return false;
}

public T toArray(T:D[],D)(char[] string)
{	
	int[ Depth!(T) ] index = 0;
	
	T temp;
	char c;
	
	int depth = -1;

	int sLoc1 = -1, sLoc2 = -1;
	int loc = -1;
	
	TOKEN last = TOKEN.BOF;
	
	while(true)
	{
		loc++;
		//debug 
		//writefln(loc);
		if( loc >= string.length )
		{
			if( last != TOKEN.SQUARE_R) throw new DDataException( `unexpected end`);
			if( depth != -1 ) throw new DDataException( `array depth not zero after parsing `);
			//throw new DDataException(`EOF before end of parsing`);
			return temp;
		}
		
		c = string[loc];
		//debug 
		//writefln(c);
		switch(c)
		{
			case ' ', '\t', '\v', '\r', '\n', '\f':
				if( last == TOKEN.ECOMMA || last == TOKEN.SQUARE_L)
				{
					if(sLoc2 < 0)
					{
						sLoc1 = loc + 1;
					}else{
						sLoc2 = loc;
						last = TOKEN.VALUE;
					}
				}
			break;
			
			case ',':
				index[depth] ++;

				// resize array if necessary
				mixin ( casesString(Depth!(T), TOKEN.COMMA ) );
				
				if( last == TOKEN.VALUE && depth == Depth!(T)-1 )
				{
					index[depth] --;
					static if( is(BaseType!(T) == bool) )
					{
						indexAssign!(T[])( temp, toBool( string[ sLoc1..sLoc2] ), index);
					}
					else static if( isNumeric!( BaseType!(T) ) )
					{
						indexAssign!(T[])( temp, to!( BaseType!(T) )( string[ sLoc1..sLoc2] ), index);
					}
					else
					{
						assert(false);
					}
					index[depth] ++;
					sLoc1 = -1;
					sLoc2 = -1;
					last = TOKEN.ECOMMA;
				}else if( last == TOKEN.SQUARE_R ){
					last = TOKEN.COMMA;
				}else{
					throw new DDataException( loc, c, `: Value, ']' or ',' expected.`);
				}
			break;
			
			case '[':
				if( last != TOKEN.COMMA && last != TOKEN.BOF && last != TOKEN.SQUARE_L ) throw new DDataException( loc, c, `: Beginning, '[' or ','  expected.`);
				
				depth++;
				if(depth > index.length) throw new DDataException( loc, c, `: Array too deep = `~toString(depth));
				mixin ( casesString(Depth!(T), TOKEN.SQUARE_L ) );
				index[depth] = 0;
				last = TOKEN.SQUARE_L;
				sLoc1 = -1;
				sLoc2 = -1;
			break;
			
			case ']':
				if( last == TOKEN.VALUE && depth == Depth!(T)-1 && sLoc2 != -1)
				{
					static if( is(BaseType!(T) == bool) )
					{
						indexAssign!(T[])( temp, toBool( string[ sLoc1..sLoc2]), index);
					}
					else static if( isNumeric!( BaseType!(T) ) )
					{
						indexAssign!(T[])( temp, to!( BaseType!(T) )(string[ sLoc1..sLoc2]), index);
					}
					else
					{
						assert(false);
					}
					sLoc1 = -1;
					sLoc2 = -1;
					index[depth] ++;
				} 
				else if( last == TOKEN.SQUARE_L ) 
				{
					sLoc1 = -1;
					sLoc2 = -1;
				} 
				else if( last == TOKEN.SQUARE_R) 
				{
					index[depth] ++;
				} 
				else 
				{
					throw new DDataException( loc, c, `: Value, '[' or ']' expected.`);
				}
				
				// set array length to index[depth]
				mixin ( casesString(Depth!(T), TOKEN.SQUARE_R) );
				
				index[depth] = 0;
				depth--;
				last = TOKEN.SQUARE_R;
			break;
				
			default:
				if( last == TOKEN.ECOMMA || last == TOKEN.SQUARE_L || last == TOKEN.VALUE)
				{
					if( sLoc1 < 0 )
					{
						sLoc1 = loc;
						sLoc2 = loc +1;
					} else {
						sLoc2 = loc +1;
					}
					last = TOKEN.VALUE;
					break;
				}
				throw new DDataException( loc, c, `: Out of place char found `);
			break;
		}
	}
}

unittest 
{
    writefln("DData UnitTest...");
    
	int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
	
	char[] s = to!(char[])(ia);
	int[][][] ia2;
	
	ia2 = toArray!(typeof(ia2))(s);
    assert( ia == ia2);  
    
    writefln("Done!");
}