자체 개발 계산기 (3) - 140 줄 코드 해결 Token 분석

해석 산식 이 든 스 크 립 트 든 문법 을 분석 하기 전에 어법 분석 과정 을 도입 한다.쉽게 말 하면 우 리 는 다음 과 같은 산식 이 있다.
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)
다음은 소스 코드 를 보 겠 습 니 다.사용 하 는 곳 부터 보 세 요.
 

  
  
  
  
  1. QList<Token*> CalculateEngine::analyzeToken(QString strQuestion)  
  2. {  
  3.     typedef TokenAnalyzer<Token, Token::EType, QList<Token*>::iterator> MyAnalyzer;  
  4.  
  5.     class MyFactory : public MyAnalyzer::TokenPatternFactory  
  6.     {  
  7.         virtual int createPatterns(QList<MyAnalyzer::TokenPattern*>& list) const 
  8.         {  
  9.             FunctionManager* manager = FunctionManager::getInstance();  
  10.             QList<QString> functions = manager->functions();  
  11.  
  12.             QString funPattern;  
  13.             foreach(QString funName, functions)  
  14.             {  
  15.                 if(funPattern.length() > 0)  
  16.                 {  
  17.                     funPattern += "|";  
  18.                 }  
  19.                 funPattern += funName;  
  20.             }  
  21.             list.append(new MyAnalyzer::TokenPattern(Token::FunctionName, funPattern));  
  22.  
  23.             list.append(new MyAnalyzer::TokenPattern(Token::Number, "((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))[eE][+-]?[0-9]+"));  
  24.             list.append(new MyAnalyzer::TokenPattern(Token::Number, "(\\.[0-9]+)|([0-9]+\\.[0-9]*)"));  
  25.             list.append(new MyAnalyzer::TokenPattern(Token::Number, "[0-9]+"));  
  26.             list.append(new MyAnalyzer::TokenPattern(Token::Operator, "[-+*/%]"));  
  27.             list.append(new MyAnalyzer::TokenPattern(Token::Parenthese, "[()]"));  
  28.             list.append(new MyAnalyzer::TokenPattern(Token::Comma, ","));  
  29.  
  30.             return list.count();  
  31.         }  
  32.     };  
  33.  
  34.     MyFactory factory;  
  35.  
  36.     MyAnalyzer analyzer;  
  37.     QList<Token*> tokenList = analyzer.analyzeToken(strQuestion, &factory);  
  38.     return tokenList;  

정규 표현 식 을 준비 하 는 데 많은 시간 을 들 인 후에 TokenAnalyzer:: analyzer Token 을 호출 하면 됩 니 다.다음은 TokenAnalyzer 의 소스 코드 입 니 다.Token 의 유형 이 수요 에 따라 다 를 수 있 음 을 고려 하여 템 플 릿 류 를 사 용 했 습 니 다.

  
  
  
  
  1. #ifndef TOKENANALYZER_H
  2. #define TOKENANALYZER_H
  3. #include<QString>
  4. #include<QList>
  5. #include<QRegExp>
  6. template<typename Token, typename TokenType, typename TokenIterator>
  7. class TokenAnalyzer
  8. {
  9. public:
  10. struct TokenPattern
  11. {
  12. TokenPattern(TokenType _type, QString _regex):regex(_regex),type(_type){}
  13. QRegExp regex;
  14. TokenType type;
  15. };
  16. class TokenPatternFactory
  17. {
  18. public:
  19. virtual int createPatterns(QList<TokenPattern*>& list) const= 0;
  20. };
  21. TokenAnalyzer(){}
  22. QList<Token*> analyzeToken(QString strInput, const TokenPatternFactory* factory);
  23. private:
  24. struct Context
  25. {
  26. Context(QList<Token*>& list, TokenIterator& _it, TokenPattern& _pattern, QString& _content)
  27. :tokenList(list), it(_it), pattern(_pattern), content(_content){}
  28. QList<Token*>& tokenList;
  29. TokenIterator& it;
  30. TokenPattern& pattern;
  31. QString& content;
  32. };
  33. void analyzeContent(Context& context);
  34. };
  35. template<typename Token, typename TokenType, typename TokenIterator>
  36. QList<Token*> TokenAnalyzer<Token, TokenType, TokenIterator>::analyzeToken(QString strInput, const TokenPatternFactory* factory)
  37. {
  38. QList<Token*> tokenList;
  39. tokenList.append(new Token(strInput));
  40. QList<TokenPattern*> list;
  41. factory->createPatterns(list);
  42. foreach(TokenPattern* pattern, list)
  43. {
  44. TokenIterator it = tokenList.begin();
  45. while(it != tokenList.end())
  46. {
  47. Token* token = *it;
  48. if(token->isNoType())
  49. {
  50. QString content = token->getContent();
  51. Context context(tokenList, it, *pattern, content);
  52. analyzeContent(context);
  53. }
  54. it++;
  55. }
  56. }
  57. return tokenList;
  58. }
  59. template<typename Token, typename TokenType, typename TokenIterator>
  60. void TokenAnalyzer<Token, TokenType, TokenIterator>::analyzeContent(Context& context)
  61. {
  62. Token* token = *context.it;
  63. int tokenBegin = context.content.indexOf(context.pattern.regex);
  64. if(tokenBegin != -1)
  65. {
  66. int matchedLength = context.pattern.regex.matchedLength();
  67. int tokenEnd = tokenBegin + matchedLength;
  68. if(tokenBegin > 0)
  69. {
  70. context.it = context.tokenList.insert(context.it, new Token(context.content.left(tokenBegin)));
  71. context.it++;
  72. }
  73. if(tokenEnd < context.content.length())
  74. {
  75. context.it = context.tokenList.insert(context.it,
  76. new Token(context.pattern.type, context.content.mid(tokenBegin, matchedLength)));
  77. context.it++;
  78. context.content.remove(0, tokenEnd);
  79. analyzeContent(context);
  80. }
  81. else
  82. {
  83. token->setContent(context.content.mid(tokenBegin, tokenEnd));
  84. token->setType(context.pattern.type);
  85. context.content.remove(0, tokenEnd);
  86. }
  87. }
  88. else
  89. {
  90. token->setContent(context.content);
  91. }
  92. }
  93. #endif // TOKENANALYZER_H

정규 표현 식 을 정의 하 는 부분 을 포함 하면 딱 120 줄 입 니 다.
물론 빠 질 수 없다. 진정한 주인공 Token 류.

  
  
  
  
  1. class Token
  2. {
  3. public:
  4. enum EType
  5. {
  6. NoType,
  7. Operator,
  8. Number,
  9. FunctionName
  10. };
  11. Token(EType type, QString content);
  12. Token(QString content);
  13. EType getType();
  14. QString getContent();
  15. void setType(EType type);
  16. bool isNoType();
  17. void setContent(QString content);
  18. private:
  19. EType mType;
  20. QString mContent;
  21. };

기타 관련 글 은 참고 하 시기 바 랍 니 다.
  • 자체 개발 계산기 (0) - 확장 바 커 스 패 러 다 임 (EBNF)
  • 자체 개발 계산기 (1) - 개발 환경 준비
  • 계산기 자체 개발 (2) - 새로운 조작 방식
  • 계산기 자체 개발 (4) - 완성!소스 코드 공개!
  • 좋은 웹페이지 즐겨찾기