Fixed this now, at least for the mentioned test expression.Just trying this out:
[B4X] Eval (expressions evaluator)
The attached class allows you to evaluate mathematical expressions with support for custom functions. It is compatible with B4A, B4J and B4i. Example: Sub AppStart (Args() As String) Dim e As B4XEval e.Initialize(Me, "Eval") Log(e.Eval("1 + Min(2, Max(-4, 1), 6)"))...www.b4x.com 
And trying to make it so that it will handle any expression, so also faulty expressions.
For now I am just testing with this simple but faulty expression:
6/2(2+1)
Added lots of Try Catch and made most return values of type Object (so it can return "error"), but sofar no success
in making Eval return eg "error". I managed to make it return "error" but that was only in debug mode.
Any suggestions how this should be done?
RBS
'version 2.01
#Event: Function(Name As String, Values As List) As Double
Sub Class_Globals
    Private Const NUMBER_TYPE = 1, OPERATOR_TYPE = 2 As Int
    Type ParsedNode (Operator As String, Left As ParsedNode, Right As ParsedNode, _
        NodeType As Int, Value As Double)
    Type OrderData (Index As Int, Level As Int, Added As Int)
    Private root As ParsedNode
    Private ParseIndex As Int
    Private Nodes As List
    Private OperatorLevel As Map
    Public Error As Boolean
    Private mCallback As Object
    Private mEventName As String
End Sub
Public Sub Initialize (Callback As Object, EventName As String)
    
    mCallback = Callback
    mEventName = EventName
    OperatorLevel = CreateMap("+": 1, "-": 1, "*":2, "/": 2)
    
End Sub
Public Sub Eval(Expression As String) As Object
    
    Dim d As Double
    
    Error = False
    Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(")
    d = EvalHelper(Expression)
    
    If Error Then
        Return "error"
    Else
        Return d
    End If
    
End Sub
Private Sub PrepareExpression(expr As String) As String
    
    If Error Then Return "error"
    
    Dim m As Matcher = Regex.Matcher("(\w*)\(", expr)
    Dim sb As StringBuilder
    Dim lastMatchEnd As Int = 0
    
    sb.Initialize
    
    Do While m.Find
        Dim currentStart As Int = m.GetStart(0)
        If currentStart < lastMatchEnd Then Continue
        sb.Append(expr.SubString2(lastMatchEnd, currentStart))
        Dim functionCall As Boolean
        Dim args As List
        If m.Match.Length > 1 Then
            args.Initialize
            functionCall = True
        End If
        Dim level As Int
        Dim start As Int = m.GetEnd(0)
        For i = start To expr.Length - 1
            If expr.CharAt(i) = "(" Then
                level = level + 1
            Else if expr.CharAt(i) = "," And level = 0 Then
                args.Add(CalcSubExpression(expr.SubString2(start, i)))
                start = i + 1
            Else if expr.CharAt(i) = ")" Then
                level = level - 1
                If level = -1 Then
                    Dim d As Double = CalcSubExpression(expr.SubString2(start, i))
                    If functionCall Then
                        args.Add(d)
                        Try
                            d = CallSub3(mCallback, mEventName & "_Function", m.Match.SubString2(0, m.Match.Length - 1), args)
                        Catch
                            Error = True
                            Return "error"
                        End Try
                    End If
                    sb.Append(NumberFormat2(d, 1, 15, 0, False))
                    lastMatchEnd = i + 1
                    Exit
                End If
            End If
        Next
    Loop
    
    If lastMatchEnd < expr.Length Then sb.Append(expr.SubString(lastMatchEnd))
    Return sb.ToString
    
End Sub
Private Sub CalcSubExpression (expr As String) As Double
    
    If Error Then Return 0
    
    Dim be As B4XEval
    be.Initialize (mCallback, mEventName)
    Dim d As Double = be.EvalHelper(expr)
    
    If be.Error Then
        Error = True
        Return 0
    End If
    
    Return d
    
End Sub
'only evaluates numbers and operators. No functions or parenthesis here.
Private Sub EvalHelper (expr As String) As Double
    
    If Error Then Return 0
    
    ParseIndex = 0
    Dim root As ParsedNode
    root.Initialize
    expr = PrepareExpression(expr)
    'If Error Then Return 0
    Dim m As Matcher = Regex.Matcher("[\.\d]+", expr)
    Nodes.Initialize
    Dim lastIndex As Int = 0
    Dim currentOrderData As OrderData
    
    currentOrderData.Initialize
    Nodes.Add(CreateOperatorNode("("))
    
    Do While m.Find
        Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0))
        Dim rawNumber As String = m.Match
        If Operator.EndsWith("-") Then
            Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1)
            If lastNode.Operator = "(" Or Operator.Length > 1 Then 
            'handle negative numbers: (-2 + 1, 1/-2
                Operator = Operator.SubString2(0, Operator.Length - 1)
                rawNumber = "-" & rawNumber
            End If
        End If
        lastIndex = m.GetEnd(0)
        If Operator.Length > 0 Then
            Dim level As Int = OperatorLevel.Get(Operator)
            If currentOrderData.Level > 0 Then
                If currentOrderData.Level < level Then
                    Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("("))
                    currentOrderData.Added = currentOrderData.Added + 1
                Else if currentOrderData.Level > level Then
                    If currentOrderData.Added > 0 Then
                        Nodes.Add(CreateOperatorNode(")"))
                        currentOrderData.Added = currentOrderData.Added - 1 
                    End If
                End If
            End If
            currentOrderData.Level = level
            currentOrderData.Index = Nodes.Size + 1
            Nodes.Add(CreateOperatorNode(Operator))
        End If
        Dim d As Double = rawNumber
        Nodes.Add(CreateNumberNode(d))
    Loop
    
    For i = 1 To currentOrderData.Added
        Nodes.Add(CreateOperatorNode(")"))
    Next
    
    Nodes.Add(CreateOperatorNode(")"))
    root = BuildTree
    
    Return EvalNode(root)
    
