Imports System.Text

Namespace Expression
    ''' <summary>
    ''' 字符算数表达式求和
    ''' </summary>
    Public Class StringExpression
        ''' <summary>精度</summary>
        Private Shared _doublePrecision As Double = 0.000001


        ''' <summary>
        ''' 替换占位符
        ''' </summary>
        ''' <param name="str">字符串表达式</param>
        ''' <returns></returns>
        Public Shared Function UseMarkSymbolReplace(str As String) As String
            Dim destString As New StringBuilder
            str = str.Replace(" ", "")

            str = str.Replace(">>", "r") 'right move
            str = str.Replace("<<", "l") ' left move
            str = str.Replace("||", "o") 'or
            str = str.Replace("&&", "a") 'and
            str = str.Replace(">=", "g") 'greater equal
            str = str.Replace("<=", "s") 'small equal
            str = str.Replace("<>", "u") 'unequal
            str = str.Replace("!=", "u") 'unequal
            str = str.Replace("==", "e") 'equal


            For i As Integer = 0 To str.Length - 1 '替换负号
                If i = 0 Then
                    If str.Chars(i) = "-"c Then
                        destString.Append("0"c)
                    End If
                    destString.Append(str.Chars(i))
                ElseIf str.Chars(i) = "-"c AndAlso str.Chars(i - 1) = "("c Then
                    destString.Append("0"c)
                    destString.Append(str.Chars(i))
                Else
                    destString.Append(str.Chars(i))
                End If
            Next
            Return destString.ToString()
        End Function

        ''' <summary>
        ''' 替换表达式式中的十六进制占位符为实际内容,例如替换B4为buf中的第4位即buf(4)的值
        ''' </summary>
        ''' <param name="buf"></param>
        ''' <param name="expressionString"></param>
        ''' <returns></returns>
        Public Shared Function ReplaceBytes(buf() As Byte, expressionString As String) As String
            Dim ch As Char
            Dim count As Integer = 0
            Dim str As String = ""
            Dim tmp As Integer
            For i As Integer = 0 To expressionString.Length - 1
                If expressionString.Chars(i) = "B"c Then
                    ch = expressionString.Chars(i + count + 1)
                    While IsNumeric(ch)
                        count += 1
                        If i + count + 1 >= expressionString.Length Then Exit While
                        ch = expressionString.Chars(i + count + 1)
                    End While
                    If count <= 0 Then Return ""
                    Dim st As String = Mid(expressionString, i + 1 + 1, count)
                    tmp = Convert.ToInt32(st)
                    If tmp > buf.Length - 1 Then Return ""
                    str += buf(tmp).ToString
                    i += count
                    count = 0
                Else
                    str += expressionString.Chars(i)
                End If
            Next
            Return str
        End Function

        ''' <summary>
        ''' 获取表达式转换后的结果是否为真,0、false、空字符为假，1，true为真, 其余为通过计算不为0判断真假
        ''' </summary>
        ''' <param name="exp"></param>
        ''' <returns></returns>
        Public Shared Function GetBooleanResult(exp As String) As Boolean
            If exp Is Nothing Then Return False
            If String.IsNullOrWhiteSpace(exp) Then Return False
            exp = exp.ToLower()
            If exp.Equals($"false") Then Return False
            If exp.Equals($"true") Then Return True

            exp = exp.Replace("false", "0"c)
            exp = exp.Replace("true", "1"c)

            Dim str As String = GetDoubleExpressionResult(exp).ToString()
            Console.WriteLine($"Result:{str}")
            Return Math.Abs(GetDoubleExpressionResult(exp) - 0) > _doublePrecision
        End Function


        ''' <summary>
        ''' 获取double类型运算式字符串后缀表达式
        ''' </summary>
        ''' <param name="str">字符串表达式</param>
        ''' <returns></returns>
        Public Shared Function GetDoubleExpressionResult(str As String) As Double
            Dim isNum As Boolean
            Dim numStack As New Stack(Of Double)
            Dim operatorStack As New Stack(Of Char)
            Dim num1, num2 As Double
            Dim priority As Integer
            Dim ch As Char
            str = UseMarkSymbolReplace(str)
            For i As Integer = 0 To str.Length - 1
                ch = str.Chars(i)
                If ch >= "0"c AndAlso ch <= "9"c Then '数字处理
                    Dim tmpStr As String = String.Empty
                    Dim len As Integer = 1
                    tmpStr += ch
                    isNum = True
                    While isNum
                        If i + len >= str.Length Then Exit While
                        ch = str.Chars(i + len)
                        If ch >= "0"c AndAlso ch <= "9"c Then
                            tmpStr += ch
                            len += 1
                        ElseIf ch = "."c Then
                            tmpStr += ch
                            len += 1
                        Else
                            isNum = False
                        End If
                    End While
                    numStack.Push(Double.Parse(tmpStr))
                    i += len - 1
                ElseIf CheckCharValidity(ch) = 1 Then '运算符号
                    If operatorStack.Count = 0 Then
                        operatorStack.Push(ch)
                    Else
                        priority = GetCharacterPriority(ch) '获取当前符号的优先级
                        While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
                            num1 = numStack.Pop
                            num2 = numStack.Pop
                            numStack.Push(GetDoubleResult(num2, num1, operatorStack.Pop))
                        End While
                        If priority = 0 Then '为后半边括号
                            operatorStack.Pop()
                        Else '为前半边括号
                            operatorStack.Push(ch)
                        End If
                    End If
                End If

                If i = str.Length - 1 Then
                    priority = -1
                    While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
                        num1 = numStack.Pop
                        num2 = numStack.Pop
                        numStack.Push(GetDoubleResult(num2, num1, operatorStack.Pop))
                    End While
                End If
            Next

            Return Math.Round(numStack.Pop, 3) '计算保留小数位三位
        End Function

        ''' <summary>
        ''' 获取小数类型运算结果
        ''' </summary>
        ''' <param name="num1">浮点型数1</param>
        ''' <param name="num2">浮点型数2</param>
        ''' <param name="c">运算符</param>
        ''' <returns></returns>
        Public Shared Function GetDoubleResult(num1 As Double, num2 As Double, c As Char) As Double
            Dim result As Double
            Select Case c
                Case "*"c
                    result = num1 * num2
                Case "/"c
                    If Math.Abs(num2 - 0) < _doublePrecision Then Throw New Exception($"除数为0执行失败")
                    result = num1 / num2
                Case "%"c
                    result = num1 Mod num2
                Case "+"c
                    result = num1 + num2
                Case "-"c
                    result = num1 - num2
                Case "l"c '左移
                    result = CInt(num1) << CInt(num2)
                Case "r"c '右移
                    result = CInt(num1) >> CInt(num2)
                Case "&"c
                    result = CInt(num1) And CInt(num2)
                Case "^"c
                    result = CInt(num1) Xor CInt(num2)
                Case "|"c
                    result = CInt(num1) Or CInt(num2)

                Case ">"c '大于等于
                    result = CInt(IIf(num1 > num2, 1, 0))
                Case "<"c '大于等于
                    result = CInt(IIf(num1 < num2, 1, 0))
                Case "="c '等于
                    result = CInt(IIf(Math.Abs(num1 - num2) < _doublePrecision, 1, 0))
                Case "g"c '大于等于
                    result = CInt(IIf(num1 >= num2, 1, 0))
                Case "s"c '小于等于
                    result = CInt(IIf(num1 <= num2, 1, 0))
                Case "u"c '不等于
                    result = CInt(IIf(Math.Abs(num1 - num2) > _doublePrecision, 1, 0))
                Case "e"c '等于
                    result = CInt(IIf(Math.Abs(num1 - num2) < _doublePrecision, 1, 0))

                Case "a"c '逻辑与
                    If num1 > 0 AndAlso num2 > 0 Then
                        result = 1
                    Else
                        result = 0
                    End If
                Case "o"c '逻辑或
                    If num1 > 0 OrElse num2 > 0 Then
                        result = 1
                    Else
                        result = 0
                    End If
                Case Else
                    Throw New Exception($"未知的操作符：[{c}]")
            End Select
            Return result
        End Function


        ''' <summary>
        ''' 获取符号的优先级
        ''' </summary>
        ''' <param name="c">运算符号</param>
        ''' <returns></returns>
        Private Shared Function GetCharacterPriority(c As Char) As Integer
            Dim intNum As Integer
            Select Case c
                Case "("c
                    intNum = 11
                Case "*"c, "/"c, "%"c
                    intNum = 10
                Case "+"c, "-"c
                    intNum = 9
                Case "l"c, "r"c
                    intNum = 8
                Case ">"c, "<"c, "b"c, "s"c
                    intNum = 7
                Case "="c, "u"c, "e"c
                    intNum = 6
                Case "&"c
                    intNum = 5
                Case "^"c
                    intNum = 4
                Case "|"c
                    intNum = 3
                Case "a"c
                    intNum = 2
                Case "o"c
                    intNum = 1
                Case ")"c
                    intNum = 0
                Case Else
                    Throw New Exception($"未知的操作符：[{c}]")
            End Select
            Return intNum
        End Function

        ''' <summary>
        ''' 校验字符合法性，0为数字，1为运算符，-1为未使用的运算符
        ''' </summary>
        ''' <param name="c">需要校验的字符</param>
        ''' <returns></returns>
        Private Shared Function CheckCharValidity(c As Char) As Integer
            Select Case c
                Case "1"c, "2"c, "3"c, "4"c, "5"c, "6"c, "7"c, "8"c, "9"c, "0"c

                    Return 0
                Case "+"c, "-"c, "*"c, "/"c, "%"c, "^"c, "|"c, "&"c, "("c, ")"c, "o"c, "a"c, "l"c, "r"c, "."c,
                    "u"c, "g"c, "s"c, "<"c, ">"c, "="c, "e"c

                    Return 1
                Case Else
                    Return -1
            End Select
        End Function


        ''' <summary>
        ''' 校验运算式字符串合法性(todo:待进一步完善检测逻辑)
        ''' </summary>
        ''' <param name="str">需要校验的字符串</param>
        ''' <returns></returns>
        Public Shared Function CheckExpressionString(str As String) As Boolean
            str = str.Replace(" ", "")
            For i As Integer = 0 To str.Length - 1
                If CheckCharValidity(str.Chars(i)) = -1 Then
                    Return False
                End If
            Next
            Return True
        End Function




        '''' <summary>
        '''' 获取int类型运算式字符串后缀表达式
        '''' </summary>
        '''' <param name="str">字符串表达式</param>
        '''' <returns></returns>
        'Public Shared Function GetIntegerExpressionResult(str As String) As Integer
        '    Dim isNum As Boolean
        '    Dim numberStack As New Stack(Of Integer)
        '    Dim operatorStack As New Stack(Of Char)
        '    Dim num1, num2, priority As Integer
        '    str = UseMarkSymbolReplace(str, False)

        '    For Each c As Char In str
        '        If c >= "0"c AndAlso c <= "9"c Then
        '            If isNum Then
        '                Dim tmp As Integer = numberStack.Pop
        '                tmp = tmp * 10 + Integer.Parse(c.ToString)
        '                numberStack.Push(tmp)
        '            Else
        '                numberStack.Push(Integer.Parse(c.ToString))
        '                isNum = True
        '            End If
        '        ElseIf CheckCharValidity(c) = 1 Then
        '            isNum = False
        '            If operatorStack.Count = 0 Then
        '                operatorStack.Push(c)
        '            Else
        '                priority = GetCharacterPriority(c)
        '                While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
        '                    num1 = numberStack.Pop
        '                    num2 = numberStack.Pop
        '                    numberStack.Push(GetIntegerResult(num2, num1, operatorStack.Pop))
        '                End While
        '                If priority = 0 Then
        '                    operatorStack.Pop()
        '                Else
        '                    operatorStack.Push(c)
        '                End If
        '            End If
        '        End If

        '        If c = str.Chars(str.Length - 1) Then
        '            priority = GetCharacterPriority(c)
        '            While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
        '                num1 = numberStack.Pop
        '                num2 = numberStack.Pop
        '                numberStack.Push(GetIntegerResult(num2, num1, operatorStack.Pop))
        '            End While
        '        End If
        '    Next

        '    Return numberStack.Pop
        'End Function

        '''' <summary>
        '''' 获取整形的运算结果
        '''' </summary>
        '''' <param name="num1">整数1</param>
        '''' <param name="num2">整数2</param>
        '''' <param name="c">运算符</param>
        '''' <returns>预算结果</returns>
        'Public Shared Function GetIntegerResult(num1 As Integer, num2 As Integer, c As Char) As Integer
        '    Dim result As Integer
        '    Select Case c
        '        Case "*"c
        '            result = num1 * num2
        '        Case "/"c
        '            result = num1 \ num2
        '        Case "%"c
        '            result = num1 Mod num2
        '        Case "+"c
        '            result = num1 + num2
        '        Case "-"c
        '            result = num1 - num2
        '        Case "L"c
        '            result = num1 << num2
        '        Case "R"c
        '            result = num1 >> num2
        '        Case "&"c
        '            result = num1 And num2
        '        Case "^"c
        '            result = num1 Xor num2
        '        Case "|"c
        '            result = num1 Or num2

        '        Case ">"c '大于等于
        '            result = CInt(IIf(num1 > num2, 1, 0))
        '        Case "<"c '大于等于
        '            result = CInt(IIf(num1 < num2, 1, 0))
        '        Case "="c '等于
        '            result = CInt(IIf(num1 = num2, 1, 0))
        '        Case "g"c '大于等于
        '            result = CInt(IIf(num1 >= num2, 1, 0))
        '        Case "l"c '小于等于
        '            result = CInt(IIf(num1 <= num2, 1, 0))
        '        Case "u"c '不等于
        '            result = CInt(IIf(num1 <> num2, 1, 0))
        '        Case "e"c '等于
        '            result = CInt(IIf(num1 = num2, 1, 0))

        '        Case "A"c
        '            If num1 > 0 AndAlso num2 > 0 Then
        '                result = 1
        '            Else
        '                result = 0
        '            End If
        '        Case "O"c
        '            If num1 > 0 OrElse num2 > 0 Then
        '                result = 1
        '            Else
        '                result = 0
        '            End If
        '        Case Else
        '            Throw New Exception($"未知的操作符：[{c}]")
        '    End Select
        '    Return result
        'End Function


        ' Private Shared ReadOnly _operateList As New List(Of Char) From {"+"c, "*"c, "/"c, "%"c, "-"c, "^"c, "|"c, "&"c}



        'Private Shared Function CheckDoubleResultChar(exp As String) As Boolean
        '    For i As Integer = 0 To exp.Length - 1
        '        If exp.Chars(i) > "0"c AndAlso exp.Chars(i) < "9"c Then
        '            '有效数字
        '            Continue For
        '        End If


        '        Select Case exp.Chars(i)
        '            Case "-"c
        '                '减运算符前不能为运算符


        '            Case "+"c, "*"c, "/"c, "%"c
        '                '运算符前后位必须为数字
        '                If i = 0 Then Return False '不能为开头
        '                If i = exp.Length - 1 Then Return False '不能为结尾
        '              '  If exp.Chars(i + 1) Then Return False'下一位不能

        '            Case "!"c'是否应该支持
        '                '按位操作前后须有数字

        '            Case "^"c
        '                '按位操作前后须有数字

        '            Case "|"c
        '                '按位操作前后须有数字

        '                '逻辑操作前后须有数字


        '                '确定是否为逻辑操作

        '            Case "&"c
        '                '按位操作前后须有数字

        '                '逻辑操作前后须有数字


        '                '确定是否为逻辑操作

        '            Case ">"c
        '                '判断逻辑符或是组合符号

        '            Case "<"c
        '                '判断逻辑符或是组合符号

        '            Case "="c

        '                '判断逻辑符或是组合符号

        '            Case "."c
        '                '前后必为数字

        '            Case "("c
        '                '括号必须匹配

        '            Case ")"c
        '                '括号必须匹配

        '            Case " "c
        '                '空格忽略

        '            Case Else


        '        End Select
        '    Next


        '    Return False
        'End Function


        ''' <summary>
        ''' 返回表达式的值 'Momo 2022-11-10 增加 、Momo 2023-12-15 错误时抛出错误而不是弹窗
        ''' </summary>
        ''' <returns></returns>
        Public Shared Function ExecuteLimit(ByVal strExpression As String, ByRef dblResult As String) As Boolean
            '检测有无需要计算的表达式
            If String.IsNullOrWhiteSpace(strExpression) Then
                '’MsgBox(strExpression & " 是空白值或表达式，执行失败！")
                'Throw New Exception(strExpression & " 是空白值或表达式，执行失败！") 'Momo 2023-12-15 错误时抛出错误而不是弹窗 
                Return False
            End If

            '检测表达式的有效性
            If StringExpression.CheckExpressionString(strExpression) = False Then
                '’MsgBox(strExpression & " 是无效值或表达式，执行失败！")
                'Throw New Exception(strExpression & " 是无效值或表达式，执行失败！") 'Momo 2023-12-15 错误时抛出错误而不是弹窗 
                Return False
            End If

            Try
                dblResult = StringExpression.GetDoubleExpressionResult(strExpression).ToString()
                Return True
            Catch ex As Exception
                '’MsgBox(strExpression & " 表达式计算失败，执行失败！")
                'Throw New Exception(strExpression & " 表达式计算失败，执行失败！") 'Momo 2023-12-15 错误时抛出错误而不是弹窗 
                Return False
            End Try

        End Function


    End Class
End Namespace