Scriptsprache

Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von nikeee13.

    @Higlav
    Eher wie TI-BASIC (habe ich ja vorher schon genannt), nur eben für PCs. Es geht mir momentan darum, zu verstehen, wie Interpreter funktionieren und gleichzeitig eine sehr einfache Skriptsprache zu erstellen.
    Hi
    was wäre der große Architekturunterschied zwischen
    push LinePointer
    ;push args
    goto 'MyFunction'

    'MyFunction':
    ;body

    ;pop args
    goto pop

    und
    Function MyFunction(firstArg, secondArg)
    Return firstArg + secondArg
    End Function
    bzw. vllt. echt funktional
    MyFunction a b = a + b

    ? Du erhöhst die Komplexität des Programms damit nicht besonders und es macht Spaß, das so zu entwickeln, besonders, weil man eben den Umweg über einen Stack und ein Array gehen kann. Im Array stehen lauter Delegaten oder entsprechende Instanzen von primitiven Opcodes, die nach-und-nach auf einen Stapel angewendet werden können. Der Stapel hat dabei Operationen, die z.B. zwei Variablen herunternehmen können (von links (2. Pop) nach rechts (1. Pop)).
    Meine erste Skriptsprache hat so gearbeitet (waren nur erste Gehversuche, daher auch ohne Tokenizer und allem - ja selbst ohne Architektur!!).

    Gruß
    ~blaze~
    @~blaze~
    Klar, so geht es auch. Jedoch geht es mir eher darum, solche Sachen wie Tokenizer und so weiter zu verstehen und, wie ich jetzt schon mehrmals gesagt habe, eine möglichst simple Skriptsprache zu erstellen, bei der es ausreicht, auch nur minimale bis gar keine Kenntnisse in dem Bereich braucht.
    Ich denke, dass es auch so für dich interessant sein könnte. Am Tokenizer musst du ja nichts machen, der ist eh total einfach zu programmieren. Syntaktische Analyse selbst muss man sich halt bisschen was überlegen, aber bei einer ansprechenden Architektur ist das auch nicht so schwer. Man muss halt nur das Pattern zuordnen, somit sind das eigentlich einfach nur mehr Pattern. Der Witz dahinter steckt eigentlich eher darin, dass du in so einem Fall auch noch lernst, wie du die zugänglichen Member referenzierst. Z.B. gilt ja eine Deklaration innerhalb einer Funktion nicht außerhalb. Die Funktionalität erhöhst du dabei außerdem noch stark, da du Rekursion ohne hässliche Patterns, wie goto einfach umsetzen kannst.

    Ich will mich da jetzt nicht weiter einmischen, aber es wäre halt praktisch und auch vom Lernaufwand her sicher nicht schlecht, wenn du sowas noch nicht gemacht hast. Viel mehr Arbeit wäre es auch nicht.
    Um noch was konstruktives zu liefern: Ich würde If, While/Until bzw. Do-Loop als gemeinsames ConditionStatement mit einer Looped-Eigenschaft modellieren, das die Condition verzögert abfragen kann, sofern es eine gibt für Do-Loop-<condition>.

    Gruß
    ~blaze~
    Danke für den Tipp, jedoch glaube ich nicht, dass ich das in diesem Projekt umsetzen werde, einfach weil es in meinen Augen für ganz einfache Programme einfach keinen Sinn macht. Jedenfalls glaube ich kaum, dass irgendjemand dieses Feature benutzen würde, außerdem kann man es dann sicher auch im Nachhinein ausbauen.
    Wenn du willst, kann ich bei Gelegenheit mal einen Blick auf die Architektur werfen, wenn du nicht sicher bist, ob sie dafür tauglich ist. Bei meinem derzeitigen Projekt habe ich auch die Patterns abstrakt modelliert (und dabei brav overengineert). Dadurch ist da die syntaktische Analyse sehr einfach durch das Hinzufügen von Pattern zur Hierarchie möglich.

    Gruß
    ~blaze~
    Du könntest mal nen Blick auf bestehende Projekte werfen und dir dort Ideen holen.

    Hier mal ein guter, aber einfacher, Tokenizer, welcher auch leicht zu verändern ist: codeproject.com/Articles/7664/StringTokenizer

    Und hier ein paar OS Projekte:
    The Microsoft NET version of the paxScript scripting engine (paxScript.NET) includes interpreters of C# and VB.NET languages.

    eco148-88394.innterhost.net/paxscriptnet/

    Dynamic Expresso is an expression interpreter for simple C# statements. Dynamic Expresso embeds its own parsing logic, and really interprets C# statements by converting it to .NET delegates that can be invoked as any standard delegate. It doesn't generate assembly but it creates dynamic expressions/delegates on the fly.

    github.com/davideicardi/DynamicExpresso/

    CS-Script is a CLR (Common Language Runtime) based scripting system which uses ECMA-compliant C# as a programming language. CS-Script currently targets Microsoft implementation of CLR (.NET 2.0/3.0/3.5/4.0/4.5) with full support on Mono.

    csscript.net/index.html

    Jedes dieser Projekte verwendet entweder C# oder (wie paxScript) VB.NET als ScriptSprache. Von denen könntest du dir abschauen, wie die das Parsen der Texte lösen.
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D
    Ich habe mich mal daran versucht, einen kleinen Tokenizer zu erstellen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Tokenizer
    2. Private LineSeperators As String() = {ControlChars.Cr, ControlChars.Lf}
    3. Private TokenSeperators As Char() = {" "c}
    4. Private StringIndicator As Char = """"c
    5. Private CommentIndicator As Char = "'"c
    6. Public Function Tokenize(code As String) As List(Of Token())
    7. Debug.WriteLine("----------------------------------------------------------------------")
    8. 'Den Code in die einzelnen Zeilen zerteilen
    9. Dim Lines As IEnumerable(Of String) = code.Split(LineSeperators, StringSplitOptions.RemoveEmptyEntries)
    10. Dim LineEnumerator As IEnumerator = Lines.GetEnumerator
    11. 'Alle Zeilen durchgehen
    12. Dim TokenList As New List(Of Token())
    13. For LineIndex As Integer = 0 To Lines.Count - 1
    14. Dim Line As String = Lines(LineIndex)
    15. Dim LineNumber = LineIndex + 1
    16. Debug.WriteLine(String.Format("Analyzing Line {0}: '{1}'", LineNumber, Line))
    17. 'Hier kommen die Tokens für diese Zeile rein
    18. Dim Tokens As New List(Of Token)
    19. Dim IsString As Boolean = False
    20. Dim IsComment As Boolean = False
    21. Dim Buffer As New List(Of Char)
    22. For Index As Integer = 0 To Line.Length - 1
    23. 'Der aktuelle Char
    24. Dim CharAtIndex As Char = Line(Index)
    25. Select Case True
    26. Case IsComment
    27. 'Nichts machen
    28. Buffer.Add(CharAtIndex)
    29. Case CharAtIndex = StringIndicator 'String fängt an oder hört auf
    30. If IsString Then
    31. 'Zeichen in den Buffer hinzufügen
    32. Buffer.Add(CharAtIndex)
    33. 'Buffer in Token speichern und abspeichern
    34. Tokens.Add(New Token(String.Join("", Buffer.Select(Function(c) c.ToString).ToArray)))
    35. 'Buffer leeren
    36. Buffer.Clear()
    37. Else
    38. 'Nichts machen
    39. Buffer.Add(CharAtIndex)
    40. IsString = True
    41. End If
    42. Case CharAtIndex = CommentIndicator 'Kommentar fängt an (außer er ist in einem String)
    43. 'Nur was machen, wenn sich das Kommentarzeichen nicht
    44. If Not IsString Then
    45. 'Speichern, dass ein Kommentar aktiv ist
    46. IsComment = True
    47. 'Zeichen dem Buffer hinzufügen
    48. Buffer.Add(CharAtIndex)
    49. Else
    50. 'Nichts machen
    51. Buffer.Add(CharAtIndex)
    52. End If
    53. Case TokenSeperators.Any(Function(c) c = CharAtIndex) 'Es handelt sich um einen Tokentrenner
    54. If (Not IsString) AndAlso (Not IsComment) Then
    55. 'Buffer in Token speichern und abspeichern
    56. If Buffer.Count > 0 Then
    57. Tokens.Add(New Token(String.Join("", Buffer.Select(Function(c) c.ToString).ToArray)))
    58. 'Buffer leeren
    59. Buffer.Clear()
    60. End If
    61. Else
    62. 'Nichts machen
    63. Buffer.Add(CharAtIndex)
    64. End If
    65. Case CharAtIndex = "("c 'Klammer auf
    66. If Buffer.Count > 0 Then
    67. 'Zeichen dem Buffer hinzufügen
    68. Buffer.Add(CharAtIndex)
    69. 'Buffer in Token speichern und abspeichern
    70. Tokens.Add(New Token(String.Join("", Buffer.Select(Function(c) c.ToString).ToArray)))
    71. 'Buffer leeren
    72. Buffer.Clear()
    73. Else
    74. 'Klammer in eigenem Token abspeichern
    75. Tokens.Add(New Token(CharAtIndex.ToString))
    76. End If
    77. Case CharAtIndex = ")"c 'Klammer zu
    78. 'Buffer in Token speichern und abspeichern
    79. If Buffer.Count > 0 Then
    80. Tokens.Add(New Token(String.Join("", Buffer.Select(Function(c) c.ToString).ToArray)))
    81. 'Buffer leeren
    82. Buffer.Clear()
    83. End If
    84. 'Klammer in Token abspeichern
    85. Tokens.Add(New Token(CharAtIndex.ToString))
    86. Case Else
    87. 'nichts spezielles machen
    88. Buffer.Add(CharAtIndex)
    89. End Select
    90. Next
    91. 'Letzten Token abschließen
    92. If Buffer.Count > 0 Then
    93. Tokens.Add(New Token(String.Join("", Buffer.Select(Function(c) c.ToString).ToArray)))
    94. 'Buffer leeren
    95. Buffer.Clear()
    96. End If
    97. Debug.WriteLine(String.Format(" Splitted Line in Tokens: '{0}'", String.Join("' '", Tokens.Select(Function(t) t.ToString).ToArray)))
    98. 'Tokens abspeichern
    99. TokenList.Add(Tokens.ToArray)
    100. Next
    101. 'Alle Tokens zurückgeben
    102. Return TokenList
    103. End Function
    104. End Class

    VB.NET-Quellcode

    1. Public Class Token
    2. Public Property Code As String = ""
    3. Public Sub New(code As String)
    4. Me.Code = code
    5. End Sub
    6. Public Overrides Function ToString() As String
    7. Return Me.Code
    8. End Function
    9. End Class


    Hier mal ein kleiner Beispielcode:

    Quellcode

    1. X <- (sin(cos(19)) + 8) / 10 'Variable zuweisen
    2. Y <- Input("Hallo Welt, bitte geben sie eine Zahl ein:")
    3. 'Jetzt das noch ausgeben
    4. Output(Y)

    und der dazugehörige Output des Tokenizers (nur das aus den Debugmeldungen, das Verarbeiten kommt noch ;):

    Quellcode

    1. Analyzing Line 1: 'X <- (sin(cos(19)) + 8) / 10 'Variable zuweisen'
    2. Splitted Line in Tokens: 'X' '<-' '(' 'sin(' 'cos(' '19' ')' ')' '+' '8' ')' '/' '10' ''Variable zuweisen'
    3. Analyzing Line 2: 'Y <- Input("Hallo Welt, bitte geben sie eine Zahl ein:")'
    4. Splitted Line in Tokens: 'Y' '<-' 'Input(' '"Hallo Welt, bitte geben sie eine Zahl ein:"' ')'
    5. Analyzing Line 3: ''Jetzt das noch ausgeben'
    6. Splitted Line in Tokens: ''Jetzt das noch ausgeben'
    7. Analyzing Line 4: 'Output(Y)'
    8. Splitted Line in Tokens: 'Output(' 'Y' ')'

    Was denkt ihr? Hab ich da jetzt irgendwas komplett falsch gemacht oder habt ihr noch Verbesserungsvorschläge?

    PS: Der Code ist noch etwas unstrukturiert und alles, sollte aber einigermaßen verständlich sein.
    Ein Tokenizer wird normalerweise mit einer StateMachine implementiert. Du gehst schon in die richtige Richtung, indem du die Zeichen einzeln verarbeitest. Wenn deine Sprache aber komplizierter wird, bekommst du bei deiner Variante Probleme mit zu vielen IsWhatever-Variablen, weil du dir beim Speichern des Zustands nicht die Objektorientierung zunutze machst.

    Erstell dir eine Basisklasse "Tokenizer" und leite davon alle Konstrukte ab, die du erkennen willst. Ein Klammerausdruck besteht z.B. aus der Subklasse RoundParen, die von "( EXPRESSION )" die öffnende und schließende Klammer erkennt. EXPRESSION selbst wird von einer weiteren Subklasse erkannt. Jede Subklasse überschreibt dabei immer dieselbe Methode "Overridable Sub ParseNext(currentChar As Char, nextChar As Char)" oder ähnlich. Das Token kommt dann in einem globalen Event in der Basisklasse, z.B. wenn RoundParen eine öffnende oder schließende Klammer erkannt hat.

    Bei der Analyse brauchst du nicht nur das aktuelle Zeichen, sondern auch das Nachfolgende. Das nennt sich LL(1)-Parser. Schau dir mal ein paar Uni-Vorlesungen dazu an - such nach theoretischer Informatik oder nach Compilerbau.

    Und noch was anderes, was in deiner Ausgabe seltsam erscheint: Du solltest die öffnende Klammer vom Namen der Funktion trennen. Ansonsten scheiterst du an einem Ausdruck wie 5 - (1 + 2).
    Gruß
    hal2000
    @hal2000

    Wieso sollte ich an so einem Ausdruck scheitern? Das Minus davor wird mit meiner momentanen Methode automatisch in einen eigenen Töten gesetzt, da nach dem Minus ein Leerzeichen ist, welches als Trennen dient.
    Im übrigen steht das Gerüst dahinter schon, ich habe bisher einfach noch nicht den Tokenizer mit dem Rest verbunden, da ich mir nicht sicher war, ob ich das mit dem Tokenizer richtig mache.

    PS: Momentan dachte ich eher an ein 3-Schritte-System (Tokens erstellen, Tokens zu einem Typ zuordnen, Interpretieren). Was wäre jetzt der Vorteil von deiner Methode?
    Mir ist gerade aufgefallen, dass ja auch einzelne öffnende Klammern erkannt werden. Dann ist das in Ordnung. Allerdings muss dein Tokenizer auch funktionieren, wenn kein Leerzeichen zwischen Minus und Klammer steht. Kommt dann als Token '-(' raus? Das wäre nämlich ungünstig. Die StateMachine würde das Minus als Token ausgeben und bei der Klammer in einen neuen Zustand gehen.

    Das 3-Schritte-System ist so korrekt - die Tokens hast du ja schon. Als nächstes werden die Typen abstrahiert, also z.B. '12' zu NUMBER, '+', '-', ... zu OPERATOR usw. Vor der Interpretation solltest du die Tokens auf syntaktische Korrektheit prüfen. Hast du dich dafür schon mit Grammatiken beschäftigt? Wenn nicht, kannst du auch erstmal drauflosinterpretieren und bei Fehlern einfach abbrechen (was ein Interpreter ja auch normalerweise tut).
    Gruß
    hal2000
    Wie gesagt, es handelt sich bei dem Teil bisher nur um einen Prototypen, außerdem möchte ich bei der Syntax möglichst wenig tolerieren. Was meinst du mit Grammatiken (ich habe mir erstmal absichtlich gar nichts angeschaut, dass ich mir auch mal was selber ausdenke und nicht größtenteils nur nochmal 1:1 dasselbe zu programmieren wie bei der Vorlage.
    Soo, ich habe mal versucht, meinen Tokenizer etwas zu verbessern bzw. mit weniger Konstanten zu arbeiten. Außerdem finde ich es jetzt etwas übersichtlicher und einfacher zu verstehen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Text
    2. Public Class Tokenizer
    3. Public Property Code As String = ""
    4. Public Property TokenizerInfo As TokenizerInfo = New TokenizerInfo
    5. Public Property Tokens As List(Of Token()) = New List(Of Token())
    6. Public Sub Tokenize()
    7. Debug.WriteLine("----------------------------------------------------------------------")
    8. 'Den Code in die einzelnen Zeilen zerteilen
    9. Dim Lines As IEnumerable(Of String) = Code.Split(TokenizerInfo.LineSeperators.ToArray, StringSplitOptions.RemoveEmptyEntries)
    10. 'Alle Zeilen durchgehen
    11. Dim Tokens As New List(Of Token())
    12. For LineIndex As Integer = 0 To Lines.Count - 1
    13. Dim LineTokens As New List(Of Token)
    14. Dim Line As String = Lines(LineIndex)
    15. 'Zeile auseinandernehmen
    16. Debug.WriteLine(String.Format("Analyzing Line {0}: '{1}'", LineIndex + 1, Line))
    17. Dim Buffer As New StringBuilder(String.Empty)
    18. Dim Type As BasicTokenType = BasicTokenType.Undefined
    19. For CharIndex As Integer = 0 To Line.Length - 1
    20. Dim CurrentChar As Char = Line(CharIndex)
    21. 'Prüfen, was der Char ist
    22. Select Case True
    23. Case TokenizerInfo.TokenSeperators.Contains(CurrentChar)
    24. 'Ein Tokentrenner wurde gefunden
    25. 'Prüfen, ob sich der Car in einem String oder einem Kommentar befindet
    26. If Not (Type = BasicTokenType.String OrElse Type = BasicTokenType.Comment) Then
    27. 'Buffer in neuen Token entleeren
    28. LineTokens.Add(New Token(Buffer.ToString))
    29. Buffer.Clear()
    30. Type = BasicTokenType.Undefined
    31. Else
    32. 'Nichts machen
    33. Buffer.Append(CurrentChar)
    34. End If
    35. Case CurrentChar = TokenizerInfo.StringIndicator
    36. 'Ein Stringanfang oder ein Stringende wurde gefunden
    37. 'Prüfen, ob sich der Car in einem String oder einem Kommentar befindet
    38. If Type = BasicTokenType.Comment Then
    39. 'Nichts machen
    40. Buffer.Append(CurrentChar)
    41. ElseIf Type = BasicTokenType.String Then
    42. 'String beenden
    43. Buffer.Append(CurrentChar)
    44. LineTokens.Add(New Token(Buffer.ToString))
    45. Buffer.Clear()
    46. Type = BasicTokenType.Undefined
    47. Else
    48. 'Buffer in neuen Token entleeren und aktuellen Char dem Buffer hinzufügen
    49. LineTokens.Add(New Token(Buffer.ToString))
    50. Buffer.Clear()
    51. Type = BasicTokenType.String
    52. Buffer.Append(CurrentChar)
    53. End If
    54. Case CurrentChar = TokenizerInfo.CommentIndicator
    55. 'Das Kommentarzeichen wurde gefunden
    56. If Not (Type = BasicTokenType.String OrElse Type = BasicTokenType.Comment) Then
    57. 'Buffer in neuen Token entleeren
    58. LineTokens.Add(New Token(Buffer.ToString))
    59. Buffer.Clear()
    60. Type = BasicTokenType.Comment
    61. Buffer.Append(CurrentChar)
    62. Else
    63. 'Nichts machen
    64. Buffer.Append(CurrentChar)
    65. End If
    66. Case CurrentChar = TokenizerInfo.OpenBracket
    67. 'Eine öffnendee Klammer wurde gefunden
    68. If Not (Type = BasicTokenType.String OrElse Type = BasicTokenType.Comment) Then
    69. 'Buffer in neuen Token entleeren
    70. Buffer.Append(CurrentChar)
    71. LineTokens.Add(New Token(Buffer.ToString))
    72. Buffer.Clear()
    73. Type = BasicTokenType.Undefined
    74. Else
    75. 'Nichts machen
    76. Buffer.Append(CurrentChar)
    77. End If
    78. Case CurrentChar = TokenizerInfo.CloseBracket
    79. 'Eine schließende Klammer wurde gefunden
    80. If Not (Type = BasicTokenType.String OrElse Type = BasicTokenType.Comment) Then
    81. If Buffer.Length > 0 Then
    82. 'Buffer in neuen Token entleeren
    83. LineTokens.Add(New Token(Buffer.ToString))
    84. Buffer.Clear()
    85. End If
    86. Buffer.Append(CurrentChar)
    87. LineTokens.Add(New Token(Buffer.ToString))
    88. Buffer.Clear()
    89. Type = BasicTokenType.Undefined
    90. Else
    91. 'Nichts machen
    92. Buffer.Append(CurrentChar)
    93. End If
    94. Case Else
    95. 'Ein anderes Zeichen wurde gefunden
    96. 'Nichts machen
    97. Buffer.Append(CurrentChar)
    98. End Select
    99. Next
    100. LineTokens.Add(New Token(Buffer.ToString))
    101. 'Alle leeren Tokens entfernen
    102. LineTokens = LineTokens.Where(Function(t) t.Code <> String.Empty).ToList
    103. 'Tokens der aktuellen Zeile abspeichern
    104. Tokens.Add(LineTokens.ToArray)
    105. Debug.WriteLine(" Splitted Line in Tokens:")
    106. Debug.WriteLine(String.Format(" '{0}'", String.Join("' '", LineTokens)))
    107. Next
    108. 'Alle Tokens abspeichern
    109. Me.Tokens = Tokens
    110. End Sub
    111. Private Enum BasicTokenType
    112. [String]
    113. Comment
    114. Undefined
    115. End Enum
    116. End Class

    Dazu gehören halt noch die Klassen Token und TokenizerInfo:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Token
    2. Public Property Code As String = ""
    3. Public Sub New(code As String)
    4. Me.Code = code
    5. End Sub
    6. Public Overrides Function ToString() As String
    7. Return Me.Code
    8. End Function
    9. End Class

    VB.NET-Quellcode

    1. Public Class TokenizerInfo
    2. Public Property OpenBracket As Char = "("c
    3. Public Property CloseBracket As Char = ")"c
    4. Public Property StringIndicator As Char = """"c
    5. Public Property CommentIndicator As Char = "'"c
    6. Public Property TokenSeperators As IEnumerable(Of String) = {" "}
    7. Public Property LineSeperators As IEnumerable(Of Char) = {ControlChars.Cr, ControlChars.Lf}
    8. End Class

    Der Sinn der Token-Klasse sollte klar sein, die TokenizerInfo gibt einfach ein paar Zeichen, welche man je nach Anwendung eventuell ändern wollte und fasst diese eben der Übersichtlichkeit wegen zusammen.

    Der Output ist soweit ich weiß, immer noch derselbe, nur etwas anders formatiert:
    Aus:

    Quellcode

    1. X <- (sin(cos(19)) + 8) / 10 'Variable zuweisen
    2. Y <- Input("Hallo Welt, bitte geben sie eine Zahl ein:")
    3. 'Jetzt das noch ausgeben
    4. Output(Y)

    wird:

    Quellcode

    1. Analyzing Line 1: 'X <- (sin(cos(19)) + 8) / 10 'Variable zuweisen'
    2. Splitted Line in Tokens:
    3. 'X' '<-' '(' 'sin(' 'cos(' '19' ')' ')' '+' '8' ')' '/' '10' ''Variable zuweisen'
    4. Analyzing Line 2: 'Y <- Input("Hallo Welt, bitte geben sie eine Zahl ein:")'
    5. Splitted Line in Tokens:
    6. 'Y' '<-' 'Input(' '"Hallo Welt, bitte geben sie eine Zahl ein:"' ')'
    7. Analyzing Line 3: ''Jetzt das noch ausgeben'
    8. Splitted Line in Tokens:
    9. ''Jetzt das noch ausgeben'
    10. Analyzing Line 4: 'Output(Y)'
    11. Splitted Line in Tokens:
    12. 'Output(' 'Y' ')'

    Jetzt fehlt nur noch die Zuordnung zu verschiedenen Typen, das Interpretieren ist dann ne andere Sache :).

    PS: Ich habe zwar schon 2 Basistypen für den Tokenizer implementiert, diese speicher ich aber nicht mit, da es mir dann etwas zu unübersichtlich zwischen all den schon zugeordneten und den noch nicht zugeordneten Tokens werden würde.
    Ich hänge gerade daran fest, die Tokens auch zuzuordnen. Mein Plan war, alles so zu machen, dass am Schluss ein großer Haufen verschachtelter Funktionen bleibt. Beispielsweise so:

    Quellcode

    1. X <- (sin(Input("Nummer 1:")) * 5 ^ tan(6))-5 / 3

    wirs zu:

    Quellcode

    1. <-(X, -((*(sin(Input("Nummer 1:")), ^(5, tan(6)), /(5, 3))

    Also im Grunde genommen eine Art Prefix-Notation, nur dass jede Klammerebene eine eigene Klasse ist. Ich mach mal eine Art Baumstruktur, vielleicht ist das dann ja übersichtlicher:

    Quellcode

    1. <-( )
    2. X, -( )
    3. ( , /( )
    4. *( ) 5, 3
    5. sin( ), ^( )
    6. Input( ) 5, tan( )
    7. "Nummer 1:" 6

    Vielleicht versteht ihr ja so, wie ich das gemeint habe :). Im Kopf funktioniert das zwar auch, jedoch habe ich immer mehr Probleme, nicht zuletzt wegen der verschiedenen Datentypen und vor Allem auch wegen den Statements (If, For, ...). Kann mir da vielleicht jemand weiterhelfen?
    Du solltest mal versuchen, die Rechnungen so zu unterteilen, dass am Schluss ein Baum mit Rechenoperationen entsteht, die nur 2 Werte haben
    Folglich wird aus "b + 9 * (3 - a)"

    Quellcode

    1. Mul
    2. / \
    3. Add Sub
    4. / \ / \
    5. b 9 3 a


    So hab ich mich mal an einen Formelparser rangemacht, der Variablen und Grundrechenarten unterstützte, wird aber komplex, sobald Funktionen im Ausdruck vorhanden sind.

    Übrigends: ein Compiler zerlegt genau so, da die Befehle so besser in Assembler übersetzt werden können.
    @EiPott
    Das Problem ist halt, dass ich das gar nicht übersetzen will, sondern danach direkt interpretieren. Dein System ist übrigens genau dasselbe wie das, was ich vorher hatte mit dem einzigsten Unterschied in der Darstellung.

    PS: Dein Beispiel ist falsch, Punkt for Strich :P.


    Mein Hauptproblem ist, dass ich ja keinen Formelparser sondern einen Parser für komplette Scripte programmieren will. Einen ganz normalen Formelparser zu programmieren ist ja jetzt wirklich nicht so schwer, Codeteile zu parsen, welche sich über mehrere Zeilen usw ziehen finde ich zumindest deutlich schwerer. Außerdem habe ich noch das Problem mit den verschiedenen Typen :S.
    Der Parser an sich funktioniert (zumindest bei deinem einfachen Fall) genauso, wie der für nen einfachen Formelparser. Du musst vielleicht noch so Sachen wie Strings berücksichtigen.
    Einzig die Tokens an sich müssen dann aufwändiger behandelt werden, aber das parsen ist davon ja erstmal gar nicht betroffen.