End Sub
private Sub BuildTree As ParsedNode
    
    If Error Then Return Null
    
    Dim rt As ParsedNode
    
    Do While ParseIndex < Nodes.Size
        Dim pn As ParsedNode = TakeNextNode
        Dim built As Boolean
        If pn.Operator = ")" Then 
            Exit
        Else If pn.Operator = "(" Then 
            pn = BuildTree
            built = True
        End If
        If pn.NodeType = NUMBER_TYPE Or built Then
            If rt.IsInitialized Then
                rt.Right = pn
            Else
                rt = pn
            End If
        Else if pn.NodeType = OPERATOR_TYPE Then
            pn.Left = rt
            rt = pn
        End If
    Loop
    
    If rt.IsInitialized = False Then rt = pn
    
    Return rt
    
End Sub
Private Sub EvalNode (pn As ParsedNode) As Double
    
    If Error Then Return 0
    
    If pn.NodeType = NUMBER_TYPE Then Return pn.Value
    Dim left As Double = EvalNode(pn.Left)
    Dim right As Double = EvalNode(pn.Right)
    
    Select pn.Operator
        Case "+"
            Return left + right
        Case "-"
            Return left - right
        Case "*"
            Return left * right
        Case "/"
            Return left / right
        Case Else
            Log("Syntax error: " & pn.Operator)
            Error = True
            Return "error"
    End Select
    
End Sub
private Sub TakeNextNode As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode = Nodes.Get(ParseIndex)
    ParseIndex = ParseIndex + 1
    
    Return pn
    
End Sub
Private Sub CreateOperatorNode(operator As String) As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode
    pn.Initialize
    pn.NodeType = OPERATOR_TYPE
    pn.Operator = operator
    
    Return pn
    
End Sub
Private Sub CreateNumberNode (d As Double) As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode
    
    pn.Initialize
    pn.NodeType = NUMBER_TYPE
    pn.Value = d
    
    Return pn
    
End Sub
	Added some further error handling and I think this may cover all now:Fixed this now, at least for the mentioned test expression.
