hier nun mal ein Beispiel für eine EvaluierungsKlasse (wenn interesse
besteht, machen wir auch einen Interpreter ...)
Der folgende Code funktioniert beim "SetExpr" Ausdruck "(5+3)*2" recht gut
nur 5+3*2 macht bei der Ausgabe Probleme ...
Das gilt es zu lösen.
Richtig: (5+3)*2 ...
; a = 5.0
mov edx, 5 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, a
call Fix2Gleit ; ruft die KonvertierungsRoutine auf
fld qword [a] ; lädt die Variable "a" in die FPU
; a = 3.0
mov edx, 3 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, b
call Fix2Gleit
fld qword [b] ; lädt die Variable "b" in die FPU
fadd qword [a] ; addiert zu "b" die var. "a"
fstp qword [ebx] ; Ergebnis "8" abspeichern in reg. "EBX"
call Ausgabe ; Ergebnis auf Bildschirm ausgeben
; a = 2.0
mov edx, 2 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, c
call Fix2Gleit ; Konvertierungsroutine
fld qword [c] ; var. "c" in FPU laden
fmul qword [b] ; mit "b" multiplizieren
fstp qword [ebx] ; Ergebnis speichern
call Ausgabe ; Ergebnis auf Bildschirm ausgeben
ret
Hier der EvalKlassenCode (Delphi/Pascal):
type
TParserClass = class
private
FExpression: string;
FExprNumbers: integer;
FLook: char;
FPtr: integer;
FParseMode: Byte;
public
function EvalExpression: extended;
function Term: Extended;
function Factor: extended;
procedure Match(const x: char);
function GetNumber: extended;
procedure Expected(const s: string);
function IsDigit(const c: char): boolean;
function GetValue: extended;
procedure GetChar;
procedure SetExpr(e: String);
procedure SkipWhiteSpace2;
constructor Create;
end;
constructor TParserClass.Create;
begin
tmpcode :=
#9+'BITS 32'+#10+
#9+'cpu 486'+#10+
#10+
#9+'extern _printf, _test_wert'+#10+
#10+
#9+'section .data'+#10+
#9+'global a,b,c'+#10+
'a:'+#9+'dq 1.0'+#10+
'b:'+#9+'dq 1.0'+#10+
'c:'+#9+'dq 1.0'+#10+
#10+
#9+'section .text'+#10+
#9+'extern Fix2Gleit, Ausgabe'+#10+
#9+'global _main'+#10+
'_main:'+#10+
#9+'push'+#9+'ebp'+#10+
#9+'mov'+#9+'ebp, esp'+#10+
#9+'sub'+#9+'esp, 32'+#10;
end;
function TParserClass.EvalExpression: extended;
function IsAddOp(const c: char): boolean;
begin
result := c in ['+', '-'];
end;
var
s1,s2: String;
et: Extended;
begin
result := 0.0;
try
if not IsAddop(FLook) then
begin
et := Term;
result := et;
end;
while IsAddop(FLook) do begin
case FLook of
'+': begin
Match('+');
et := Term;
if Fparsemode = 1 then begin
result := result + et;
exit;
end;
s1 := 'a';
tmpcode := tmpcode + #9 + 'fadd qword ['+s1+']' + #10;
tmpcode := tmpcode + #9 + 'fstp qword [ebx]' + #10;
tmpcode := tmpcode + #9 + 'call Ausgabe'+#10;
result := result + et;
end;
'-': begin
Match('-');
et := Term;
if Fparsemode = 1 then begin
result := result - et;
exit;
end;
tmpcode := tmpcode + 'call _subtrahieren'+#10;
result := result - et;
end;
end;
end;
except
on E: Exception do
raise Exception.Create(e.message);
end;
end;
function TParserClass.Term: extended;
var
ef : Extended;
s1 : String;
begin
result := Factor;
try
while FLook in ['*', '/'] do begin
case FLook of
'*': begin
Match('*');
ef := Factor;
if Fparsemode = 1 then begin
result := result * ef;
exit;
end;
if regcounter = 0 then s1 := 'b' else
if regcounter = 1 then s1 := 'c' else s1 := 'b';
tmpcode := tmpcode + #9 + 'fmul qword ['+ s1 +']' + #10;
tmpcode := tmpcode + #9 + 'fstp qword [ebx]' + #10;
tmpcode := tmpcode + #9 + 'call Ausgabe'+#10;
result := result * ef;
end;
'/': begin
Match('/');
ef := Factor;
if Fparsemode = 1 then begin
result := result / ef;
exit;
end;
result := result / ef;
end;
end;
end;
except
on E: Exception do
raise Exception.Create(e.message);
end;
end;
function TParserClass.IsDigit(const c: char): boolean;
begin
result := c in ['0'..'9'];
end;
function TParserClass.GetNumber: extended;
var
f: extended;
begin
result := 0.0;
try
if not IsDigit(FLook) then Expected('Value');
while IsDigit(FLook) do begin
result := 10 * result + Ord(FLook) - Ord('0');
GetChar;
end;
if FLook = '.' then begin
Match('.');
f := 10;
while IsDigit(FLook) do begin
result := result + (Ord(FLook) - Ord('0')) / f;
f := f * 10;
GetChar;
end;
end;
if FParseMode = 1 then inc(FExprNumbers);
SkipWhiteSpace2;
except
on E: Exception do
raise Exception.Create(e.message);
end;
end;
function TParserClass.Factor: extended;
var
undefined: boolean;
ident,s1,s2, s3,s: string;
i: integer;
en: Extended;
bw: Boolean;
fw: single;
looker: char;
fs: TFileStream;
r1, r2: Real;
begin
try
if FLook = '(' then begin
Match('(');
result := EvalExpression;
Match(')');
end else
begin
en := GetNumber;
if Fparsemode = 1 then begin
result := en;
exit;
end;
inc(FExprNumbers);
r1 := Frac(en);
if r1 = 0 then
s1 := '0' else
s1 := Copy(FloatToStr(r1),3,Length(FloatToStr(r1)));
r2 := Int(en);
s2 := FloatToStr(r2);
if regcounter = 0 then s3 := 'a' else
if regcounter = 1 then s3 := 'b' else s3 := 'c';
tmpcode := tmpcode + #9 + '; a = ' + s2 + '.' + s1 + #10;
tmpcode := tmpcode + #9 + 'mov' + #9 + 'edx, ' + s2 + #9 + #10;
tmpcode := tmpcode + #9 + 'mov' + #9 + 'eax, ' + s1 + #9 + #10;
tmpcode := tmpcode + #9 + 'mov' + #9 + 'ebx, ' + s3 + #10;
tmpcode := tmpcode + #9 + 'call'+ #9 + 'Fix2Gleit'+#10;
tmpcode := tmpcode + #9 + 'fld' + #9 + 'qword ['+s3+']'+#10;
inc(regcounter);
result := en;
end;
except
on E: Exception do
raise Exception.Create(e.message);
end;
end;
procedure TParserClass.SkipWhitespace2;
begin
while FLook in [' ', #9] do
GetChar;
end;
procedure TParserClass.GetChar;
begin
if FPtr < Length(FExpression) then begin
inc(FPtr);
FLook := FExpression[FPtr];
end else
FLook := #0;
end;
procedure TParserClass.Match(const x: char);
begin
if FLook = x then begin
GetChar;
SkipWhiteSpace2;
end else
Expected('''' + x + '''');
end;
procedure TParserClass.Expected(const s: string);
begin
RAISE Exception.Create(s + ' expected');
end;
function TParserClass.GetValue: extended;
var ee: Extended;
begin
try
FPtr := 0;
GetChar;
ee := EvalExpression;
inc(FExprNumbers);
result := ee;
finally
end;
end;
procedure TParserClass.SetExpr(e: String);
begin
FParseMode := 2;
FExprNumbers := 0;
FExpression := e;
showmessage(FloatToStr(GetValue));
end;
Das Showmessagedialogfeld gibt 16 aus .. also richtig.
Nur der AssemblerOutputCode ist falsch .. da muss nen algo. her
denke ich mir ...
--------------------------------
Hier nun der Komplette ASM-Code:
kompiliert mit "nasm" für win32
und den MingW CC Compiler
Eine Beispielumgebnung kann hier geladen werden:
http://kallup.part-time-scientists.com/setup.exe
Dazu das Programm starten, Datei öffnen,
FormTest2.wfm öffnen und dann auf das Menu
"Start->Ausführen" klicken
;-----------------
; Datei: test.asm
;-----------------
BITS 32
cpu 486
extern _printf, _test_wert
section .data
global a,b,c
a: dq 1.0
b: dq 1.0
c: dq 1.0
section .text
extern Fix2Gleit, Ausgabe
global _main
_main:
push ebp
mov ebp, esp
sub esp, 32
; a = 5.0
mov edx, 5 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, a
call Fix2Gleit
fld qword [a]
; a = 3.0
mov edx, 3 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, b
call Fix2Gleit
fld qword [b]
fadd qword [a]
fstp qword [ebx]
call Ausgabe
; a = 2.0
mov edx, 2 ; Vorkomma
mov eax, 0 ; Nachkomma
mov ebx, c
call Fix2Gleit
fld qword [c]
fmul qword [b]
fstp qword [ebx]
call Ausgabe
leave
ret
;--------------------------
; i:> nasm -f win32 fix.asm
;--------------------------
; Datei: fix.asm
;--------------------------
bits 32
cpu 486
section .data
zehn: dd 10
section .text
extern a,b,c
global Fix2Gleit, Ausgabe
Fix2Gleit:
mov [ebx], edx ; Vorkomma
mov [ebx+4], eax ; Nachkomma
; Anzahl der Dezimalstellen der Nachkommazahl bestimmen
push ebx
mov ebx, 10
xor ecx, ecx
.1:
inc ecx
xor edx, edx
div ebx
test eax, eax
jne .1
; Den Divisor für die FPU bestimmen
mov eax, 1
mov ebx, 10
.2:
mul ebx
loop .2
mov dword [zehn], eax
pop ebx
fild dword [ebx+4] ; Nachkommazahl
fidiv dword [zehn] ; Nachkommazahl in Realzahl umwandeln
fiadd dword [ebx] ; Vorkommazahl hinzu
fstp qword [ebx] ; Ergebnis als Double abspeichern
ret
EXTERN _printf, a,b,c
SECTION .data
fmt: db "%s, a=%.10f, b=%.10f, c=%.10f",10,0
fm2: db "%s, a=%.10f",10,0
str: db "c=a*b",0
SECTION .text
Ausgabe:
; push dword [c+4] ; double c (bottom)
; push dword [c] ; double c
; push dword [b+4] ; double b (bottom)
; push dword [b] ; double b
push dword [ebx+4] ; double a (bottom)
push dword [ebx] ; double a
push str ; users string
push fm2 ; address of format string
call _printf ; Call C function
add esp,16 ; pop stack 8*4 bytes
ret
--------------------------------------------
so alle Dateien zusammenführen:
c:> gcc -o test.exe test.obj fix.obj math.cc -lstdc++
Inztance öffnen:
var
pc: TParserClass;
...
pc := TParserClass.Create;
pc.SetExpr('(5+3)*2');
tmpcode := tmpcode +
#9+'leave'+#10+
#9+'ret';
showmessage(tmpcode);
...
Grüße
Jens
zum Beispiel:
(5+1)*2*3 = 36
...
pc := TParserClass.Create;
pc.SetExpr('(5+1)*2*3');
...
Servus
Jens
procedure TParserClass.SetExpr(e: String);
var i: integer;
r1, r2: Real;
s1, s2: String;
en: Extended;
begin
FExprNumbers := 0;
FExpression := e;
FTempNumber := 0;
datacode := 'v1: dq 1.0';
//FTempNumber := FExprNumbers;
FExprNumbers := 0;
FExpression := e;
en := GetValue;
r1 := Frac(en);
if r1 = 0 then
s1 := '0' else
s1 := Copy(FloatToStr(r1),3,Length(FloatToStr(r1)));
r2 := Int(en);
s2 := FloatToStr(r2);
tmpcode :=
#9+'mov' + #9 + 'edx, '+s2+#9+'; Vorkomma'+#10+
#9+'mov' + #9 + 'eax, '+s1+#9+'; Nachkomma'+#10+
#9+'mov' + #9 + 'ebx, v1'+#10+
#9+'call' + #9 + 'Fix2Gleit'+#10+
#9+'call' + #9 + 'Ausgabe'+#10;
//showmessage(FloatToStr(GetValue));
end;
function TParserClass.EvalExpression: extended;
function IsAddOp(const c: char): boolean;
begin
result := c in ['+', '-'];
end;
var
s1,s2: String;
et: Extended;
begin
result := 0.0;
try
if not IsAddop(FLook) then result := Term;
while IsAddop(FLook) do begin
case FLook of
'+': begin
Match('+');
et := Term;
result := result + et;
end;
'-': begin
Match('-');
et := Term;
result := result - et;
end;
end;
end;
except
on E: Exception do
raise Exception.Create(e.message);
end;
end;
function TParserClass.Term: extended;
var
ef : Extended;
s1, s2 : String;
begin
result := Factor;
try
while FLook in ['*', '/'] do begin
case FLook of
'*': begin
Match('*');
ef := Factor;
result := result * ef;
end;
'/': begin
Match('/');
ef := Factor;
function TParserClass.GetValue: extended;
begin
try
FPtr := 0;
GetChar;
result := EvalExpression;
if FLook <> #0 then
raise Exception.Create('incomplete expression');
finally
end;
end;