共享一个从字符串转 Lambda 表达式的类(2)
2012-05-29 12:33
513 查看
共享一个从字符串转Lambda表达式的类(2)
Postedon2012-05-2912:33从这篇开始,我将按照依赖顺序,从核心到外围一步步说明这个东西。时间有点长,东西也不少。耐下心来看,或许能发现一些用得上的东西,当然不仅仅是在这里!
从字符串解析为一个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]
最后,给大家一个效果图:
不要说我邪恶啊,看十遍不如自己动手做一遍,呵呵。就到这里吧,期待下一篇的,帮忙点下推荐,我会很感激的,谢谢了!
相关文章推荐
- 共享一个从字符串转 Lambda 表达式的类(4)
- 共享一个从字符串转 Lambda 表达式的类(6)
- 共享一个从字符串转 Lambda 表达式的类(5)
- 共享一个从字符串转 Lambda 表达式的类(1)
- 共享一个从字符串转 Lambda 表达式的类(2)
- 共享一个从字符串转 Lambda 表达式的类(1)
- 共享一个从字符串转 Lambda 表达式的类(3)
- 共享一个从字符串转 Lambda 表达式的类(3) 转
- 一个将lambda字符串转化为lambda表达式的公共类
- 正则表达式:检查一个句子或者字符串是否以大写字母开头,以句号结尾.
- 软件开发与数学基础 一个C#中的例子——lambda表达式
- 计算一个字符串表示的四则运算表达式
- 请教一个正则表达式,匹配所有Html标签外部的指定字符串
- js正则表达式判断一个字符串是否是正确的有数字和小数点组成的金钱形式和 判读数值类型的正则表达式
- [转]C# 多个项目共享配置文件(共用一个SQL连接字符串)
- 正则表达式 在一个字符串中获取数字
- 利用正则表达式判断一个字符串是否为数字
- js正则表达式判断一个字符串是否是正确的有数字和小数点组成的金钱形式和 判读数值类型的正则表达式
- javascript 计算一个字符串表达式的值
- 怎么判断一个字符串是整型?不能用正则表达式,也不能用convert强制转换,更不能用Parsint