This is the text of the class B4XEval:
B4X:'version 2.01 #Event: Function(Name As String, Values As List) As Double Sub Class_Globals Private Const NUMBER_TYPE = 1, OPERATOR_TYPE = 2 As Int Type ParsedNode (Operator As String, Left As ParsedNode, Right As ParsedNode, _ NodeType As Int, Value As Double) Type OrderData (Index As Int, Level As Int, Added As Int) Private root As ParsedNode Private ParseIndex As Int Private Nodes As List Private OperatorLevel As Map Public Error As Boolean Private mCallback As Object Private mEventName As String End Sub Public Sub Initialize (Callback As Object, EventName As String) mCallback = Callback mEventName = EventName OperatorLevel = CreateMap("+": 1, "-": 1, "*":2, "/": 2) End Sub Public Sub Eval(Expression As String) As Object Dim d As Double Error = False Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(") d = EvalHelper(Expression) If Error Then Return "error" Else Return d End If End Sub Private Sub PrepareExpression(expr As String) As String If Error Then Return "error" Dim m As Matcher = Regex.Matcher("(\w*)\(", expr) Dim sb As StringBuilder Dim lastMatchEnd As Int = 0 sb.Initialize Do While m.Find Dim currentStart As Int = m.GetStart(0) If currentStart < lastMatchEnd Then Continue sb.Append(expr.SubString2(lastMatchEnd, currentStart)) Dim functionCall As Boolean Dim args As List If m.Match.Length > 1 Then args.Initialize functionCall = True End If Dim level As Int Dim start As Int = m.GetEnd(0) For i = start To expr.Length - 1 If expr.CharAt(i) = "(" Then level = level + 1 Else if expr.CharAt(i) = "," And level = 0 Then args.Add(CalcSubExpression(expr.SubString2(start, i))) start = i + 1 Else if expr.CharAt(i) = ")" Then level = level - 1 If level = -1 Then Dim d As Double = CalcSubExpression(expr.SubString2(start, i)) If functionCall Then args.Add(d) Try d = CallSub3(mCallback, mEventName & "_Function", m.Match.SubString2(0, m.Match.Length - 1), args) Catch Error = True Return "error" End Try End If sb.Append(NumberFormat2(d, 1, 15, 0, False)) lastMatchEnd = i + 1 Exit End If End If Next Loop If lastMatchEnd < expr.Length Then sb.Append(expr.SubString(lastMatchEnd)) Return sb.ToString End Sub Private Sub CalcSubExpression (expr As String) As Double If Error Then Return 0 Dim be As B4XEval be.Initialize (mCallback, mEventName) Dim d As Double = be.EvalHelper(expr) If be.Error Then Error = True Return 0 End If Return d End Sub 'only evaluates numbers and operators. No functions or parenthesis here. Private Sub EvalHelper (expr As String) As Double If Error Then Return 0 ParseIndex = 0 Dim root As ParsedNode root.Initialize expr = PrepareExpression(expr) 'If Error Then Return 0 Dim m As Matcher = Regex.Matcher("[\.\d]+", expr) Nodes.Initialize Dim lastIndex As Int = 0 Dim currentOrderData As OrderData currentOrderData.Initialize Nodes.Add(CreateOperatorNode("(")) Do While m.Find Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0)) Dim rawNumber As String = m.Match If Operator.EndsWith("-") Then Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1) If lastNode.Operator = "(" Or Operator.Length > 1 Then 'handle negative numbers: (-2 + 1, 1/-2 Operator = Operator.SubString2(0, Operator.Length - 1) rawNumber = "-" & rawNumber End If End If lastIndex = m.GetEnd(0) If Operator.Length > 0 Then Dim level As Int = OperatorLevel.Get(Operator) If currentOrderData.Level > 0 Then If currentOrderData.Level < level Then Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("(")) currentOrderData.Added = currentOrderData.Added + 1 Else if currentOrderData.Level > level Then If currentOrderData.Added > 0 Then Nodes.Add(CreateOperatorNode(")")) currentOrderData.Added = currentOrderData.Added - 1 End If End If End If currentOrderData.Level = level currentOrderData.Index = Nodes.Size + 1 Nodes.Add(CreateOperatorNode(Operator)) End If Dim d As Double = rawNumber Nodes.Add(CreateNumberNode(d)) Loop For i = 1 To currentOrderData.Added Nodes.Add(CreateOperatorNode(")")) Next Nodes.Add(CreateOperatorNode(")")) root = BuildTree Return EvalNode(root) End Sub private Sub BuildTree As ParsedNode If Error Then Return Null Dim rt As ParsedNode Do While ParseIndex < Nodes.Size Dim pn As ParsedNode = TakeNextNode Dim built As Boolean If pn.Operator = ")" Then Exit Else If pn.Operator = "(" Then pn = BuildTree built = True End If If pn.NodeType = NUMBER_TYPE Or built Then If rt.IsInitialized Then rt.Right = pn Else rt = pn End If Else if pn.NodeType = OPERATOR_TYPE Then pn.Left = rt rt = pn End If Loop If rt.IsInitialized = False Then rt = pn Return rt End Sub Private Sub EvalNode (pn As ParsedNode) As Double If Error Then Return 0 If pn.NodeType = NUMBER_TYPE Then Return pn.Value Dim left As Double = EvalNode(pn.Left) Dim right As Double = EvalNode(pn.Right) Select pn.Operator Case "+" Return left + right Case "-" Return left - right Case "*" Return left * right Case "/" Return left / right Case Else Log("Syntax error: " & pn.Operator) Error = True Return "error" End Select End Sub private Sub TakeNextNode As ParsedNode If Error Then Return Null Dim pn As ParsedNode = Nodes.Get(ParseIndex) ParseIndex = ParseIndex + 1 Return pn End Sub Private Sub CreateOperatorNode(operator As String) As ParsedNode If Error Then Return Null Dim pn As ParsedNode pn.Initialize pn.NodeType = OPERATOR_TYPE pn.Operator = operator Return pn End Sub Private Sub CreateNumberNode (d As Double) As ParsedNode If Error Then Return Null Dim pn As ParsedNode pn.Initialize pn.NodeType = NUMBER_TYPE pn.Value = d Return pn End Sub
RBS
'version 2.01
#Event: Function(Name As String, Values As List) As Double
Sub Class_Globals
    Private Const NUMBER_TYPE = 1, OPERATOR_TYPE = 2 As Int
    Type ParsedNode (Operator As String, Left As ParsedNode, Right As ParsedNode, _
        NodeType As Int, Value As Double)
    Type OrderData (Index As Int, Level As Int, Added As Int)
    Private root As ParsedNode
    Private ParseIndex As Int
    Private Nodes As List
    Private OperatorLevel As Map
    Public Error As Boolean
    Private mCallback As Object
    Private mEventName As String
