您的位置:首页 > 其它

共享一个从字符串转 Lambda 表达式的类(2)

2012-05-29 12:33 513 查看

共享一个从字符串转Lambda表达式的类(2)

Postedon2012-05-2912:33Lenic阅读(1469)评论(12)编辑收藏


从这篇开始,我将按照依赖顺序,从核心到外围一步步说明这个东西。时间有点长,东西也不少。耐下心来看,或许能发现一些用得上的东西,当然不仅仅是在这里!

从字符串解析为一个Lambda表达式树,第一步就是从中分析出有用的东西来。字符串【()=>newint[6]】经过解析之后,我们会获得如下信息:

左括号、索引值:0、文本表示:(

右括号、索引值:1、文本表示:)

Lambda表达式前缀、索引值:3、文本表示:=>

标识符、索引值:6、文本表示:new

标识符、索引值:10、文本表示:int

左中括号、索引值:13、文本表示:[

整形数字、索引值:14、文本表示:6

右中括号、索引值:15、文本表示:]

好了,字符串解析完毕后,可以保存在一个列表中,方便后面的读取操作。这些信息,每一项都可以作为一个对象,整体抽象出一个类。封装一下,不用太多信息,包含上面列出来的三个属性就可以了:单元类型、索引值和文本表示,经过整理后,代码如下:

///<summary>

[code]///字符单元
///</summary>
[DebuggerStepThrough]

[DebuggerDisplay("Text={Text},ID={ID},Index={Index}")]

publicstructToken
{

#regionFields

///<summary>

///空的字符单元

///</summary>

publicstaticreadonlyTokenEmpty=newToken();

privateTokenIdid;

privatestringtext;

privateintindex;

privateint?hash;

#endregion


#regionProperties

///<summary>

///获取或设置字符类型

///</summary>

publicTokenIdID
{

get{returnid;}

set
{

id=value;

hash=null;
}
}

///<summary>

///获取或设置当前字符单元的文本表示

///</summary>

publicstringText
{

get{returntext;}

set
{

text=value;

hash=null;
}
}

///<summary>

///获取或设置当前字符单元在整体结果中的索引

///</summary>

publicintIndex
{

get{returnindex;}

set
{

index=value;

hash=null;
}
}

#endregion


#regionOverrideMethods

///<summary>

///Determineswhetherthespecified<seecref="System.Object"/>isequaltothisinstance.

///</summary>

///<paramname="obj">The<seecref="System.Object"/>tocomparewiththisinstance.</param>

///<returns>

///<c>true</c>ifthespecified<seecref="System.Object"/>isequaltothisinstance;otherwise,<c>false</c>.

///</returns>

publicoverrideboolEquals(objectobj)
{

if(ReferenceEquals(obj,null))returnfalse;

if(objisToken)

returnEquals((Token)obj);

else

returnfalse;
}


///<summary>

///Equalsesthespecifiedtoken.

///</summary>

///<paramname="token">Thetoken.</param>

///<returns></returns>

publicboolEquals(Tokentoken)
{

if(ReferenceEquals(token,null))returnfalse;

returnID==token.id&&Text==token.Text;
}


///<summary>

///Returnsahashcodeforthisinstance.

///</summary>

///<returns>

///Ahashcodeforthisinstance,suitableforuseinhashingalgorithmsanddatastructureslikeahashtable.

///</returns>

publicoverrideintGetHashCode()
{

unchecked
{

if(!hash.HasValue)
{

hash=ID.GetHashCode();

hash^=Text.GetHashCode();

hash^=Index.GetHashCode();
}

returnhash.Value;
}
}


///<summary>

///Returnsa<seecref="System.String"/>thatrepresentsthisinstance.

///</summary>

///<returns>

///A<seecref="System.String"/>thatrepresentsthisinstance.

///</returns>

publicoverridestringToString()
{

returntext;
}


///<summary>

///Performsanimplicitconversionfrom<seecref="Lenic.DI.Core.Token"/>to<seecref="System.String"/>.

///</summary>

///<paramname="value">Thevalue.</param>

///<returns>Theresultoftheconversion.</returns>

publicstaticimplicitoperatorstring(Tokenvalue)
{

returnvalue.text;
}

#endregion


#regionExceptionThrow

///<summary>

///如果当前实例的文本表示与指定的字符串不符,则抛出异常.

///</summary>

///<paramname="id">待判断的字符串.</param>

///<returns>当前实例对象.</returns>

publicTokenThrow(TokenIdid)
{

if(ID!=id)

thrownewParserSyntaxErrorException();


returnthis;
}


///<summary>

///如果当前实例的字符类型与指定的字符类型不符,则抛出异常.

///</summary>

///<paramname="id">待判断的目标类型的字符类型.</param>

///<returns>当前实例对象.</returns>

publicTokenThrow(stringtext)
{

if(Text!=text)

thrownewParserSyntaxErrorException();


returnthis;
}

#endregion
}

