自己开发计算器(3)-140行代码搞定Token解析
2011-10-04 07:41
369 查看
解析算式也好,脚本也罢,在分析语法之前,一般都会引入词法分析的过程。简单说比如我们有如下算式
为了处理简单,在真正计算之前都会先将输入分解成一个个的单词(Token)。比如上面的内容经过处理,如果能变成下面的形式,就容易处理了。
这里,类似于acosd,cosd和(,),*之类的要素可以通过简单的字符串比较来解决。但是数字还有最后e指数就没有那么简单了。当然如果你认为这正好是展现编程基本功的机会的话,没有人拦着你,但是在本软件中我们采用了正则表达式来解决这个问题。
例如e指数可以如下定义:
对于一般形式的小数:
整数就更简单了。
这样一来我们就可以象处理+/-号一样处理其他要素了。正则表达式的语法大家可以参照【精通正则表达式(第3版) 】一书。至于实际的内容建议大家看一下另一篇文章:
自己开发计算器(0)-扩展巴科斯范式(EBNF)
下面看看源代码。先从使用的地方看起。
在花费大量篇幅准备正则表达式以后调用TokenAnalyzer::analyzeToken就OK了。下面是TokenAnalyzer的源代码。考虑到Token的类型可能会因为需求而不同,这里采用了模板类。
算上定义正则表达式的部分,正好120行。
当然也少不了,真正的主角Token类.
其他关联文章请参考。
自己开发计算器(0)-扩展巴科斯范式(EBNF)
自己开发计算器(1)-准备开发环境
自己开发计算器(2)-全新的操作方式
自己开发计算器(4)-完成!源代码公开!
acosd(2)+cosd(30)*12+1.23E-21 |
acosd,(,2,),+,cosd,(,30,),*,12+1.23E-21 |
例如e指数可以如下定义:
"((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))[eE][+-]?[0-9]+" |
"(\\.[0-9]+)|([0-9]+\\.[0-9]*)" |
"[0-9]+" |
自己开发计算器(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)-完成!源代码公开!
相关文章推荐
- MacOS 开发 - 计算代码行数
- Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]
- Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载][续]
- Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]
- SkylineGlobe Android 开发 面积计算示例代码
- 【原创】程序员从SEO角度来开发网站之自己手写代码来实现URL重写
- STM32代码自动生成工具,三步搞定一个智能硬件APP开发
- 开发自己的UBB”代码“
- 使用百度地图SDK来开发自己的程序(主要是考察自己读代码的能力)
- Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]
- 微信/易信公共平台开发(一):开发了一个简单易用的PHP类(提供源码),十几行代码搞定一个公众号
- 自己写的计算时间坐标的代码
- 自己写的一个计算代码执行时间的函数
- 一行代码搞定Weex蓝牙开发
- ios开发计算代码行数
- 自己动手开发编译器(十二)生成托管代码
- 如何使用网页开发自己的app,在网页中的按钮与自己的java代码绑定来实现打电话即javascript代码调用java代码,和java代码来调用javascript代码
- iOS开发一行代码系列:一行搞定输入框
- 一行代码不用敲,15分钟轻松搞定GIS开发,GISer福音,教你如何从下载到使用webappbuilder创建第一个应用程
- Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]