End Sub
Public Sub Initialize (Callback As Object, EventName As String)
    
    mCallback = Callback
    mEventName = EventName
    OperatorLevel = CreateMap("+": 1, "-": 1, "*":2, "/": 2)
    
End Sub
Public Sub Eval(Expression As String) As Object
    
    Dim d As Double
    
    Error = False
    Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(")
    d = EvalHelper(Expression)
    
    If Error Then
        Return "error"
    Else
        Return d
    End If
    
End Sub
Private Sub PrepareExpression(expr As String) As String
    
    If Error Then Return "error"
    
    Dim m As Matcher = Regex.Matcher("(\w*)\(", expr)
    Dim sb As StringBuilder
    Dim lastMatchEnd As Int = 0
    
    sb.Initialize
    
    Do While m.Find
        Dim currentStart As Int = m.GetStart(0)
        If currentStart < lastMatchEnd Then Continue
        sb.Append(expr.SubString2(lastMatchEnd, currentStart))
        Dim functionCall As Boolean
        Dim args As List
        If m.Match.Length > 1 Then
            args.Initialize
            functionCall = True
        End If
        Dim level As Int
        Dim start As Int = m.GetEnd(0)
        For i = start To expr.Length - 1
            If expr.CharAt(i) = "(" Then
                level = level + 1
            Else if expr.CharAt(i) = "," And level = 0 Then
                args.Add(CalcSubExpression(expr.SubString2(start, i)))
                start = i + 1
            Else if expr.CharAt(i) = ")" Then
                level = level - 1
                If level = -1 Then
                    Dim d As Double = CalcSubExpression(expr.SubString2(start, i))
                    If functionCall Then
                        args.Add(d)
                        Try
                            d = CallSub3(mCallback, mEventName & "_Function", m.Match.SubString2(0, m.Match.Length - 1), args)
                        Catch
                            Error = True
                            Return "error"
                        End Try
                    End If
                    sb.Append(NumberFormat2(d, 1, 15, 0, False))
                    lastMatchEnd = i + 1
                    Exit
                End If
            End If
        Next
    Loop
    
    If lastMatchEnd < expr.Length Then sb.Append(expr.SubString(lastMatchEnd))
    Return sb.ToString
    
End Sub
Private Sub CalcSubExpression (expr As String) As Double
    
    If Error Then Return 0
    
    Dim be As B4XEval
    be.Initialize (mCallback, mEventName)
    Dim d As Double = be.EvalHelper(expr)
    
    If be.Error Then
        Error = True
        Return 0
    End If
    
    Return d
    
End Sub
'only evaluates numbers and operators. No functions or parenthesis here.
Private Sub EvalHelper (expr As String) As Double
    
    If Error Then Return 0
    
    ParseIndex = 0
    Dim root As ParsedNode
    root.Initialize
    expr = PrepareExpression(expr)
    
    If Error Then Return 0
    
    Dim m As Matcher = Regex.Matcher("[\.\d]+", expr)
    Nodes.Initialize
    Dim lastIndex As Int = 0
    Dim currentOrderData As OrderData
    
    currentOrderData.Initialize
    Nodes.Add(CreateOperatorNode("("))
    
    Do While m.Find
        Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0))
        Dim rawNumber As String = m.Match
        If Operator.EndsWith("-") Then
            Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1)
            If lastNode.Operator = "(" Or Operator.Length > 1 Then
                'handle negative numbers: (-2 + 1, 1/-2
                Operator = Operator.SubString2(0, Operator.Length - 1)
                rawNumber = "-" & rawNumber
            End If
        End If
        lastIndex = m.GetEnd(0)
        If Operator.Length > 0 Then
            Dim level As Int = OperatorLevel.Get(Operator)
            If currentOrderData.Level > 0 Then
                If currentOrderData.Level < level Then
                    Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("("))
                    currentOrderData.Added = currentOrderData.Added + 1
                Else if currentOrderData.Level > level Then
                    If currentOrderData.Added > 0 Then
                        Nodes.Add(CreateOperatorNode(")"))
                        currentOrderData.Added = currentOrderData.Added - 1
                    End If
                End If
            End If
            currentOrderData.Level = level
            currentOrderData.Index = Nodes.Size + 1
            Nodes.Add(CreateOperatorNode(Operator))
        End If
        Dim d As Double = rawNumber
        Nodes.Add(CreateNumberNode(d))
    Loop
    
    For i = 1 To currentOrderData.Added
        Nodes.Add(CreateOperatorNode(")"))
    Next
    
    Nodes.Add(CreateOperatorNode(")"))
    root = BuildTree
    
    Return EvalNode(root)
    