[/code]

注意到上面出现了一个.Net类库中没有的TokenId,其实就是一个枚举,指示单元的类型是括号、Lambda表达式前缀,还是整形数字。我贴出代码,很好理解:

///<summary>

[code]///字符单元类型
///</summary>

publicenumTokenId
{

///<summary>

///End

///</summary>

End,

///<summary>

///Identifier

///</summary>

Identifier,

///<summary>

///String

///</summary>

StringLiteral,

///<summary>

///IntegerLiteral

///</summary>

IntegerLiteral,

///<summary>

///LongIntegerLiteral

///</summary>

LongIntegerLiteral,

///<summary>

///SingleRealLiteral

///</summary>

SingleRealLiteral,

///<summary>

///DecimalRealLiteral

///</summary>

DecimalRealLiteral,

///<summary>

///RealLiteral

///</summary>

RealLiteral,

///<summary>

///!

///</summary>

Exclamation,

///<summary>

///%

///</summary>

Percent,

///<summary>

///&

///</summary>

Amphersand,

///<summary>

///(

///</summary>

OpenParen,

///<summary>

///)

///</summary>

CloseParen,

///<summary>

///*

///</summary>

Asterisk,

///<summary>

///+

///</summary>

Plus,

///<summary>

///,

///</summary>

Comma,

///<summary>

///-

///</summary>

Minus,

///<summary>

///.

///</summary>

Dot,

///<summary>

////

///</summary>

Slash,

///<summary>

///:

///</summary>

Colon,

///<summary>

///<

///</summary>

LessThan,

///<summary>

///=

///</summary>

Equal,

///<summary>

///>

///</summary>

GreaterThan,

///<summary>

///?

///</summary>

Question,

///<summary>

///??

///</summary>

DoubleQuestion,

///<summary>

///[

///</summary>

OpenBracket,

///<summary>

///]

///</summary>

CloseBracket,

///<summary>

///|

///</summary>

Bar,

///<summary>

///!=

///</summary>

ExclamationEqual,

///<summary>

///&&

///</summary>

DoubleAmphersand,

///<summary>

///<=

///</summary>

LessThanEqual,

///<summary>

///<>

///</summary>

LessGreater,

///<summary>

///==

///</summary>

DoubleEqual,

///<summary>

///>=

///</summary>

GreaterThanEqual,

///<summary>

///||

///</summary>

DoubleBar,

///<summary>

///=>

///</summary>

LambdaPrefix,

///<summary>

///{

///</summary>

OpenBrace,

///<summary>

///}

///</summary>

CloseBrace,
}

[/code]

接下来,这些解析出来的字符单元,放到List<>中是个不错的主意,不过我想增强一下会更好,比如添加下面的几个方法:

读取并返回下一个(Next)

尝试读取下一个(PeekNext)

判断下一项是什么(NextIs)

跳过下面的几项(Skip)

重置读取位置(ReturnToIndex)

……

这些方法,会为后续操作带来极大的便利。幸运的是,代码已经在下面了:

///<summary>



///SymbolParseResult



///</summary>


[Serializable]


[DebuggerStepThrough]


[DebuggerDisplay("{ToString()}")]



publicclassSymbolParseResult:ReadOnlyCollection<Token>


{



#regionPrivateFields



[DebuggerBrowsable(DebuggerBrowsableState.Never)]



privateint_maxIndex=0;



[DebuggerBrowsable(DebuggerBrowsableState.Never)]



privateint_lastIndex=0;



[DebuggerBrowsable(DebuggerBrowsableState.Never)]



privateint_index=-1;



#endregion






#regionConstuction



///<summary>



///Initializesanewinstanceofthe<seecref="SymbolParseResult"/>class.



///</summary>



internalSymbolParseResult()



:base(newList<Token>())


{





}






///<summary>



///Initializesanewinstanceofthe<seecref="SymbolParseResult"/>class.



///</summary>



///<paramname="list">Thelist.</param>



internalSymbolParseResult(IList<Token>list)



:base(list)


{



_maxIndex=list.Count-1;


}



#endregion






#regionBusinessProperties



///<summary>



///获取或设置当前读取索引



///</summary>



publicintIndex


{



get{return_index;}



privateset


{



_lastIndex=_index;



_index=value;


}


}



///<summary>



///获取当前读取中的字符单元



///</summary>



publicTokenCurrent


{



get


{



if(Index<0||Index>_maxIndex)



returnToken.Empty;






returnthis[Index];


}


}



///<summary>



///获取完整的字符串表达式



///</summary>



privatestringStringExpression


{



get{returnstring.Join("",this);}


}



#endregion






#regionBusinessMethods



///<summary>



///读取下一个字符单元,同时读取索引前进.



///</summary>



///<returns>读取得到的字符单元</returns>



publicTokenNext()


{



Tokentoken;



if(TryGetElement(outtoken,Index+1))



returntoken;



else



returnToken.Empty;


}






///<summary>



///判断下一个字符单元是否是指定的类型,同时读取索引前进.



///</summary>



///<paramname="tokenId">期待得到的字符单元类型.</param>



///<paramname="throwIfNot">如果设置为<c>true</c>表示抛出异常.默认为<c>false</c>表示不抛出异常.</param>



///<returns><c>true</c>表示读取的单元类型和期待的单元类型一致;否则返回<c>false</c>.</returns>



publicboolNextIs(TokenIdtokenId,boolthrowIfNot=false)


{



varresult=Next().ID==tokenId;



if(!result&&throwIfNot)



thrownewApplicationException(string.Format("nextisnot{0}",tokenId));



returnresult;


}






///<summary>



///尝试读取下一个字符单元,但并不前进.



///</summary>



///<paramname="count">尝试读取的当前字符单元的后面第几个单元,默认为后面第一个单元.</param>



///<returns>读取得到的字符单元.</returns>



publicTokenPeekNext(intcount=1)


{



Tokentoken;



if(PeekGetElement(outtoken,Index+count))



returntoken;



else



returnToken.Empty;


}






///<summary>



///判断下一个字符单元是否是指定的类型,但读取索引不前进.



///</summary>



///<paramname="tokenId">期待得到的字符单元类型.</param>



///<paramname="count">判断当前字符后面第几个是指定的字符单元类型,默认值为1.</param>



///<paramname="throwIfNot">如果设置为<c>true</c>表示抛出异常.默认为<c>false</c>表示不抛出异常.</param>



///<returns>



///<c>true</c>表示读取的单元类型和期待的单元类型一致;否则返回<c>false</c>.



///</returns>



publicboolPeekNextIs(TokenIdtokenId,intcount=1,boolthrowIfNot=false)


{



varresult=PeekNext(count).ID==tokenId;



if(!result&&throwIfNot)



thrownewApplicationException(string.Format("nextisnot{0}",tokenId));



returnresult;


}






///<summary>



///前进跳过指定的字符单元.



///</summary>



///<paramname="count">Thecount.</param>



publicvoidSkip(intcount=1)


{



count=Index+count;



CheckIndexOut(count);






Index=count;


}






///<summary>



///读取直到符合predicate的条件时停止.



///</summary>



///<paramname="predicate">比较当前Token是否符合条件的方法.</param>



///<returns>读取停止时的Token列表.</returns>



publicIList<Token>SkipUntil(Func<Token,bool>predicate)


{



List<Token>data=newList<Token>();



while(!predicate(Current)||Current.ID==TokenId.End)



data.Add(Next());



returndata;


}






///<summary>



///返回到指定的读取索引.



///</summary>



///<paramname="index">目标读取索引.</param>



publicvoidReturnToIndex(intindex)


{



if(index<-1||index>_maxIndex)



thrownewIndexOutOfRangeException();






Index=index;


}



#endregion






#regionPrivateMethods



privateboolTryGetElement(outTokentoken,intindex)


{



boolresult=PeekGetElement(outtoken,index);



if(result)



Index=index;



returnresult;


}






privateboolPeekGetElement(outTokentoken,intindex)


{



if(index<0||index>_maxIndex)


{



token=Token.Empty;



returnfalse;


}



else


{



token=this[index];



returntrue;


}


}






privatevoidCheckIndexOut(intindex)


{



if(index<0||index>_maxIndex)



thrownewIndexOutOfRangeException();


}



#endregion






#regionOverrideMethods



///<summary>



///Returnsa<seecref="System.String"/>thatrepresentsthisinstance.



///</summary>



///<returns>



///A<seecref="System.String"/>thatrepresentsthisinstance.



///</returns>



publicoverridestringToString()


{



returnstring.Join("",this.TakeWhile(p=>p.Index<Current.Index));


}



#endregion


}


