上一篇谈到了词法分析,并且给了一小段词法分析上的代码。如果大家对着代码看,可能就会发现我构造了一个对象来对字符串进行读取。
具体代码是:CharReader src = new CharReader(chars);
这样子写是为了减少在后面的while语句对字符串数组末尾的判断,起到简化代码的作用;这样的小技巧在写一些比较复杂的算法的时候特别的重要。
实现完词法分析之后我们就要进行到下一步的语法分析了。教科书的的语法分析一般都是教我们怎么去实现一个抽象语法树,而我个人比较喜欢用所谓的面向对象的方式去实现它,这样子的实现方式代码看起来比较直观,而且扩展也相对容易一些。
语法分析的前提是我们首先要构建出对应的对象结构。
ExpressionNode
private abstract class ExpressionNode
{
private Token _relatedToken;
public ExpressionNode(Token relatedToken)
{
_relatedToken = relatedToken;
}
public Token RelatedToken
{
get { return _relatedToken; }
}
public abstract object Eval(Type targetType);
}
ExpressionNode是我们的对象结构的最基本的对象,所有的其它的对象都来继承于它。
ObjectExpression
private class ObjectExpression : ExpressionNode
{
private IList<NameValue> _elements;
public ObjectExpression(Token relatedToken)
: base(relatedToken)
{
_elements = new List<NameValue>();
}
public IList<NameValue> Elements
{
get { return _elements; }
}
public void Add(string name, ExpressionNode value)
{
_elements.Add(new NameValue(name, value));
}
public override object Eval(Type targetType)
{
}
}
ObjectExpression 对象对应的是JSON里的Object对象。
ArrayExpression
private class ArrayExpression : ExpressionNode
{
private IList<ExpressionNode> _elements;
public ArrayExpression(Token relatedToken)
: base(relatedToken)
{
_elements = new List<ExpressionNode>();
}
public IList<ExpressionNode> Elements
{
get { return _elements; }
}
public void Add(ExpressionNode value)
{
_elements.Add(value);
}
public override object Eval(Type targetType)
{
}
}
ArrayExpression 对象对应的是JSON里的Array对象。
NullPrimitive
private class NullPrimitive : ExpressionNode
{
public NullPrimitive(Token relatedToken)
: base(relatedToken)
{
}
public override object Eval(Type targetType)
{
if (targetType == null)
{
return null;
}
else
{
return ConverFundamentalType(RelatedToken.Data, targetType);
}
}
}
NullPrimitive 对应的是null值。BooleanPrimitive、IntPrimitive、DoublePrimitive、StringPrimitive 所代表的东西一眼就能看出来,不再贴代码,可以自己对着代码去看。
构建完对象后要进行的工作那就是最终的分析了。JSON的value是最具有普遍性的对象之一,我们就先实现它,代码如下:
ParseValue
private ExpressionNode ParseValue(TokenReader src)
{
ExpressionNode node;
switch (src.CurTok.TokenId)
{
case TokenId.LBracket:
{
node = ParseArray(src);
if (src.CurTok.TokenId == TokenId.RBracket)
src.Advance();
break;
}
case TokenId.LCurly:
{
node = ParseObject(src);
src.AssertAndAdvance(TokenId.RCurly);
break;
}
case TokenId.Ident:
case TokenId.String:
{
node = new StringPrimitive(src.CurTok);
src.Advance();
break;
}
case TokenId.Double:
{
node = new DoublePrimitive(src.CurTok);
src.Advance();
break;
}
case TokenId.Int:
{
node = new IntPrimitive(src.CurTok);
src.Advance();
break;
}
case TokenId.True:
{
node = new BooleanPrimitive(true, src.CurTok);
src.Advance();
break;
}
case TokenId.False:
{
node = new BooleanPrimitive(false, src.CurTok);
src.Advance();
break;
}
case TokenId.Null:
{
node = new NullPrimitive(src.CurTok);
src.Advance();
break;
}
default:
{
throw new JSONException("Illegal JSON value.");
}
}
return node;
}
其次是对数组的分析:
ParseArray
private ArrayExpression ParseArray(TokenReader src)
{
if (src.CurTok.TokenId == TokenId.LBracket)
src.Advance();
ArrayExpression arr = new ArrayExpression(src.CurTok);
while (src.CurTok.TokenId != TokenId.RBracket && !src.Eof)
{
arr.Add(ParseValue(src));
if (src.CurTok.TokenId == TokenId.Comma)
src.Advance();
}
return arr;
}
再接着是对Object对象的分析:
ParseObject
private ObjectExpression ParseObject(TokenReader src)
{
src.AssertAndAdvance(TokenId.LCurly);
ObjectExpression o = new ObjectExpression(src.CurTok);
string name = string.Empty;
while (src.CurTok.TokenId != TokenId.RCurly && !src.Eof)
{
switch (src.CurTok.TokenId)
{
case TokenId.Ident:
case TokenId.String:
{
name = src.CurTok.Data;
src.Advance();
break;
}
case TokenId.Colon:
{
src.Advance();
o.Add(name, ParseValue(src));
if (src.CurTok.TokenId == TokenId.Comma)
src.Advance();
break;
}
default:
{
throw new JSONException("Illegal JSON object.");
}
}
}
return o;
}
这样子整个语法分析的过程就已经完成,大家可能认为是因为json比较简单才可以用这样的方式来实现,其实不是,目前比较流行的编译器我们也可以用这样的方式来实现了,只是其结构会相对来说比较复杂一些而已。
下一篇:
谈谈编译原理和其在WEB开发中的应用3