End Sub
private Sub BuildTree As ParsedNode
    
    If Error Then Return Null
    
    Dim rt As ParsedNode
    
    Do While ParseIndex < Nodes.Size
        Dim pn As ParsedNode = TakeNextNode
        Dim built As Boolean
        If pn.Operator = ")" Then 
            Exit
        Else If pn.Operator = "(" Then 
            pn = BuildTree
            built = True
        End If
        If pn.NodeType = NUMBER_TYPE Or built Then
            If rt.IsInitialized Then
                rt.Right = pn
            Else
                rt = pn
            End If
        Else if pn.NodeType = OPERATOR_TYPE Then
            pn.Left = rt
            rt = pn
        End If
    Loop
    
    If rt.IsInitialized = False Then rt = pn
    
    Return rt
    
End Sub
Private Sub EvalNode (pn As ParsedNode) As Double
    
    If Error Then Return 0
    
    If pn.IsInitialized = False Then
        Error = True
        Return 0
    End If
    
    If pn.Operator = Null Then
        Error = True
        Return 0
    End If
    
    Try
        If pn.NodeType = NUMBER_TYPE Then Return pn.Value
    Catch
        Error = True
        Return 0
    End Try
    
    Dim left As Double = EvalNode(pn.Left)
    Dim right As Double = EvalNode(pn.Right)
    
    Select pn.Operator
        Case "+"
            Return left + right
        Case "-"
            Return left - right
        Case "*"
            Return left * right
        Case "/"
            Return left / right
        Case Else
            Log("Syntax error: " & pn.Operator)
            Error = True
            Return 0
    End Select
    
End Sub
private Sub TakeNextNode As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode = Nodes.Get(ParseIndex)
    ParseIndex = ParseIndex + 1
    
    Return pn
    
End Sub
Private Sub CreateOperatorNode(operator As String) As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode
    pn.Initialize
    pn.NodeType = OPERATOR_TYPE
    pn.Operator = operator
    
    Return pn
    
End Sub
Private Sub CreateNumberNode (d As Double) As ParsedNode
    
    If Error Then Return Null
    
    Dim pn As ParsedNode
    
    pn.Initialize
    pn.NodeType = NUMBER_TYPE
    pn.Value = d
    
    Return pn
    
End Sub
	Not sure why I got the unhandled errors, but simply altering the Eval Sub to thisAdded some further error handling and I think this may cover all now:
B4X:'version 2.01 #Event: Function(Name As String, Values As List) As Double Sub Class_Globals Private Const NUMBER_TYPE = 1, OPERATOR_TYPE = 2 As Int Type ParsedNode (Operator As String, Left As ParsedNode, Right As ParsedNode, _ NodeType As Int, Value As Double) Type OrderData (Index As Int, Level As Int, Added As Int) Private root As ParsedNode Private ParseIndex As Int Private Nodes As List Private OperatorLevel As Map Public Error As Boolean Private mCallback As Object Private mEventName As String End Sub Public Sub Initialize (Callback As Object, EventName As String) mCallback = Callback mEventName = EventName OperatorLevel = CreateMap("+": 1, "-": 1, "*":2, "/": 2) End Sub Public Sub Eval(Expression As String) As Object Dim d As Double Error = False Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(") d = EvalHelper(Expression) If Error Then Return "error" Else Return d End If End Sub Private Sub PrepareExpression(expr As String) As String If Error Then Return "error" Dim m As Matcher = Regex.Matcher("(\w*)\(", expr) Dim sb As StringBuilder Dim lastMatchEnd As Int = 0 sb.Initialize Do While m.Find Dim currentStart As Int = m.GetStart(0) If currentStart < lastMatchEnd Then Continue sb.Append(expr.SubString2(lastMatchEnd, currentStart)) Dim functionCall As Boolean Dim args As List If m.Match.Length > 1 Then args.Initialize functionCall = True End If Dim level As Int Dim start As Int = m.GetEnd(0) For i = start To expr.Length - 1 If expr.CharAt(i) = "(" Then level = level + 1 Else if expr.CharAt(i) = "," And level = 0 Then args.Add(CalcSubExpression(expr.SubString2(start, i))) start = i + 1 Else if expr.CharAt(i) = ")" Then level = level - 1 If level = -1 Then Dim d As Double = CalcSubExpression(expr.SubString2(start, i)) If functionCall Then args.Add(d) Try d = CallSub3(mCallback, mEventName & "_Function", m.Match.SubString2(0, m.Match.Length - 1), args) Catch Error = True Return "error" End Try End If sb.Append(NumberFormat2(d, 1, 15, 0, False)) lastMatchEnd = i + 1 Exit End If End If Next Loop If lastMatchEnd < expr.Length Then sb.Append(expr.SubString(lastMatchEnd)) Return sb.ToString End Sub Private Sub CalcSubExpression (expr As String) As Double If Error Then Return 0 Dim be As B4XEval be.Initialize (mCallback, mEventName) Dim d As Double = be.EvalHelper(expr) If be.Error Then Error = True Return 0 End If Return d End Sub 'only evaluates numbers and operators. No functions or parenthesis here. Private Sub EvalHelper (expr As String) As Double If Error Then Return 0 ParseIndex = 0 Dim root As ParsedNode root.Initialize expr = PrepareExpression(expr) If Error Then Return 0 Dim m As Matcher = Regex.Matcher("[\.\d]+", expr) Nodes.Initialize Dim lastIndex As Int = 0 Dim currentOrderData As OrderData currentOrderData.Initialize Nodes.Add(CreateOperatorNode("(")) Do While m.Find Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0)) Dim rawNumber As String = m.Match If Operator.EndsWith("-") Then Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1) If lastNode.Operator = "(" Or Operator.Length > 1 Then 'handle negative numbers: (-2 + 1, 1/-2 Operator = Operator.SubString2(0, Operator.Length - 1) rawNumber = "-" & rawNumber End If End If lastIndex = m.GetEnd(0) If Operator.Length > 0 Then Dim level As Int = OperatorLevel.Get(Operator) If currentOrderData.Level > 0 Then If currentOrderData.Level < level Then Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("(")) currentOrderData.Added = currentOrderData.Added + 1 Else if currentOrderData.Level > level Then If currentOrderData.Added > 0 Then Nodes.Add(CreateOperatorNode(")")) currentOrderData.Added = currentOrderData.Added - 1 End If End If End If currentOrderData.Level = level currentOrderData.Index = Nodes.Size + 1 Nodes.Add(CreateOperatorNode(Operator)) End If Dim d As Double = rawNumber Nodes.Add(CreateNumberNode(d)) Loop For i = 1 To currentOrderData.Added Nodes.Add(CreateOperatorNode(")")) Next Nodes.Add(CreateOperatorNode(")")) root = BuildTree Return EvalNode(root) End Sub private Sub BuildTree As ParsedNode If Error Then Return Null Dim rt As ParsedNode Do While ParseIndex < Nodes.Size Dim pn As ParsedNode = TakeNextNode Dim built As Boolean If pn.Operator = ")" Then Exit Else If pn.Operator = "(" Then pn = BuildTree built = True End If If pn.NodeType = NUMBER_TYPE Or built Then If rt.IsInitialized Then rt.Right = pn Else rt = pn End If Else if pn.NodeType = OPERATOR_TYPE Then pn.Left = rt rt = pn End If Loop If rt.IsInitialized = False Then rt = pn Return rt End Sub Private Sub EvalNode (pn As ParsedNode) As Double If Error Then Return 0 If pn.IsInitialized = False Then Error = True Return 0 End If If pn.Operator = Null Then Error = True Return 0 End If Try If pn.NodeType = NUMBER_TYPE Then Return pn.Value Catch Error = True Return 0 End Try Dim left As Double = EvalNode(pn.Left) Dim right As Double = EvalNode(pn.Right) Select pn.Operator Case "+" Return left + right Case "-" Return left - right Case "*" Return left * right Case "/" Return left / right Case Else Log("Syntax error: " & pn.Operator) Error = True Return 0 End Select End Sub private Sub TakeNextNode As ParsedNode If Error Then Return Null Dim pn As ParsedNode = Nodes.Get(ParseIndex) ParseIndex = ParseIndex + 1 Return pn End Sub Private Sub CreateOperatorNode(operator As String) As ParsedNode If Error Then Return Null Dim pn As ParsedNode pn.Initialize pn.NodeType = OPERATOR_TYPE pn.Operator = operator Return pn End Sub Private Sub CreateNumberNode (d As Double) As ParsedNode If Error Then Return Null Dim pn As ParsedNode pn.Initialize pn.NodeType = NUMBER_TYPE pn.Value = d Return pn End Sub
RBS
'    Type EvalReturn(dValue As Double, _
'                    bError As Boolean)
Public Sub Eval(Expression As String) As EvalReturn
    
    Dim d As Double
    Dim tET As EvalReturn
    
    Error = False
    Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(")
    d = EvalHelper(Expression)
    
    tET.dValue = d 'will be 0 if error, could return something like 123456 instead to indicate error and avoid the type return
    tET.bError = Error
    
    Return tET
    
End Sub
	There needs to one more change for in case the expression has no numbers, brackets or operators:Not sure why I got the unhandled errors, but simply altering the Eval Sub to this
(and nil else to be altered from Erel's original code) will sort it all out:
B4X:' Type EvalReturn(dValue As Double, _ ' bError As Boolean) Public Sub Eval(Expression As String) As EvalReturn Dim d As Double Dim tET As EvalReturn Error = False Expression = Expression.Replace(" ", "").ToLowerCase.Replace("-(", "-1*(") d = EvalHelper(Expression) tET.dValue = d 'will be 0 if error, could return something like 123456 instead to indicate error and avoid the type return tET.bError = Error Return tET End Sub
RBS
Private Sub EvalNode (pn As ParsedNode) As Double
    
    If pn.IsInitialized = False Then
        Error = True
        Return 0
    End If
    
    If pn.NodeType = NUMBER_TYPE Then Return pn.Value
    Dim left As Double = EvalNode(pn.Left)
    Dim right As Double = EvalNode(pn.Right)
    Select pn.Operator
        Case "+"
            Return left + right
        Case "-"
            Return left - right
        Case "*"
            Return left * right
        Case "/"
            Return left / right
        Case Else
            Log("Syntax error: " & pn.Operator)
            Error = True
            Return 0
    End Select
    
End Sub
	One more alteration needed for in case the provided expression contained only dots:There needs to one more change for in case the expression has no numbers, brackets or operators:
B4X:Private Sub EvalNode (pn As ParsedNode) As Double If pn.IsInitialized = False Then Error = True Return 0 End If If pn.NodeType = NUMBER_TYPE Then Return pn.Value Dim left As Double = EvalNode(pn.Left) Dim right As Double = EvalNode(pn.Right) Select pn.Operator Case "+" Return left + right Case "-" Return left - right Case "*" Return left * right Case "/" Return left / right Case Else Log("Syntax error: " & pn.Operator) Error = True Return 0 End Select End Sub
RBS
'only evaluates numbers and operators. No functions or parenthesis here.
Private Sub EvalHelper (expr As String) As Double
    
    'Log("Expr: " & expr)
    ParseIndex = 0
    Dim root As ParsedNode
    root.Initialize
    expr = PrepareExpression(expr)
    If Error Then Return 0
    Dim m As Matcher = Regex.Matcher("[\.\d]+", expr)
    Nodes.Initialize
    Dim lastIndex As Int = 0
    Dim currentOrderData As OrderData
    currentOrderData.Initialize
    Nodes.Add(CreateOperatorNode("("))
    
    Do While m.Find
        Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0))
        Dim rawNumber As String = m.Match
        If Operator.EndsWith("-") Then
            Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1)
            If lastNode.Operator = "(" Or Operator.Length > 1 Then
                'handle negative numbers: (-2 + 1, 1/-2
                Operator = Operator.SubString2(0, Operator.Length - 1)
                rawNumber = "-" & rawNumber
            End If
        End If
        lastIndex = m.GetEnd(0)
        
        If Operator.Length > 0 Then
            Dim level As Int = OperatorLevel.Get(Operator)
            If currentOrderData.Level > 0 Then
                If currentOrderData.Level < level Then
                    Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("("))
                    currentOrderData.Added = currentOrderData.Added + 1
                Else if currentOrderData.Level > level Then
                    If currentOrderData.Added > 0 Then
                        Nodes.Add(CreateOperatorNode(")"))
                        currentOrderData.Added = currentOrderData.Added - 1
                    End If
                End If
            End If
            currentOrderData.Level = level
            currentOrderData.Index = Nodes.Size + 1
            Nodes.Add(CreateOperatorNode(Operator))
        End If
        
        'Log("rawNumber: " & rawNumber)
        
        'otherwise there will be an unhandled error if provided expression has dots only
        If StringHasCharXOnly(rawNumber, Asc(".")) Then
            Error = True
            Return 0
        End If
        
        Dim d As Double = rawNumber
        Nodes.Add(CreateNumberNode(d))
    Loop
    
    For i = 1 To currentOrderData.Added
        Nodes.Add(CreateOperatorNode(")"))
    Next
    Nodes.Add(CreateOperatorNode(")"))
    root = BuildTree
    
    Return EvalNode(root)
    