接下来,核心的字符串分析类!这个类是我从DynamicLINQ中拆出来的,每次读取一个字符,下一次和前一次的比对,整理出一个个的Token。作者的思路很严谨,把能想到的都放到里面去了,严格按照C#的语法读取,否则抛出异常。除此之外,还可以加入自定义的Token,只要在NextToken大方法中添加自己的逻辑就好,可以参照我添加的LambdaPrefix块:

///<summary>

[code]///SymbolParser
///</summary>
[DebuggerStepThrough]

[DebuggerDisplay("CurrentPosition={CurrentPosition},Source={Source}")]

publicsealedclassSymbolParser
{

#regionFieldsAndProperties

///<summary>

///Getsthesource.

///</summary>

publicstringSource{get;privateset;}

///<summary>

///Getsthecurrentposition.

///</summary>

publicintCurrentPosition{get;privateset;}

///<summary>

///Getsthelength.

///</summary>

publicintLength{get;privateset;}

///<summary>

///Getsthecurrentchar.

///</summary>

publiccharCurrentChar{get;privateset;}


privateTokencurrentToken;

///<summary>

///Getsthecurrenttoken.

///</summary>

publicTokenCurrentToken{get{returncurrentToken;}}

#endregion


#regionConstructor

///<summary>

///Initializesanewinstanceofthe<seecref="SymbolParser"/>class.

///</summary>

///<paramname="source">Thesource.</param>

publicSymbolParser(stringsource)
{

if(ReferenceEquals(null,source))

thrownewArgumentNullException("source");


Source=source;

Length=source.Length;

SetPosition(0);
}

#endregion


#regionBusinessMethods

///<summary>

///Setstheposition.

///</summary>

///<paramname="index">Theindex.</param>

publicvoidSetPosition(intindex)
{

CurrentPosition=index;

CurrentChar=CurrentPosition<Length?Source[CurrentPosition]:'\0';
}


///<summary>

///Nextsthechar.

///</summary>

publicvoidNextChar()
{

if(CurrentPosition<Length)CurrentPosition++;

CurrentChar=CurrentPosition<Length?Source[CurrentPosition]:'\0';
}


///<summary>

///Nextsthetoken.

///</summary>

///<returns></returns>

publicTokenNextToken()
{

while(Char.IsWhiteSpace(CurrentChar))NextChar();

TokenIdt;

inttokenPos=CurrentPosition;

switch(CurrentChar)
{

case'!':

NextChar();

if(CurrentChar=='=')
{

NextChar();

t=TokenId.ExclamationEqual;
}

else
{

t=TokenId.Exclamation;
}

break;

case'%':

NextChar();

t=TokenId.Percent;

break;

case'&':

NextChar();

if(CurrentChar=='&')
{

NextChar();

t=TokenId.DoubleAmphersand;
}

else
{

t=TokenId.Amphersand;
}

break;

case'(':

NextChar();

t=TokenId.OpenParen;

break;

case')':

NextChar();

t=TokenId.CloseParen;

break;

case'*':

NextChar();

t=TokenId.Asterisk;

break;

case'+':

NextChar();

t=TokenId.Plus;

break;

case',':

NextChar();

t=TokenId.Comma;

break;

case'-':

NextChar();

t=TokenId.Minus;

break;

case'.':

NextChar();

t=TokenId.Dot;

break;

case'/':

NextChar();

t=TokenId.Slash;

break;

case':':

NextChar();

t=TokenId.Colon;

break;

case'<':

NextChar();

if(CurrentChar=='=')
{

NextChar();

t=TokenId.LessThanEqual;
}

elseif(CurrentChar=='>')
{

NextChar();

t=TokenId.LessGreater;
}

else
{

t=TokenId.LessThan;
}

break;

case'=':

NextChar();

if(CurrentChar=='=')
{

NextChar();

t=TokenId.DoubleEqual;
}

elseif(CurrentChar=='>')
{

NextChar();

t=TokenId.LambdaPrefix;
}

else
{

t=TokenId.Equal;
}

break;

case'>':

NextChar();

if(CurrentChar=='=')
{

NextChar();

t=TokenId.GreaterThanEqual;
}

else
{

t=TokenId.GreaterThan;
}

break;

case'?':

NextChar();

if(CurrentChar=='?')
{

NextChar();

t=TokenId.DoubleQuestion;
}

else
{

t=TokenId.Question;
}

break;

case'[':

NextChar();

t=TokenId.OpenBracket;

break;

case']':

NextChar();

t=TokenId.CloseBracket;

break;

case'{':

NextChar();

t=TokenId.OpenBrace;

break;

case'}':

NextChar();

t=TokenId.CloseBrace;

break;

case'|':

NextChar();

if(CurrentChar=='|')
{

NextChar();

t=TokenId.DoubleBar;
}

else
{

t=TokenId.Bar;
}

break;

case'"':

case'\'':

charquote=CurrentChar;

do
{

NextChar();

while(CurrentPosition<Length&&CurrentChar!=quote)NextChar();

if(CurrentPosition==Length)

throwParseError(CurrentPosition,"Unterminatedstringliteral");

NextChar();
}while(CurrentChar==quote);

t=TokenId.StringLiteral;

break;

default:

if(Char.IsLetter(CurrentChar)||CurrentChar=='@'||CurrentChar=='_')
{

do
{

NextChar();
}while(Char.IsLetterOrDigit(CurrentChar)||CurrentChar=='_'||CurrentChar=='?');

t=TokenId.Identifier;

break;
}

if(Char.IsDigit(CurrentChar))
{

t=TokenId.IntegerLiteral;

do
{

NextChar();
}while(Char.IsDigit(CurrentChar));


if(CurrentChar=='l'||CurrentChar=='L')
{

t=TokenId.LongIntegerLiteral;

NextChar();

break;
}

elseif(CurrentChar=='f'||CurrentChar=='F')
{

t=TokenId.SingleRealLiteral;

NextChar();

break;
}

elseif(CurrentChar=='m'||CurrentChar=='M')
{

t=TokenId.DecimalRealLiteral;

NextChar();

break;
}

elseif(CurrentChar=='d'||CurrentChar=='D')
{

t=TokenId.RealLiteral;

NextChar();

break;
}


if(CurrentChar=='.')
{

t=TokenId.RealLiteral;

NextChar();

ValidateDigit();

do
{

NextChar();
}while(Char.IsDigit(CurrentChar));
}


if(CurrentChar=='E'||CurrentChar=='e')
{

t=TokenId.RealLiteral;

NextChar();

if(CurrentChar=='+'||CurrentChar=='-')NextChar();

ValidateDigit();

do
{

NextChar();
}while(Char.IsDigit(CurrentChar));
}


if(CurrentChar=='F'||CurrentChar=='f')
{

t=TokenId.SingleRealLiteral;

NextChar();

break;
}

elseif(CurrentChar=='m'||CurrentChar=='M')
{

t=TokenId.DecimalRealLiteral;

NextChar();

break;
}

elseif(CurrentChar=='d'||CurrentChar=='D')
{

t=TokenId.RealLiteral;

NextChar();

break;
}


break;
}

if(CurrentPosition==Length)
{

t=TokenId.End;

break;
}

throwParseError(CurrentPosition,"Syntaxerror'{0}'",CurrentChar);
}

currentToken.ID=t;

currentToken.Text=Source.Substring(tokenPos,CurrentPosition-tokenPos);

currentToken.Index=tokenPos;


returnnewToken{ID=t,Text=currentToken.Text,Index=tokenPos,};
}


///<summary>

///Buildsthespecifiedsource.

///</summary>

///<paramname="source">Thesource.</param>

///<returns>TheBuildresult.</returns>

publicstaticSymbolParseResultBuild(stringsource)
{

varitem=newSymbolParser(source);

List<Token>data=newList<Token>();

while(true)
{

vartoken=item.NextToken();

data.Add(token);

if(token.ID==TokenId.End)

break;
}

returnnewSymbolParseResult(data);
}

#endregion


#regionPrivateMethods

privatevoidValidateDigit()
{

if(!Char.IsDigit(CurrentChar))throwParseError(CurrentPosition,"Digitexpected");
}


privateExceptionParseError(stringformat,paramsobject[]args)
{

returnParseError(currentToken.Index,format,args);
}


privateExceptionParseError(intpos,stringformat,paramsobject[]args)
{

returnnewParseException(string.Format(CultureInfo.CurrentCulture,format,args),pos);
}

#endregion
}

