您的位置:首页 > 编程语言

自己开发计算器(3)-140行代码搞定Token解析

2011-10-04 07:41 369 查看
解析算式也好,脚本也罢,在分析语法之前,一般都会引入词法分析的过程。简单说比如我们有如下算式
acosd(2)+cosd(30)*12+1.23E-21
为了处理简单,在真正计算之前都会先将输入分解成一个个的单词(Token)。比如上面的内容经过处理,如果能变成下面的形式,就容易处理了。
acosd,(,2,),+,cosd,(,30,),*,12+1.23E-21
这里,类似于acosd,cosd和(,),*之类的要素可以通过简单的字符串比较来解决。但是数字还有最后e指数就没有那么简单了。当然如果你认为这正好是展现编程基本功的机会的话,没有人拦着你,但是在本软件中我们采用了正则表达式来解决这个问题。

例如e指数可以如下定义:
"((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))[eE][+-]?[0-9]+"
对于一般形式的小数:
"(\\.[0-9]+)|([0-9]+\\.[0-9]*)"
整数就更简单了。
"[0-9]+"
这样一来我们就可以象处理+/-号一样处理其他要素了。正则表达式的语法大家可以参照【精通正则表达式(第3版) 】一书。至于实际的内容建议大家看一下另一篇文章:

自己开发计算器(0)-扩展巴科斯范式(EBNF)

下面看看源代码。先从使用的地方看起。

QList<Token*> CalculateEngine::analyzeToken(QString strQuestion)
{
typedef TokenAnalyzer<Token, Token::EType, QList<Token*>::iterator> MyAnalyzer;

class MyFactory : public MyAnalyzer::TokenPatternFactory
{
virtual int createPatterns(QList<MyAnalyzer::TokenPattern*>& list) const
{
FunctionManager* manager = FunctionManager::getInstance();
QList<QString> functions = manager->functions();

QString funPattern;
foreach(QString funName, functions)
{
if(funPattern.length() > 0)
{
funPattern += "|";
}
funPattern += funName;
}
list.append(new MyAnalyzer::TokenPattern(Token::FunctionName, funPattern));

list.append(new MyAnalyzer::TokenPattern(Token::Number, "((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))[eE][+-]?[0-9]+"));
list.append(new MyAnalyzer::TokenPattern(Token::Number, "(\\.[0-9]+)|([0-9]+\\.[0-9]*)"));
list.append(new MyAnalyzer::TokenPattern(Token::Number, "[0-9]+"));
list.append(new MyAnalyzer::TokenPattern(Token::Operator, "[-+*/%]"));
list.append(new MyAnalyzer::TokenPattern(Token::Parenthese, "[()]"));
list.append(new MyAnalyzer::TokenPattern(Token::Comma, ","));

return list.count();
}
};

MyFactory factory;

MyAnalyzer analyzer;
QList<Token*> tokenList = analyzer.analyzeToken(strQuestion, &factory);
return tokenList;
}

在花费大量篇幅准备正则表达式以后调用TokenAnalyzer::analyzeToken就OK了。下面是TokenAnalyzer的源代码。考虑到Token的类型可能会因为需求而不同,这里采用了模板类。

#ifndef TOKENANALYZER_H
#define TOKENANALYZER_H
#include<QString>
#include<QList>
#include<QRegExp>
template<typename Token, typename TokenType, typename TokenIterator>
class TokenAnalyzer
{
public:
struct TokenPattern
{
TokenPattern(TokenType _type, QString _regex):regex(_regex),type(_type){}
QRegExp regex;
TokenType type;
};
class TokenPatternFactory
{
public:
virtual int createPatterns(QList<TokenPattern*>& list) const= 0;
};
TokenAnalyzer(){}
QList<Token*> analyzeToken(QString strInput, const TokenPatternFactory* factory);
private:
struct Context
{
Context(QList<Token*>& list, TokenIterator& _it, TokenPattern& _pattern, QString& _content)
:tokenList(list), it(_it), pattern(_pattern), content(_content){}
QList<Token*>& tokenList;
TokenIterator& it;
TokenPattern& pattern;
QString& content;
};
void analyzeContent(Context& context);
};
template<typename Token, typename TokenType, typename TokenIterator>
QList<Token*> TokenAnalyzer<Token, TokenType, TokenIterator>::analyzeToken(QString strInput, const TokenPatternFactory* factory)
{
QList<Token*> tokenList;
tokenList.append(new Token(strInput));
QList<TokenPattern*> list;
factory->createPatterns(list);
foreach(TokenPattern* pattern, list)
{
TokenIterator it = tokenList.begin();
while(it != tokenList.end())
{
Token* token = *it;
if(token->isNoType())
{
QString content = token->getContent();
Context context(tokenList, it, *pattern, content);
analyzeContent(context);
}
it++;
}
}
return tokenList;
}
template<typename Token, typename TokenType, typename TokenIterator>
void TokenAnalyzer<Token, TokenType, TokenIterator>::analyzeContent(Context& context)
{
Token* token = *context.it;
int tokenBegin = context.content.indexOf(context.pattern.regex);
if(tokenBegin != -1)
{
int matchedLength = context.pattern.regex.matchedLength();
int tokenEnd = tokenBegin + matchedLength;
if(tokenBegin > 0)
{
context.it = context.tokenList.insert(context.it, new Token(context.content.left(tokenBegin)));
context.it++;
}
if(tokenEnd < context.content.length())
{
context.it = context.tokenList.insert(context.it,
new Token(context.pattern.type, context.content.mid(tokenBegin, matchedLength)));
context.it++;
context.content.remove(0, tokenEnd);
analyzeContent(context);
}
else
{
token->setContent(context.content.mid(tokenBegin, tokenEnd));
token->setType(context.pattern.type);
context.content.remove(0, tokenEnd);
}
}
else
{
token->setContent(context.content);
}
}
#endif // TOKENANALYZER_H

算上定义正则表达式的部分,正好120行。

当然也少不了,真正的主角Token类.

class Token
{
public:
enum EType
{
NoType,
Operator,
Number,
FunctionName
};

Token(EType type, QString content);
Token(QString content);
EType getType();
QString getContent();
void setType(EType type);
bool isNoType();
void setContent(QString content);

private:
EType mType;
QString mContent;
};

其他关联文章请参考。

自己开发计算器(0)-扩展巴科斯范式(EBNF)

自己开发计算器(1)-准备开发环境

自己开发计算器(2)-全新的操作方式

自己开发计算器(4)-完成!源代码公开!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