End Sub
Sub StringHasCharXOnly(strString As String, iByte As Int) As Boolean
    
    Dim i As Int
    Dim arrBytes() As Byte
    arrBytes = strString.GetBytes("UTF8")
    For i = 0 To arrBytes.Length - 1
        If arrBytes(i) <> iByte Then
            Return False
        End If
    Next
    
    Return True
    
End Sub
	Found some other expression strings that would cause an unhandled error and fixed these as well.One more alteration needed for in case the provided expression contained only dots:
B4X:'only evaluates numbers and operators. No functions or parenthesis here. Private Sub EvalHelper (expr As String) As Double 'Log("Expr: " & expr) ParseIndex = 0 Dim root As ParsedNode root.Initialize expr = PrepareExpression(expr) If Error Then Return 0 Dim m As Matcher = Regex.Matcher("[\.\d]+", expr) Nodes.Initialize Dim lastIndex As Int = 0 Dim currentOrderData As OrderData currentOrderData.Initialize Nodes.Add(CreateOperatorNode("(")) Do While m.Find Dim Operator As String = expr.SubString2(lastIndex, m.GetStart(0)) Dim rawNumber As String = m.Match If Operator.EndsWith("-") Then Dim lastNode As ParsedNode = Nodes.Get(Nodes.Size - 1) If lastNode.Operator = "(" Or Operator.Length > 1 Then 'handle negative numbers: (-2 + 1, 1/-2 Operator = Operator.SubString2(0, Operator.Length - 1) rawNumber = "-" & rawNumber End If End If lastIndex = m.GetEnd(0) If Operator.Length > 0 Then Dim level As Int = OperatorLevel.Get(Operator) If currentOrderData.Level > 0 Then If currentOrderData.Level < level Then Nodes.InsertAt(currentOrderData.Index, CreateOperatorNode("(")) currentOrderData.Added = currentOrderData.Added + 1 Else if currentOrderData.Level > level Then If currentOrderData.Added > 0 Then Nodes.Add(CreateOperatorNode(")")) currentOrderData.Added = currentOrderData.Added - 1 End If End If End If currentOrderData.Level = level currentOrderData.Index = Nodes.Size + 1 Nodes.Add(CreateOperatorNode(Operator)) End If 'Log("rawNumber: " & rawNumber) 'otherwise there will be an unhandled error if provided expression has dots only If StringHasCharXOnly(rawNumber, Asc(".")) Then Error = True Return 0 End If Dim d As Double = rawNumber Nodes.Add(CreateNumberNode(d)) Loop For i = 1 To currentOrderData.Added Nodes.Add(CreateOperatorNode(")")) Next Nodes.Add(CreateOperatorNode(")")) root = BuildTree Return EvalNode(root) End Sub Sub StringHasCharXOnly(strString As String, iByte As Int) As Boolean Dim i As Int Dim arrBytes() As Byte arrBytes = strString.GetBytes("UTF8") For i = 0 To arrBytes.Length - 1 If arrBytes(i) <> iByte Then Return False End If Next Return True End Sub
RBS
OK, will do.It might be useful to post this in the Libraries & Classes forum, with a usage example perhaps (given that your version is returning a custom type rather than a Double)
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?