| |
|
|
- Seite 1 - |
|
Julian Schmidt | Hallo, ich habe mich mal an einem kleinen Parser probiert. Evtl. sind in diesen allerdings noch ein paar kleinere Fehler drin.
Vlt. könnt ihr mir helfen. KompilierenMarkierenSeparierenWindowTitle "Simple Parser"
Windowstyle 24
Window (%maxx-280)\2,(%maxy-210)\2 - 280,210
Var ed_calc&=Create("Edit",%hwnd,"5+(30*10)",10,10,width(%hwnd)-20,20)
Var lb_calc&=Create("ListBox", %hwnd, 0, 10, 40, width(%hwnd)-20, 100)
Var bt_calc&=Create("Button",%hwnd,"Ausrechnen",10,150,width(%hwnd)-20,25)
Declare calc$,pos_z1%
WhileNot iskey(27)
Waitinput 300
If getfocus(bt_calc&) or iskey(13)
ClearList lb_calc&
calc$=Translate$(Translate$(GetText$(ed_calc&),",",".")," ","")
Addstring(lb_calc&,calc$)
If (InStr(")",calc$)<>0) and (InStr("(",calc$)<>0)
while (InStr(")",calc$)<>0) and (InStr("(",calc$)<>0)
whileloop InStr(")",calc$),1,-1
pos_z1%=&loop
case (Mid$(calc$,pos_z1%,1)="(") : break
Endwhile
calc$=Init_Parse(Mid$(calc$,pos_z1%+1,InStr(")",calc$)-(pos_z1%+1)),Mid$(calc$,1,pos_z1%-1),Mid$(calc$,InStr(")",calc$)+1,Len(calc$)-InStr(")",calc$)))
Endwhile
Else
calc$=Translate$(Translate$(calc$,")",""),"(","")
Endif
Init_Parse(calc$)
Setfocus(ed_calc&)
Endif
EndWhile
Proc Init_Parse
Parameters math$, str1$, str2$
WhileNot iskey(27)
Case GetString$(lb_calc&,Getcount(lb_calc&)-1)<>str1$+math$+str2$ : Addstring(lb_calc&,str1$+math$+str2$)
If (InStr("^",math$)<>0)
Parse("^")
ElseIf (InStr("*",math$)<>0)
Parse("*")
ElseIf (InStr("+",math$)<>0)
Parse("+")
ElseIf (InStr("-",math$)<>0)
Parse("-")
ElseIf (InStr("\",math$)<>0) or (InStr("/",math$)<>0)
Parse("\")
Else
Settext ed_calc&,math$
break
EndIf
EndWhile
Return str1$+math$+str2$
EndProc
Proc Parse
Parameters op$
Declare pos_z2%, z1$,z2$
Var pos_op%=InStr(op$,math$)
Whileloop pos_op%-1,0,-1
If (Mid$(math$,&loop,1)="+") or (Mid$(math$,&loop,1)="-") or (Mid$(math$,&loop,1)="\") or (Mid$(math$,&loop,1)="/") or (Mid$(math$,&loop,1)="*") or (Mid$(math$,&loop,1)="^") or (trim$(Mid$(math$,&loop,1))="")
z1$=Mid$(math$,&loop+1,pos_op%-(&loop+1))
pos_z1%=&loop+1
break
Endif
EndWhile
Whileloop pos_op%+1,Len(math$)+1,1
If (Mid$(math$,&loop,1)="+") or (Mid$(math$,&loop,1)="-") or (Mid$(math$,&loop,1)="\") or (Mid$(math$,&loop,1)="/") or (Mid$(math$,&loop,1)="*") or (Mid$(math$,&loop,1)="^") or (trim$(Mid$(math$,&loop,1))="")
z2$=Mid$(math$,pos_op%+1,&loop-(pos_op%+1))
pos_z2%=&loop
break
Endif
EndWhile
If ((op$="\") and (z2$="0"))
math$="Division durch NULL!"
Else
Var erg$=Str$(If(op$="+",Val(z1$)+Val(z2$),If(op$="-",Val(z1$)-Val(z2$),If(op$="*",Val(z1$)*Val(z2$),If(op$="\",Val(z1$)/Val(z2$),If(op$="^",Val(z1$)^Val(z2$),"Error"))))))
math$=Mid$(math$,1,pos_z1%-1)+erg$+Mid$(math$,pos_z2%,(Len(math$)+1)-pos_z2%)
Settext ed_calc&,math$
Endif
EndProc
Scheint soweit allerdings zu funktionieren. Nur bei dem Klammern!?
LG
Julian |
|
|
| |
|
|
|
| |
|
- Seite 1 - |
|
Julian Schmidt | Jo, schon gesehen. Hilft mir aber nicht wirklich! Ich bin eher auf Fehlersuche und würde gerne einfach mal einige Fälle von euch hören wo mein Taschenrechner versagt und evtl. eine Strategie zur Lösung des Problems.
Evtl. habe ich ja sogar einige Rechenregeln außer Acht gelassen, welche wichtig wären!?? Evtl. auch Anregungen zum Programmier-Style, und wie man gewisse Dinge kürzen könnte. |
|
|
| |
|
|
|
| Gibt ja erstmal nur Klammer vor Punkt und Punkt vor Strich umzusetzen.
Funktioniert das denn bereits fehlerfrei?
Tricky ist eher sowas wie -5 + -1 |
|
|
| |
|
|
|
Julian Schmidt | Klammer vor Potenz vor Punkt vor Strich funktioniert bereits zum Großenteil. Nur gibt es bei bestimmten Klammerabfolgen leider noch Probleme. Etwa bei "5+(30*(4/2)+10)". Kann es momentan selbst mit TraceModus nicht ganz nachvollziehen. Scheint irgendwie mit dem Geteilt innerhalb der Klammer zusammenzuhängen. Mit Mal funktioniert es ganz hervorragend!
Wie würdest du, die Geschichte mit dem negativen Zahlen angehen? |
|
|
| |
|
|
|
| Das hängt sehr davon ab was genau Dein Parser später mal alles können soll -
diese Information fehlt mir z.B. .
Ich würde wohl wie in xpse umgesetzt immer einen Parser herstellen der möglichst
flexibel ist und eben egal welche Argumente von links nach rechts parsen und auflösen
kann. Wenn Du nur bisl Matheformeln parsen möchtest dann kannst für das Minus-
Vorzeichen einfach Ausnahmen/ Erkennungen programmieren. Hat zwar nichts mit
dem Thema zu tun aber Du kannst auch XProfan selbst verwenden wie z.B. in
XP-Script [...] (was aber nur bis XP vollständig korrekt funktioniert). |
|
|
| |
|
|
|
Julian Schmidt | Kannst du nochmal konkret etwa zu, wie man am Besten einen flexiblen Parser baut und zur Ausnahme\Erkennung einer negativen Zahl sagen? |
|
|
| |
|
|
|
| Ich schrieb ja bereits:
Das hängt sehr davon ab was genau Dein Parser später mal alles können soll -
diese Information fehlt mir z.B. . |
|
|
| |
|
|
|
Julian Schmidt | Konkret +,-,*,\,^ und den richtigen Umgang mit Klammern, Minuszahlen. Evtl. Wurzel bzw. Wurzel_n und Logarithmus. |
|
|
| |
|
|
|
Julian Schmidt | Wie würdest du den einen Parser möglichst flexibel schreiben? Wie programmiert man am besten eine "Minus-Vorzeichen Ausnahmen/ Erkennungen"? |
|
|
| |
|
|
|
| Erstmal brauchst paar Helferfunktionen.
Z.B. eine Funktion parse schreiben die z.B. 2 Longs als Param erhält:
SpeicherAddr,BytesToParse
und die Anzahl der geparsten Bytes zurückliefert.
Wenns ein Ansi-Parser werden soll dann ist BytesToParse ist ein 256 Byte großer
Speicher - einfach Dim nehmen. Darin sind alle Bytes auf True die die Funktion
parse mitnehmen soll. Quasi.
byte BytesToParse,65=1,1,1,1,1
womit die Chars A;B;C;D;E gemeint wären -
also die Funktion nur so lange ließt wie diese Chars auftreten.
Ist halt wohl das Schnellste da solch Funktion parse nur gezielte/ direkte Speicherzugriffe
braucht um zu ermitteln was los ist.
quasi while byte(BytesToParse,byte(SpeicherAddr,pos++)) statt 17 ifs und instrs -
also komplett ohne Instr und herumsucherei.
Wenn das erste Zeichen eine Klammer ist hat die Funktion z.B. auch die Eigenheit,
die aufgehenden Klammern zu zählen und auch dann die Anzahl gelesener Bytes
zurückzuliefern wenn, die Anzahl der Klammern von 1 auf 0 springt - so kannst
einfach die Klammern dann wegoperieren da immer das erste und das letzte
Zeichen Klammern sind wenn das erste Zeichen eine Klammer ist.
Dies ist dann meist 1 Argument das man in einer extra Liste ablegen
kann für späteres weiteres zerlegen.
Mache aus:
1+(10*20)+5
Listboxeinträge:
add 1 add 10*20 add 5 und in einem weitere Pass:
add 1 add tmp1,10 mul tmp1,20 add tmp1 add 5
quasi auflösen bis es nur noch Operationen gibt die Du in der Liste als "kann
ich ohne weitere OP ausrechnen" markiert hast.
Nach diesem Verfahren kannst beliebig-komplexe und mehrzeilige Argumente-
Ketten auflösen. |
|
|
| |
|
|
|
Julian Schmidt | hmmmm...ich muss ich ehrlich gestehen ich habe nur Bahnhof verstanden.
Wie würdest du einen Parser möglichst flexibel, unter Verwendung von (Teil)string-Funktionen, schreiben? |
|
|
| |
|
|
| |
|
- Seite 2 - |
|
|
Andreas Koch | Hallo Julian,
ich bin zwar nicht angesprochen worden, glaube aber zu wissen, was iF meint, da ich genau so einen Parser mal geschrieben habe. Die Grundüberlegung ist, dass man auch die komplizierteste Formel in Bestandteile (substr) zerlegen kann, die nach dem Schema "Variable Operator Variable" aufgebaut sind. Beispiel: "a+b" oder "a*b" oder "a^b" usw. a und b funktionieren dabei als Platzhalter und können selbst beliebig kompliziert sein, eigentlich werden sie als komplett neue Formel behandelt. Wenn man die Formel lange und fein genug aufdröselt, erhält man irgendwann etwas, was man ausrechnen kann und setzt dann rückwärts ein. Ich will versuchen es an einem Beispiel deutlicher zu machen:
25+8*(6-3*(15-3))
1. Stufe: 25+a a=8*(6-3*(15-3)) 2. Stufe: 8*b b=6-3*(15-3) 3. Stufe: 6-c c=3*(15-3) 4. Stufe: 3*d d=15-3
Nun kann ich d ausrechnen und muss nur wieder rückwärts einsetzen: d=12 c=3*d=36 b=6-c=-30 a=8*b=-240 ans=25-240=-215
Anstatt a b c und d empfehlen sich natürlich Listboxeinträge und keine Buchstaben.
Schönen Gruß
Andreas |
|
|
| |
|
|
|
Julian Schmidt | Danke, für deine Antwort. Mir war imho schon klar was ein Parser ist, ich habe ja auch schon einen selbst geschrieben (siehe Ausgangsbeitrag). Mir ging es viel mehr darum, wie man einen Parser möglichst flexibel programmiert um schnell weitere Rechengesetze wie zum Beispiel Klammerregeln etwa. Binomische Formeln oder weitere Rechenoperatoren wie Wurzel oder Logarithmus oder das korekte erkennen von negativen Zahlen einzufügen IF hatte schon dargelegt wie er einen Parser in dem Byte für Byte geparst wird schreiben würde. Dies war für meinen Geschmack leider etwas zu kompliziert (oder einfach nur für den Laien unverständlich formuliert)..... Gruß |
|
|
| |
|
|