[/code]

最后,还有两个异常类:

///<summary>

[code]///分析语法错误类
///</summary>
[DebuggerStepThrough]

publicclassParserSyntaxErrorException:Exception
{

///<summary>

///初始化新建一个<seecref="ParserSyntaxErrorException"/>类的实例对象.

///</summary>

publicParserSyntaxErrorException()

:base("syntaxerror!"){}
}

[/code]

///<summary>

[code]///ParseException
///</summary>
[DebuggerStepThrough]

publicsealedclassParseException:Exception
{

privateintposition;


///<summary>

///Initializesanewinstanceofthe<seecref="ParseException"/>class.

///</summary>

///<paramname="message">Themessage.</param>

///<paramname="position">Theposition.</param>

internalParseException(stringmessage,intposition)

:base(message)
{

this.position=position;
}


///<summary>

///Getstheposition.

///</summary>

publicintPosition
{

get{returnposition;}
}


///<summary>

///Returnsa<seecref="System.String"/>thatrepresentsthisinstance.

///</summary>

///<returns>

///A<seecref="System.String"/>thatrepresentsthisinstance.

///</returns>

publicoverridestringToString()
{

returnstring.Format("{0}(atindex{1})",Message,position);
}
}

[/code]

最后,给大家一个效果图:





不要说我邪恶啊,看十遍不如自己动手做一遍,呵呵。就到这里吧,期待下一篇的,帮忙点下推荐,我会很感激的,谢谢了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: