Τετάρτη 8 Μαρτίου 2023

Revision 20 (Version 12)

 I found a mistake on evaluator parser, and I repaired it (see readme.txt in github).

New eval2 module. This evaluator produce M2000 code as a string value and passed to INLINE statement which execute it and print the result of the evaluated expression. Although is a good expression evaluator parser, is not so good as the M2000 parser, which can do this:

z=-32768% : print type$(z)="Integer", z=-32768

The integer value -32768% exist but the 32768% not exist as integer, so we have to apply the unary minus, the negation at the literal constant, not by splitting to a positive constant (which not exist as integer), and then apply the negation. The example parser use double type (the default type) so there is no problem. However if we like to use long, or integer or long long and currency type, we have the limitation of the max positive value which isn't same as the minimum negative allowed value. Type Decimal has no such problem because the sign has own bit at the binary representation level, and an always positive value for the value. So -1 written internal as 1 bit for sign and 1 bit for value 1. An integer -1 is 0xFFFF (16 bit with value 1), so not only the most significant bit is 1 but also the value is two complements, which means we add 2 to 0xFFFF dropping the carry (module 16 addition), to get 0x0001 the actual absolute value).

Visual Basic 6 evaluator can't process this: ? 10%-32768%  at the immediate window. Because the - operator isn't unary, so has to parse 32768 as integer and that value not exist. M2000 parser can parse this (the ?  the Print statement in M2000 also) and return -32758. Another difference with Vb6 parser is the use of exponent operator, where any evaluation return double type. For M2000 where the base value is long, integer, long long, the result is the same type, with overflow control (raise error on overflow). The long long value (a 64bit integer, 8 bytes) has more digits than a double (8 bytes, with exponent and mantissa fields, so the exponent make mantissa with  less bit from a long long, less information). The Vb6 can't handle long long values, but because has low level statements the M2000 environment which is written with Vb6 can use them.

a=10%-32768%
? type$(a)="Integer", a=-32758


b= -3%^2
? type$(b)="Integer", b=-9


b1= (-3%)^2
? type$(b1)="Integer", b1=9


c= -3%^3
? type$(c)="Integer", c=-27




The eval2 module (exist in Info.gsb, which is on the setup file). The parser return the string when the parsing ends, which maybe has some other "statements" or "clauses" to be interpreted.


module CheckEvaluator (Expr$){
class EvalAst {
private:
Function Ast(&in as string) {
string Ast, op, op1
do
in=Trim$(in)
oper$=left$(in,1)
if oper$="-" then
in=Mid$(in, 2)
op1+="push -number:"
else.if oper$="+" then
in=Mid$(in, 2)
rem op1+=":"
else
exit
end if
always
Do
Ast+=.Ast1(&in , op1): op1=""
in=Trim$(in)
oper$=left$(in,1)
if Instr("+-", oper$)>0 else exit
Ast+=op : op=""
if len(oper$)>0 then
if oper$="+" then op+="push number+number:" else op+="shift 2: push number-number:"
end if
in=Mid$(in, 2)
until len(in)=0
=Ast+op '+op1
}
Function Ast1(&in as string, op1 as string) {
string Ast, op
Ast+=.Ast2(&in, &op1)
Do
in=Ltrim$(in)
oper$=left$(in,1)
if oper$="*" then
in=Mid$(in, 2)
Ast+=.Ast2(&in, &op)
Ast+="push number*number:"+op1
op1=""
else.if oper$="/" then
in=Mid$(in, 2)
Ast+=.Ast2(&in, &op)
Ast+="shift 2:push number/number:"+op1
op1=""
else
exit
end if
until len(in)=0
=Ast
}
Function Ast2(&in as string, &op1 as string) {
string Ast, op2
integer m
Do
Do
in=Trim$(in)
oper$=left$(in,1)
if oper$="-" then
in=Mid$(in, 2)
op1+="push -number:"
else.if oper$="+" then
in=Mid$(in, 2)
rem op1+=":"
else
exit
end if
always
Ast+=.Ast3(&in)
m++
in=Trim$(in)
oper$=left$(in,1)
if m>1 then
Ast+="shift 2:push number^number:"
if oper$<>"^" then Ast+=op1: op1=""
end if
oper$=left$(in,1)
if oper$="^" else exit
in=ltrim$(Mid$(in, 2))
oper$=left$(in,1)
if oper$="" then exit
if Instr("-+", oper$)>0 then
op2=""
do
in=ltrim$(Mid$(in, 2))
if oper$="-" then op2+="push -number:"
oper$=left$(in,1): if oper$="" then exit
when Instr("-+", oper$)>0 and len(oper$)>0
Ast+=.Ast2(&in, &op2)+op2+"shift 2:push number^number:"
op2=""
   exit
end if
until len(in)=0
=Ast+op1: op1=""
}
Function Ast3(&in as string) {
in=Trim$(in)
if Asc(in)<>40 then =.GetNumber(&in) : exit
in=Mid$(in, 2)
=.Ast(&in)
in=Mid$(in, 2)
}
Function GetNumber (&in as string) {
string ch, num
boolean once
Do
ch=left$(in,1)
if once then
if instr("0123456789", ch)>0 else exit
else
if instr(".0123456789", ch)>0 else exit
if ch="." then once=true
end if
num+=ch
in=Mid$(in, 2)
until len(in)=0
if num="." then Error "missing number"
if len(num)=0 then =stack: exit
="push "+num+":"
}
public:
value () {
=.Ast(![])+"Print number"
}
}
Ast=EvalAst()

print "Evaluation using Eval():";
Pen 11 {print expr$+"="+ eval(expr$)}
r$=Ast(&Expr$)
print "       Remain:"+Expr$
print "           ";
Report r$, width-pos*2
print @(0),
Print "       Result :";
Pen 15 {inline r$}
}
CheckEvaluator "-2^-3^-4"
CheckEvaluator "-2^(-3)^-4"
CheckEvaluator "-2^2-2^2"
CheckEvaluator "(-2.5)^2"
CheckEvaluator "(-2)^3"
CheckEvaluator "(-(2))^3"
CheckEvaluator "--++--3.45"
CheckEvaluator "(-((-(4)))) alfa"
CheckEvaluator "1+2 * (3 + ( 2 ^ 2 * 5 + 6 * 7 * 2^3) - 9)  / 10"
CheckEvaluator "-10*-2/-3*-5"
CheckEvaluator "-10*-(2/-3*-5)"
CheckEvaluator "2/3/4"
checkevaluator "-2/3^(3*4)"
CheckEvaluator "-(2/2)^(3*-4)"
CheckEvaluator "-2/(3)^(3*-4)"
CheckEvaluator "-2/(2)^-12"


Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.