
import sys



class Modifier:
    
    def __init__(self, fac=0.0, mod=0.0):
        self.fac = fac
        self.mod = mod
        
    def apply(self, v):
        return v*self.fac+self.mod


class ForthFunction:
    
    def __init__(self, s):
        self.formula=forth_parse(s)
    
    def f(self, *a):
        return forth_eval(self.formula, a)
        

def forth_parse(s):
    res=[]
    makenewv=False
    # pass 1
    i=0
    while i<len(s):
        c=s[i]
        if c==' ':
            makenewv=True
            i+=1
            continue
        if c.isdigit() or c=='.':
            if len(res)==0 or not res[-1][-1].isdigit() or makenewv:
                res.append(c)
                makenewv=False
            else:
                res[-1]+=c
        elif c=='-' and i+1<len(s) and s[i+1].isdigit():
            makenewv=False
            res.append(s[i:i+2])
            i+=1
        elif c.isalpha():
            if len(res)==0 or not res[-1][0].isalpha() or makenewv:
                res.append(c)
                makenewv=False
            else:
                res[-1]+=c
        else:
            if makenewv:
                res.append(c)
                makenewv=False
            else:
                res[-1]+=c
        i+=1
    # pass 2
    for i in range(len(res)):
        if res[i][0].isdigit() or (res[i][0]=='-' and len(res[i])>1 and res[i][1].isdigit()):
            if "." in res[i]:
                res[i]=float(res[i])
            else:
                res[i]=int(res[i])
    return res


def bool2int(v):
    if v:
        return 1
    return 0


class ForthFormulaException(Exception):
    
    def __init__(self, v):
        self.v=v
        
    def __str__(self):
        return self.v


def forth_eval(frm, v=[], i=0, stk=[]):
    while i<len(frm):
        s=frm[i]
        print("DEBUG:", s, "stack:", stk)
        if type(s) is type(2) or type(s) is type(2.2):
            stk.append(s)
        elif s.isalpha():
            if len(s)!=1:
                if s=="SWAP":
                    n1=stk.pop()
                    n2=stk.pop()
                    stk.append(n1)
                    stk.append(n2)
                    i+=1
                    continue
                elif s=="DUP":
                    n1=stk.pop()
                    stk.append(n1)
                    stk.append(n1)
                    i+=1
                    continue
                else:
                    raise ForthFormulaException("no long vars allowed or keyword not known")
            j=ord(s)
            if j>64+32:
                j-=64+32+1
            else:
                j-=64+1
            if j>=len(v):
                raise ForthFormulaException("referenced value not present")
            stk.append(v[j])
        elif s=='+':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            nv=stk.pop()+stk.pop()
            stk.append(nv)
        elif s=='-':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=n2-n1
            stk.append(nv)
        elif s=='*':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            nv=stk.pop()*stk.pop()
            stk.append(nv)
        elif s=='/':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=n2/n1
            stk.append(nv)
        elif s=='<':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=bool2int(n2<n1)
            stk.append(nv)
        elif s=='>':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=bool2int(n2>n1)
            stk.append(nv)
        elif s=='<=':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=bool2int(n2<=n1)
            stk.append(nv)
        elif s=='>=':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=bool2int(n2>=n1)
            stk.append(nv)
        elif s=='=' or s=='==':
            if len(stk)<2:
                raise ForthFormulaException("missing value on stack")
            n1=stk.pop()
            n2=stk.pop()
            nv=bool2int(n2==n1)
            stk.append(nv)
        elif s=='(':
            if len(stk)<1:
                raise ForthFormulaException("missing value on stack")
            if stk.pop()>0:
                k=i+1
                i,stk = forth_eval(frm,v,k,stk)
                i+=k
            else:
                j=i+1
                while j<len(frm) and frm[j]!=':':
                    j+=1
                if j>=len(frm):
                    raise ForthFormulaException("':' missing")
                k=j+1
                print("k=",k)
                i,stk = forth_eval(frm,v,k,stk)
                i+=k
        elif s==')' or s==':':
            if s==':':
                j=i+1
                while j<len(frm) and frm[j]!=')':
                    j+=1
                if j>=len(frm):
                    raise ForthFormulaException("')' missing")
            else:
                j=i
            return j,stk
        i+=1
    if len(stk)!=1:
        raise ForthFormulaException("too many or no values remaining on stack")
    return stk[0]



if __name__=="__main__":

    tests=[ ( "3 4 5 6 7 7 * + - * - a -", [99], 104 ), 
            ( "3 4 5 6 7 7 * + - * - a - DUP 100 > ( 17 10 + + : -23 -59 + - ) b +", [99,3], 131),
            ( "3 4 5 6 7 7 * + - * - a - DUP 100 < ( 17 10 + + : -23 -59 + - ) b +", [99,3], 186) ]

    fails=0
    for ttt in tests:
        print(ttt[0])
        a=forth_parse(ttt[0])
        print(a)
        b=forth_eval(a,ttt[1],0,[])
        #b=forth_eval(a,ttt[1],0)
        print("result:",b,"should be:",ttt[2])
        if b!=ttt[2]:
            fails+=1
        del a,b
        print()

print("failures:", fails)
if fails:
    sys.exit(9)
else:
    sys.exit(0)

