1 using System;
  2 
  3 namespace Calculator
  4 {
  5     public inline bool IsNumberChar(uchar c)
  6     {
  7         return IsAsciiDigit(c) || c == '.';
  8     }
  9 
 10     public class Scanner
 11     {
 12         public Scanner(const ustring& input_) : input(input_)
 13         {
 14         }
 15         public inline Token* CurrentToken() const
 16         {
 17             return token.Get();
 18         }
 19         public Result<bool> NextToken()
 20         {
 21             SkipWhiteSpace();
 22             if (pos < input.Length())
 23             {
 24                 if (IsNumberChar(input[pos]))
 25                 {
 26                     int start = pos;
 27                     ++pos;
 28                     while (pos < input.Length() && IsNumberChar(input[pos]))
 29                     {
 30                         ++pos;
 31                     }
 32                     auto utf8Result = ToUtf8(input.Substring(startpos - start));
 33                     if (utf8Result.Error())
 34                     {
 35                         return Result<bool>(ErrorId(utf8Result.GetErrorId()));
 36                     }
 37                     auto parseResult = ParseDouble(utf8Result.Value());
 38                     if (parseResult.Error())
 39                     {
 40                         return Result<bool>(ErrorId(parseResult.GetErrorId()));
 41                     }
 42                     double number = parseResult.Value();
 43                     token.Reset(new NumberToken(number));
 44                 }
 45                 else
 46                 {
 47                     auto idStartResult = IsIdStart(input[pos]);
 48                     if (idStartResult.Error())
 49                     {
 50                         return idStartResult;
 51                     }
 52                     bool isIdStart = idStartResult.Value();
 53                     if (isIdStart)
 54                     {
 55                         int start = pos;
 56                         ++pos;
 57                         bool isIdCont = true;
 58                         while (pos < input.Length() && isIdCont)
 59                         {
 60                             auto idContResult = IsIdCont(input[pos]);
 61                             if (idContResult.Error())
 62                             {
 63                                 return idContResult;
 64                             }
 65                             isIdCont = idContResult.Value();
 66                             if (isIdCont)
 67                             {
 68                                 ++pos;
 69                             }
 70                         }
 71                         ustring variableName = input.Substring(startpos - start);
 72                         auto toLowerResult = ToLower(variableName);
 73                         if (toLowerResult.Error())
 74                         {
 75                             return Result<bool>(ErrorId(toLowerResult.GetErrorId()));
 76                         }
 77                         ustring lower = toLowerResult.Value();
 78                         if (lower == u"print")
 79                         {
 80                             token.Reset(new PrintToken());
 81                         }
 82                         else
 83                         {
 84                             token.Reset(new VariableNameToken(variableName));
 85                         }
 86                     }
 87                     else
 88                     {
 89                         token.Reset(new OperatorToken(input[pos]));
 90                         ++pos;
 91                     }
 92                 }
 93             }
 94             else
 95             {
 96                 token.Reset(new EndToken());
 97             }
 98             return Result<bool>(true);
 99         }
100         public void Rewind()
101         {
102             pos = 0;
103         }
104         private void SkipWhiteSpace()
105         {
106             while (pos < input.Length() && (input[pos] == ' ' || input[pos] == '\t'))
107             {
108                 ++pos;
109             }
110         }
111         private UniquePtr<Token> token;
112         private ustring input;
113         private int pos;
114     }