Parser - Anfang ist getan wie weiter machen?

  • VB.NET

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von faxe1008.

    Parser - Anfang ist getan wie weiter machen?

    Hi,

    ich habe vor langer Zeit mal an einem Parser rumgebaut und das Projekt nun wieder herausgekramt. So weit hab ich ihn nun:
    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Public Class Parser
    2. Public Token_List As New List(Of String)
    3. Private Function NewSplit(ByVal sen As String, ByVal splitter As Char()) As List(Of String)
    4. Dim final_list As New List(Of String)
    5. Dim last_found As Integer = 0
    6. For u = 0 To sen.Length - 1
    7. For Each Chr As Char In splitter
    8. If Char.Parse(sen(u)) = Chr Then
    9. final_list.Add(sen.Substring(last_found, u - last_found))
    10. final_list.Add(sen(u))
    11. last_found = u + 1
    12. End If
    13. Next
    14. Next
    15. If last_found < sen.Length Then
    16. final_list.Add(sen.Substring(last_found, sen.Length - last_found))
    17. End If
    18. Return final_list
    19. End Function
    20. Private Function SolveMinusProblem(ByVal liste As List(Of String)) As List(Of String)
    21. If liste(0) = "-" AndAlso IsNumeric(liste(1)) Then
    22. liste(1) = "-" + liste(1)
    23. liste.RemoveAt(0)
    24. End If
    25. For i As Integer = 1 To liste.Count - 1
    26. If i < liste.Count AndAlso i - 1 < liste.Count Then
    27. If liste(i) = "-" AndAlso IsNumeric(liste(i - 1)) = False Then
    28. liste(i + 1) = "-" + liste(i + 1)
    29. liste.RemoveAt(i)
    30. liste.Remove("")
    31. End If
    32. End If
    33. Next
    34. Return liste
    35. End Function
    36. Public Function Eval(ByVal expression As String) As Double
    37. Token_List = SolveMinusProblem(NewSplit(expression, {"^"c, "/"c, "*"c, "+"c, "-"c, "("c, ")"c}))
    38. Token_List = DeleteEmpty(Token_List)
    39. While Token_List.Count > 1
    40. While Contains(Token_List, "^", True)
    41. Dim opindex As Integer = GetIndex(Token_List, "^")
    42. Token_List(opindex) = Math.Pow(Double.Parse(Token_List(opindex - 1)), Double.Parse(Token_List(opindex + 1))).ToString
    43. Token_List.RemoveAt(opindex - 1)
    44. Token_List.RemoveAt(opindex)
    45. Token_List = DeletEmptyBrackets(Token_List)
    46. End While
    47. While Contains(Token_List, "/", True)
    48. Dim opindex As Integer = GetIndex(Token_List, "/")
    49. Token_List(opindex) = (Double.Parse(Token_List(opindex - 1)) / Double.Parse(Token_List(opindex + 1))).ToString
    50. Token_List.RemoveAt(opindex - 1)
    51. Token_List.RemoveAt(opindex)
    52. Token_List = DeletEmptyBrackets(Token_List)
    53. End While
    54. While Contains(Token_List, "*", True)
    55. Dim opindex As Integer = GetIndex(Token_List, "*")
    56. Token_List(opindex) = (Double.Parse(Token_List(opindex - 1)) * Double.Parse(Token_List(opindex + 1))).ToString
    57. Token_List.RemoveAt(opindex - 1)
    58. Token_List.RemoveAt(opindex)
    59. Token_List = DeletEmptyBrackets(Token_List)
    60. End While
    61. While Contains(Token_List, "-", True)
    62. Dim opindex As Integer = GetIndex(Token_List, "-")
    63. Token_List(opindex) = (Double.Parse(Token_List(opindex - 1)) - Double.Parse(Token_List(opindex + 1))).ToString
    64. Token_List.RemoveAt(opindex - 1)
    65. Token_List.RemoveAt(opindex)
    66. Token_List = DeletEmptyBrackets(Token_List)
    67. End While
    68. While Contains(Token_List, "+", True)
    69. Dim opindex As Integer = GetIndex(Token_List, "+")
    70. Token_List(opindex) = (Double.Parse(Token_List(opindex - 1)) + Double.Parse(Token_List(opindex + 1))).ToString
    71. Token_List.RemoveAt(opindex - 1)
    72. Token_List.RemoveAt(opindex)
    73. Token_List = DeletEmptyBrackets(Token_List)
    74. End While
    75. End While
    76. Return Double.Parse(Token_List.ElementAt(0))
    77. End Function
    78. Private Function Contains(ByVal liste As List(Of String), ByVal search As String, ByVal isoperator As Boolean) As Boolean
    79. Dim re = False
    80. For u = 0 To liste.Count - 1
    81. If liste(u) = search AndAlso isoperator AndAlso IsNumeric(liste(u - 1)) AndAlso IsNumeric(liste(u + 1)) Then re = True
    82. If liste(u) = search AndAlso isoperator = False AndAlso IsNumeric(liste(u + 1)) AndAlso liste(u + 2) = ")" Then re = True
    83. Next
    84. Return re
    85. End Function
    86. Private Function GetIndex(ByVal liste As List(Of String), ByVal search As String) As Integer
    87. For u = 0 To liste.Count - 1
    88. If search = liste(u) Then Return u
    89. Next
    90. End Function
    91. Private Function DeletEmptyBrackets(ByVal liste As List(Of String)) As List(Of String)
    92. While Contains(liste, "(", False)
    93. Dim brackopen As Integer = GetIndex(liste, "(")
    94. If IsNumeric(liste(brackopen + 1)) AndAlso liste(brackopen + 2) = ")" Then
    95. liste.RemoveAt(brackopen)
    96. liste.RemoveAt(brackopen + 1)
    97. End If
    98. End While
    99. Return liste
    100. End Function
    101. Private Function DeleteEmpty(ByVal liste As List(Of String)) As List(Of String)
    102. While liste.Contains("")
    103. liste.Remove("")
    104. End While
    105. Return liste
    106. End Function
    107. End Class


    Das funktioniert soweit auch gut - bis eben mehrere Klammern ins Spiel kommen. Hier mal das Grundgerüst:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Parser
    2. Public Token_List As New List(Of String)
    3. Private Function NewSplit(ByVal sen As String, ByVal splitter As Char()) As List(Of String)
    4. Dim final_list As New List(Of String)
    5. Dim last_found As Integer = 0
    6. For u = 0 To sen.Length - 1
    7. For Each Chr As Char In splitter
    8. If Char.Parse(sen(u)) = Chr Then
    9. final_list.Add(sen.Substring(last_found, u - last_found))
    10. final_list.Add(sen(u))
    11. last_found = u + 1
    12. End If
    13. Next
    14. Next
    15. If last_found < sen.Length Then
    16. final_list.Add(sen.Substring(last_found, sen.Length - last_found))
    17. End If
    18. Return final_list
    19. End Function
    20. Private Function SolveMinusProblem(ByVal liste As List(Of String)) As List(Of String)
    21. If liste(0) = "-" AndAlso IsNumeric(liste(1)) Then
    22. liste(1) = "-" + liste(1)
    23. liste.RemoveAt(0)
    24. End If
    25. For i As Integer = 1 To liste.Count - 1
    26. If i < liste.Count AndAlso i - 1 < liste.Count Then
    27. If liste(i) = "-" AndAlso IsNumeric(liste(i - 1)) = False Then
    28. liste(i + 1) = "-" + liste(i + 1)
    29. liste.RemoveAt(i)
    30. liste.Remove("")
    31. End If
    32. End If
    33. Next
    34. Return liste
    35. End Function
    36. Public Function Eval(ByVal expression As String) As Double
    37. Token_List = SolveMinusProblem(NewSplit(expression, {"^"c, "/"c, "*"c, "+"c, "-"c, "("c, ")"c}))
    38. Token_List = DeleteEmpty(Token_List)
    39. End Function
    40. Private Function Contains(ByVal liste As List(Of String), ByVal search As String, ByVal isoperator As Boolean) As Boolean
    41. Dim re = False
    42. For u = 0 To liste.Count - 1
    43. If liste(u) = search AndAlso isoperator AndAlso IsNumeric(liste(u - 1)) AndAlso IsNumeric(liste(u + 1)) Then re = True
    44. If liste(u) = search AndAlso isoperator = False AndAlso IsNumeric(liste(u + 1)) AndAlso liste(u + 2) = ")" Then re = True
    45. Next
    46. Return re
    47. End Function
    48. Private Function GetIndex(ByVal liste As List(Of String), ByVal search As String) As Integer
    49. For u = 0 To liste.Count - 1
    50. If search = liste(u) Then Return u
    51. Next
    52. End Function
    53. Private Function DeletEmptyBrackets(ByVal liste As List(Of String)) As List(Of String)
    54. While Contains(liste, "(", False)
    55. Dim brackopen As Integer = GetIndex(liste, "(")
    56. If IsNumeric(liste(brackopen + 1)) AndAlso liste(brackopen + 2) = ")" Then
    57. liste.RemoveAt(brackopen)
    58. liste.RemoveAt(brackopen + 1)
    59. End If
    60. End While
    61. Return liste
    62. End Function
    63. Private Function DeleteEmpty(ByVal liste As List(Of String)) As List(Of String)
    64. While liste.Contains("")
    65. liste.Remove("")
    66. End While
    67. Return liste
    68. End Function
    69. End Class

    NewSplit befüllt die Tokenlist mit den einzelnen Zahlen, Operatoren. SolveMinusProblem schiebt zum Beispiel bei 12*-1 das Minus und die 1 in ein Token. Durch die NewSplit-Function entstehen Leerzeichen die durch die Function DeleteEmpty gelöscht werden.

    Soviel zu dem aktuellen Stand. Wie kann ich nun weitermachen ohne allzuviel an der bisherigen Grundstruktur herumzufummeln ? Danke für eure Hilfe :thumbup:

    8-) faxe1008 8-)
    Klammern sind nur sehr schwer zu handhaben, am ehesten müsste man sowas wohl rekursiv lösen.
    Aber warum wehrst du dich denn gegen ShuntingYard? Ist richtig einfach zu implementieren (die Vorgehensweise ist exakt spezifiziert), lässt Klammern verschwinden und die Auswertung besteht dann nur noch darin, nen Stack zu durchlaufen. Da du die Tokens ja schon gefiltert hast, ist der Rest dann nur noch Firlefanz.
    @Artentus
    Offengestanden, weil ich nicht wirklich verstanden habe wie die Function Infix zu Postfix schreiben soll.... Außerdem hab ich mir die Frage gestellt, ob man zwangsläufig eine extra Klasse Token (=> List(of Token))entwerfen muss, oder ob man das auch mit einer List(of String) regeln könnte.

    8-) faxe1008 8-)

    faxe1008 schrieb:

    ob man das auch mit einer List(of String) regeln könnte
    Kannst du auch - wenn du einen großen SelectCase-Block am Ende haben willst.

    Der Algo ist eigentlich gar nicht so kompliziert. Eingabe ist die Liste der Tokens in Infix-Notation, Ausgabe soll eine Liste der Tokens in Postfix-Notation sein.
    Vereinfacht passiert dann das:
    Alle Tokens werden der Reihe nach durchgegangen. Eine Zahl wird sofort zur Ausgabe hinzugefügt, denn an der Reihenfolge, in der die Zahlen in der Postfix-Notation im Vergleich zur Infix-Notation auftauchen, ändert sich nichts, steht in der Eingabe a vor b, dann muss auch in der Ausgabe a vor b stehen.
    Ist der Token ein Operator, passiert die Magie. Zunächst wird nachgesehen, ob auf dem Operatorstack ein Operator vorhanden ist. Ist einer vorhanden und besitzt dieser eine höhere Priorität, als der aktuelle Operator (Assoziativität wird hier auch noch berücksichtigt), so wird dieser vom Operatorstack entfernt und zur Ausgabe hinzugefügt, denn je höher die Priorität, desto früher muss der Operator natürlich ausgeführt werden. Dieser Schritt wird solange wiederholt, bis die Bedingung nicht mehr erfüllt ist. Dann wird der aktuelle Operator selbst auf den Stack geschoben und landet damit vor allen verbleibenden Operatoren mit niedrigerer Priorität, so wies sein soll.
    Ganz am Ende des Algorithmuses müssen noch alle auf dem Stack verbleibenden Operatoren zur Ausgabe hinzugefügt werden, was ja logisch ist, weil sie sonst einfach unterschlagen werden würden.
    Wenn Funktionen und Klammern dazukommen, wirds nochmal etwas komplizierter, aber ich hoffe, ich hab das einigermaßen verständlich rübergebracht.
    Für die Priorität würde sich ein Dictionary(String, Integer) anbieten oder?
    Jetzt noch zum konvertieren. Nehmen wir mal an der Term 2+18/3-7 vor. Dann müsste ich deiner Anweisung nach die Liste so editieren:
    2 ; + ; 18 ; / ; 3 ; - ; 7
    => 2 ; 18 ; + ; 3 ; / ; 7; -
    => 2 ; 18 ; / ; 3 ; + ; 7 ; -

    oder nicht? Falls das passt wie wäre die Vorgehensweise von Schritt 2 zu 3 ?

    8-) faxe1008 8-)
    Dieser Schritt zwei wird mit ShuntingYard niemals erreicht, es geht gleich von eins nach drei, ist nämlich einfacher. Der Dritte Schritt ist übrigens nicht korrekt.

    Hier mal Schritt für Schritt:
    Spoiler anzeigen
    1. Das Token ist 2, also eine Zahl, und kommt direkt zur Ausgabe.
    Ausgabe: 2
    Operatorstack: Leer

    2. Das Token ist +, ein Operator. Da der Operatorstack leer ist müssen keine zusätzlichen Aktionen getätigt werden, der Token kommt direkt auf den Stack.
    Ausgabe: 2
    Operatorstack: +

    3. Die Zahl 18 kommt wieder zur Ausgabe.
    Ausgabe: 2 18
    Operatorstack: +

    4. Ein Operator, und diesmal ist der Stack ist nicht leer. Die Präzedenz des obersten Operators auf dem Stack (+) ist aber niedriger als die von /, deswegen werden wieder keine zusätzlichen Aktionen ausgeführt.
    Ausgabe: 2 18
    Operatorstack: + /

    5. Zahl kommt auf den Stack.
    Ausgabe: 2 18 3
    Operatorstack: + /

    6. Der oberste Operator auf dem Stack, Division, wir höher priorisiert als der aktuelle Operator Subtraktion und wird deswegen zur Ausgabe geschoben.
    Ausgabe: 2 18 3 /
    Operatorstack: +
    Jetzt liegt der Additionsoperator auf dem Stack. Dieser hat die selbe Präzedenz wie Subtraktion, ist jedoch linksassoziativ und wird deswegen auch zur Ausgabe verschoben.
    Ausgabe: 2 18 3 / +
    Operatorstack: Leer
    Nun muss der aktuelle Operator noch auf den Stack.
    Ausgabe: 2 18 3 / +
    Operatorstack: -

    7. Noch eine Zahl, die auf die Ausgabe kommt.
    Ausgabe: 2 18 3 / + 7
    Operatorstack: -

    8. Es befinden sich keine Tokens mehr in der Eingabeliste, wir leeren also als letze Handlung den Operatorstack vollständig in die Ausgabe.
    Ausgabe: 2 18 3 / + 7 -


    Jetzt liegt eine korrekte Postfix-Notation der Tokens vor. Gelöst wird jetzt wie folgt (es wird ein Augabestack benötigt, nicht mit dem Operatorstack verwechseln):
    Spoiler anzeigen
    1. Eine Zahl wird auf den Ausgabestack geschoben.
    Eingabeliste: 18 3 / + 7 -
    Ausgabestack: 2

    2. Wieder Zahl.
    Eingabelist: 3 / + 7 -
    Ausgabestack: 2 18

    3. Und nochmal.
    Eingabeliste: / + 7 -
    Ausgabestack: 2 18 3

    4. Jetzt haben wir einen Operator. Division ist ein binärer Operator, wir holen also zwei Werte vom Ausgabestack und führen die Division durch. Das Ergebnis der Division landet wieder auf dem Ausgabestack.
    Eingabeliste: + 7 -
    Ausgabestack: 2 6

    5. Noch ein binärer Operator. Wieder holen wir zwei Werte vom Stack, berechnen und legen das Ergebnis auf den Stack.
    Eingabeliste: 7 -
    Ausgabestack: 8

    6. Die Zahl kommt wieder auf den Stack.
    Eingabeliste: -
    Ausgabestack: 8 7

    7. Und zuletzt ein Operator, wir führen das selbe durch wie zuvor.
    Eingabeliste: Leer
    Ausgabestack: 1

    Das Ergebnis liegt jetzt als einziges Element auf dem Stack und wir können es ganz einfach auslesen.
    @Artentus
    So, habe nun mich an einer Infix zu Postfix Funktion versucht...:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Function Infix2Postfix(ByVal liste As List(Of String)) As List(Of String)
    2. Dim ausgabestack As New List(Of String)
    3. Dim operatorstack As New List(Of String)
    4. For Each token As String In liste
    5. If IsNumeric(token) Then ausgabestack.Add(token)
    6. If IsNumeric(token) = False Then operatorstack.Add(token)
    7. If operatorstack.Count - 1 > 0 AndAlso IsNumeric(token) Then
    8. If operatorstack.Count > 1 Then
    9. operatorstack = SortStackAfterPriority(operatorstack)
    10. ausgabestack.Add(operatorstack(operatorstack.Count - 1))
    11. operatorstack.RemoveAt(operatorstack.Count - 1)
    12. End If
    13. End If
    14. Next
    15. Return ausgabestack
    16. End Function
    17. Public Function SortStackAfterPriority(ByVal liste As List(Of String)) As List(Of String)
    18. Dim gabe As New List(Of String)
    19. While liste.Contains("+")
    20. gabe.Add("+")
    21. liste.Remove("+")
    22. End While
    23. While liste.Contains("-")
    24. gabe.Add("-")
    25. liste.Remove("-")
    26. End While
    27. While liste.Contains("/")
    28. gabe.Add("/")
    29. liste.Remove("/")
    30. End While
    31. While liste.Contains("*")
    32. gabe.Add("*")
    33. liste.Remove("*")
    34. End While
    35. While liste.Contains("^")
    36. gabe.Add("^")
    37. liste.Remove("^")
    38. End While
    39. Return gabe
    40. End Function


    2+18/3-7
    => 2 ; 18 ; 3 ; / ; 7 ; -

    Das Plus wird leider verschluckt.

    8-) faxe1008 8-)

    Artentus schrieb:

    Klammern sind nur sehr schwer zu handhaben
    @ faxe1008
    Du musst zu einer öffnenden Klammer die kommunizierende schließende Klammer finden.
    Dabei wird es passieren, dass Du dabei beliebig viele weitere öffnende Klammern und auch schließende Klammern findest, die musst Du dann von innen nach außen auflösen.
    Das ist mühselig, aber effizient machbar.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Artentus
    Hi,

    hab das ganze auch nochmal mit Stack versucht (läuft leider nicht):
    Spoiler anzeigen

    Quellcode

    1. Private Function Infix2Postfix(ByVal liste As List(Of String)) As List(Of String)
    2. Dim ausgabestack As New List(Of String)
    3. Dim operatorstack As New Stack(Of String)
    4. For Each token As String In liste
    5. If IsNumeric(token) Then ausgabestack.Add(token)
    6. If Not IsNumeric(token) Then
    7. operatorstack.Push(token)
    8. End If
    9. If operatorstack.Count = 2 Then
    10. ausgabestack.Add(operatorstack.Pop)
    11. End If
    12. Next
    13. If operatorstack.Count = 1 Then
    14. ausgabestack.Add(operatorstack.Pop)
    15. End If
    16. Return ausgabestack
    17. End Function


    Allerdings hänge ich ein wenig an dem Verständnis, wann aus dem Operatorstack auf den Ausgabestack geschoben wird ?(. Und wie kann ich den Opstack effizient sortieren lassen?


    8-) faxe1008 8-)
    Zu der Sache mit den Klammern:
    Das mit den Klammern hab ich so gemacht, dass ich ab dem index der ersten gefundenen Klammer zusätzlich eine Variable anzahlKlammern gemacht habe und immer wenn eine "(" klammer gefunden wurde, wird die variable hochgezählt, wenn eine ")" wird sie runtergezählt, wenn die Variable nun 0 erreicht hat hat man den Index der schliessenden Klammer und kann den Ausdruck mit substring rausholen und die Parse funktion mit dem Ausdruck rekursiv aufrufen (also so mach ich das, ka ob dir das hilft, weil ich habs ohne stack undson zeug gemacht)

    faxe1008 schrieb:

    wie kann ich den Opstack effizient sortieren lassen
    Der muss doch gar nicht sortiert werden, bei einer korrekten Umsetzung des Algorithmus ist der ja automatisch sortiert.

    Ich hab dir mal ne stark vereinfachte Version des Algos geschrieben:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public NotInheritable Class Token
    2. Private Enum TokenType
    3. Number
    4. [Operator]
    5. End Enum
    6. Private Enum Associativity
    7. Left
    8. Right
    9. End Enum
    10. Private Const NumberName As String = "<Number>"
    11. Private Shared ReadOnly OperatorList() As String
    12. Private Shared ReadOnly Actions As Dictionary(Of String, Action(Of Stack(Of Double)))
    13. Private Shared ReadOnly Priorities As Dictionary(Of String, Integer)
    14. Private Shared ReadOnly Associativities As Dictionary(Of String, Associativity)
    15. Shared Sub New()
    16. OperatorList = {"!", "+", "-", "*", "/", "^"}
    17. Actions = New Dictionary(Of String, Action(Of Stack(Of Double)))()
    18. Actions.Add("!", Sub(stack) stack.Push(-stack.Pop()))
    19. Actions.Add("+", Sub(stack) stack.Push(stack.Pop() + stack.Pop()))
    20. Actions.Add("-", Sub(stack) stack.Push(-stack.Pop() + stack.Pop()))
    21. Actions.Add("*", Sub(stack) stack.Push(stack.Pop() * stack.Pop()))
    22. Actions.Add("/", Sub(stack) stack.Push((1 / stack.Pop()) * stack.Pop()))
    23. Actions.Add("^", Sub(stack)
    24. Dim exponent = stack.Pop()
    25. stack.Push(Math.Pow(stack.Pop(), exponent))
    26. End Sub)
    27. Priorities = New Dictionary(Of String, Integer)()
    28. Priorities.Add(NumberName, Integer.MaxValue)
    29. Priorities.Add("!", 0)
    30. Priorities.Add("+", 1)
    31. Priorities.Add("-", 1)
    32. Priorities.Add("*", 2)
    33. Priorities.Add("/", 2)
    34. Priorities.Add("^", 3)
    35. Associativities = New Dictionary(Of String, Associativity)()
    36. Associativities.Add(NumberName, Associativity.Left)
    37. Associativities.Add("!", Associativity.Left)
    38. Associativities.Add("+", Associativity.Left)
    39. Associativities.Add("-", Associativity.Left)
    40. Associativities.Add("*", Associativity.Left)
    41. Associativities.Add("/", Associativity.Left)
    42. Associativities.Add("^", Associativity.Right)
    43. End Sub
    44. Private ReadOnly _name As String
    45. Private ReadOnly _type As TokenType
    46. Private ReadOnly _value As Double
    47. Public Sub New(value As String)
    48. If OperatorList.Contains(value) Then
    49. _name = value
    50. _type = TokenType.Operator
    51. _value = 0
    52. ElseIf Double.TryParse(value, _value) Then
    53. _name = NumberName
    54. _type = TokenType.Number
    55. Else
    56. Throw New ArgumentException("The specified value is not a valid token.")
    57. End If
    58. End Sub
    59. Public Function EvaluatesBefore(other As Token) As Boolean
    60. Dim thisPriority = Priorities(Me._name)
    61. Dim otherPriority = Priorities(other._name)
    62. Return thisPriority > otherPriority OrElse (thisPriority = otherPriority AndAlso Associativities(Me._name) = Associativity.Left)
    63. End Function
    64. Public Sub Eval(stack As Stack(Of Double))
    65. Select Case _type
    66. Case TokenType.Number
    67. stack.Push(_value)
    68. Case TokenType.Operator
    69. Actions(_name).Invoke(stack)
    70. End Select
    71. End Sub
    72. Public Overrides Function ToString() As String
    73. Select Case _type
    74. Case TokenType.Number
    75. Return _value.ToString()
    76. Case TokenType.Operator
    77. Return _name
    78. Case Else
    79. Return String.Empty
    80. End Select
    81. End Function
    82. Public Shared Widening Operator CType(value As String) As Token
    83. Return New Token(value)
    84. End Operator
    85. End Class
    86. Public Shared Function ShuntingYardBasic(inputList As IEnumerable(Of Token)) As Token()
    87. Dim outputList As New List(Of Token)
    88. Dim operatorStack As New Stack(Of Token)
    89. For Each token In inputList
    90. Do While operatorStack.Count > 0 AndAlso operatorStack.Peek().EvaluatesBefore(token)
    91. outputList.Add(operatorStack.Pop())
    92. Loop
    93. operatorStack.Push(token)
    94. Next
    95. Do While operatorStack.Count > 0
    96. outputList.Add(operatorStack.Pop())
    97. Loop
    98. Return outputList.ToArray()
    99. End Function
    100. Public Shared Function EvalPostfixTokens(postfixTokens As IEnumerable(Of Token)) As Double
    101. Dim stack As New Stack(Of Double)
    102. For Each token In postfixTokens
    103. token.Eval(stack)
    104. Next
    105. Return stack.Pop()
    106. End Function

    Anwendung dann z.B. so:

    VB.NET-Quellcode

    1. Dim tokens() As Token = {"2", "+", "18", "/", "3", "-", "7"}
    2. Dim postfixTokens() As Token = ShuntingYardBasic(tokens)
    3. Dim result As Double = EvalPostfixTokens(postfixTokens)
    4. MessageBox.Show(result.ToString())

    RushDen schrieb:

    wenn eine ")" wird sie runtergezählt, wenn die Variable nun 0 erreicht hat hat man
    Glück, wenn die Syntax stimmt.
    Du musst Dich davon überzeugen, dass zu jeder öffnenden Klammer die kommunizierende schließende Klammer da ist.
    Ansonsten ist es richtig:
    Finde die erste öffnende Klammer, finde die zu dieser öffnenden die kommunizierende schließende Klammer und parse diesen Ausdruck so lange, bis alle Klammern aufgelöst sind, dann Du haben feddich. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RushDen schrieb:

    exception
    Ich habe mir angewöhnt, vom Schlecht-Fall auszugehen, da fährt man letztenendes bequemer.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Artentus
    So habe mit deinen Ideen mal an meiner Version rumgebastelt:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Parser
    2. Public Token_List As New List(Of String)
    3. #Region "preparing-functions"
    4. Private Function NewSplit(ByVal sen As String, ByVal splitter As Char()) As List(Of String)
    5. Dim final_list As New List(Of String)
    6. Dim last_found As Integer = 0
    7. For u = 0 To sen.Length - 1
    8. For Each Chr As Char In splitter
    9. If Char.Parse(sen(u)) = Chr Then
    10. final_list.Add(sen.Substring(last_found, u - last_found))
    11. final_list.Add(sen(u))
    12. last_found = u + 1
    13. End If
    14. Next
    15. Next
    16. If last_found < sen.Length Then
    17. final_list.Add(sen.Substring(last_found, sen.Length - last_found))
    18. End If
    19. Return final_list
    20. End Function
    21. Private Function SolveMinusProblem(ByVal liste As List(Of String)) As List(Of String)
    22. If liste(0) = "-" AndAlso IsNumeric(liste(1)) Then
    23. liste(1) = "-" + liste(1)
    24. liste.RemoveAt(0)
    25. End If
    26. For i As Integer = 1 To liste.Count - 1
    27. If i < liste.Count AndAlso i - 1 < liste.Count Then
    28. If liste(i) = "-" AndAlso IsNumeric(liste(i - 1)) = False Then
    29. liste(i + 1) = "-" + liste(i + 1)
    30. liste.RemoveAt(i)
    31. liste.Remove("")
    32. End If
    33. End If
    34. Next
    35. Return liste
    36. End Function
    37. Private Function DeleteEmpty(ByVal liste As List(Of String)) As List(Of String)
    38. While liste.Contains("")
    39. liste.Remove("")
    40. End While
    41. Return liste
    42. End Function
    43. #End Region
    44. Public Function Eval(ByVal expr As String) As String
    45. Token_List = SolveMinusProblem(NewSplit(expr, {"^"c, "/"c, "*"c, "+"c, "-"c, "("c, ")"c}))
    46. Token_List = DeleteEmpty(Token_List)
    47. Token_List = ShuntingYardBasic(Token_List)
    48. Dim str = String.Join(" ; ", Token_List)
    49. Return str
    50. End Function
    51. Public Function ShuntingYardBasic(inputList As List(Of String)) As List(Of String)
    52. Dim outputList As New List(Of String)
    53. Dim operatorStack As New Stack(Of String)
    54. For Each token As String In inputList
    55. If IsNumeric(token) Then
    56. outputList.Add(token)
    57. End If
    58. If operatorStack.Count = 0 AndAlso IsNumeric(token) = False Then
    59. operatorStack.Push(token)
    60. Continue For
    61. End If
    62. If operatorStack.Count > 0 AndAlso IsNumeric(token) = False Then
    63. If IsHigher(operatorStack.Peek, token) Then
    64. outputList.Add(operatorStack.Pop)
    65. operatorStack.Push(token)
    66. End If
    67. End If
    68. Next
    69. While operatorStack.Count > 0
    70. outputList.Add(operatorStack.Pop)
    71. End While
    72. Return outputList
    73. End Function
    74. Public Enum Associt
    75. Left
    76. Right
    77. End Enum
    78. Function IsHigher(ByVal older As String, ByVal newer As String) As Boolean
    79. Dim Priority As New Dictionary(Of String, Integer)
    80. Priority.Add("+", 0)
    81. Priority.Add("-", 0)
    82. Priority.Add("*", 1)
    83. Priority.Add("/", 1)
    84. Priority.Add("^", 2)
    85. Dim Associativity As New Dictionary(Of String, Associt)
    86. Associativity.Add("+", Associt.Left)
    87. Associativity.Add("-", Associt.Left)
    88. Associativity.Add("*", Associt.Left)
    89. Associativity.Add("/", Associt.Left)
    90. Associativity.Add("^", Associt.Right)
    91. Return Priority(older) > Priority(newer) OrElse (Priority(older) = Priority(newer) AndAlso Associativity(newer) = Associt.Left)
    92. End Function
    93. End Class


    Zu Testzwecken gibt Eval nen String zurück. Ausdrücke wie 2*4/4+3 parst er schon richtig :thumbsup: , allerdings kommt er bei 3+4*2 ins Schleudern und gibt

    3 ; 4 ; 2 ; +

    zurück. Es muss eine Kleinigkeit sein die hier nicht stimmt, aber ich weiß nicht was. Wäre nett wenn jemand den Fehler findet und erklärt :thumbup:

    EDIT//: Hab den Fehler gefunden :thumbup: :

    Quellcode

    1. Public Function ShuntingYardBasic(inputList As List(Of String)) As List(Of String)
    2. Dim outputList As New List(Of String)
    3. Dim operatorStack As New Stack(Of String)
    4. For Each token As String In inputList
    5. If IsNumeric(token) Then
    6. outputList.Add(token)
    7. End If
    8. If operatorStack.Count = 0 AndAlso IsNumeric(token) = False Then
    9. operatorStack.Push(token)
    10. Continue For
    11. End If
    12. If operatorStack.Count > 0 AndAlso IsNumeric(token) = False Then
    13. If IsHigher(operatorStack.Peek, token) Then
    14. outputList.Add(operatorStack.Pop)
    15. operatorStack.Push(token)
    16. Else
    17. operatorStack.Push(token)
    18. End If
    19. End If
    20. Next
    21. While operatorStack.Count > 0
    22. outputList.Add(operatorStack.Pop)
    23. End While
    24. Return outputList
    25. End Function

    8-) faxe1008 8-)

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „faxe1008“ ()