解析算式也好,脚本也罢,在分析语法之前,一般都会引入词法分析的过程。简单说比如我们有如下算式
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版) 】一书。至于实际的内容建议大家看一下另一篇文章:
下面看看源代码。先从使用的地方看起。
- 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;
- };
其他关联文章请参考。
转载于:https://blog.51cto.com/craftsman1970/679002