protected virtual string ReadIdent()
        {
            var sb = new StringBuilder();

            char ch = _reader.NextChar;

            sb.Append(ch);
            ch = _reader.Next();

            while (CommandStream.IsAlpha(ch) || CommandStream.IsDigit(ch))
            {
                sb.Append(ch);
                ch = _reader.Next();
            }

            return(sb.ToString());
        }
 protected virtual bool IsIdentStart(char ch)
 {
     return(CommandStream.IsAlpha(ch));
 }
 public ExpressionParser(CommandStream reader) : base(reader)
 {
 }
        protected virtual void ScanNextToken()
        {
            char ch = _reader.NextChar;

            if (IsToken("||", false, false))
            {
                _state._detailToken = ETokenType.XOrSy;
                return;
            }

            if (IsToken("<<", false, false))
            {
                _state._detailToken = ETokenType.BitShiftLeftSy;
                return;
            }

            if (IsToken(">>", false, false))
            {
                _state._detailToken = ETokenType.BitShiftRightSy;
                return;
            }

            if (IsToken("==", false, false))
            {
                _state._detailToken = ETokenType.EqualSy;
                return;
            }

            if (IsToken("!=", false, false))
            {
                _state._detailToken = ETokenType.UnEqualSy;
                return;
            }

            if (IsToken(">=", false, false))
            {
                _state._detailToken = ETokenType.GreaterEqualSy;
                return;
            }

            if (IsToken("<=", false, false))
            {
                _state._detailToken = ETokenType.LessEqualSy;
                return;
            }

            if (ch == LeftParenthesis)
            {
                _state._detailToken = ETokenType.LeftParenthesisSy;
                _reader.Next();
                return;
            }

            if (ch == RightParenthesis)
            {
                _state._detailToken = ETokenType.RightParenthesisSy;
                _reader.Next();
                return;
            }

            switch (ch)
            {
            case '>':
                _state._detailToken = ETokenType.GreaterSy;
                _reader.Next();
                return;

            case '<':
                _state._detailToken = ETokenType.LessSy;
                _reader.Next();
                return;

            case '&':
                _state._detailToken = ETokenType.AndSy;
                _reader.Next();
                return;

            case '|':
                _state._detailToken = ETokenType.OrSy;
                _reader.Next();
                return;

            case '-':
                _state._detailToken = ETokenType.MinusSy;
                _reader.Next();
                return;

            case '+':
                _state._detailToken = ETokenType.PlusSy;
                _reader.Next();
                return;

            case '*':
                _state._detailToken = ETokenType.MultiplySy;
                _reader.Next();
                return;

            case '/':
                _state._detailToken = ETokenType.DivideSy;
                _reader.Next();
                return;

            case '%':
                _state._detailToken = ETokenType.ModuloSy;
                _reader.Next();
                return;

            case '^':
                _state._detailToken = ETokenType.PowSy;
                _reader.Next();
                return;

            case '!':
                _state._detailToken = ETokenType.FactorialSy;
                _reader.Next();
                return;

            case '=':
                _state._detailToken = ETokenType.AssignSy;
                _reader.Next();
                return;
            }

            // check for a value
            if (CommandStream.IsNumber(ch))
            {
                _state._detailToken = ETokenType.FloatSy;
                _state._number      = _reader.GetDouble(out bool istFloatingPoint);
                return;
            }

            // check for variables or functions
            if (IsIdentStart(ch))
            {
                var start = ReadIdent();
                ch = _reader.SkipSpaces();

                // check if this is a variable or a function.
                // a function has a parenthesis '(' open after the name

                if (ch == LeftParenthesis)
                {
                    switch (start.ToUpper())
                    {
                    case "ABS":
                        _state._detailToken = ETokenType.AbsSy;
                        return;

                    case "EXP":
                        _state._detailToken = ETokenType.ExpSy;
                        return;

                    case "SIGN":
                        _state._detailToken = ETokenType.SignSy;
                        return;

                    case "SQRT":
                        _state._detailToken = ETokenType.SqrtSy;
                        return;

                    case "LOG":
                        _state._detailToken = ETokenType.LogSy;
                        return;

                    case "LOG10":
                        _state._detailToken = ETokenType.Log10Sy;
                        return;

                    case "SIN":
                        _state._detailToken = ETokenType.SinSy;
                        return;

                    case "COS":
                        _state._detailToken = ETokenType.CosSy;
                        return;

                    case "TAN":
                        _state._detailToken = ETokenType.TanSy;
                        return;

                    case "ASIN":
                        _state._detailToken = ETokenType.AsinSy;
                        return;

                    case "ACOS":
                        _state._detailToken = ETokenType.AcosSy;
                        return;

                    case "ATAN":
                        _state._detailToken = ETokenType.AtanSy;
                        return;

                    case "FACTORIAL":
                        _state._detailToken = ETokenType.FactorialFncSy;
                        return;

                    case "FIX":
                        _state._detailToken = ETokenType.FixSy;
                        return;

                    case "FUP":
                        _state._detailToken = ETokenType.FupSy;
                        return;

                    case "ROUND":
                        _state._detailToken = ETokenType.RoundSy;
                        return;

                    default:
                        Error(MESSAGE_EXPR_UNKNOWN_FUNCTION);
                        return;
                    }
                }
                else
                {
                    _state._detailToken = ETokenType.VariableSy;
                    _state._variableOK  = EvalVariable(start, ref _state._number);
                }

                return;
            }

            // something unknown is found, wrong characters -> a syntax error
            _state._detailToken = ETokenType.UnknownSy;
            Error(MESSAGE_EXPR_SYNTAX_ERROR);
        }