#!/usr/bin/env python
# <http://colorforth.com/POL.htm>

from __future__ import division
import sys
import StringIO
program = "4837 758 + -338 + 23 + 4457 + -8354 + , quit a"
position = 0
def parseWord():
    global program
    global position
    word = StringIO.StringIO()
    while program[position] == " ":
        position += 1
    while program[position] != " ":
        word.write(program[position])
        position += 1
    return(word.getvalue())
class DictEntry(object):
    def __init__(self, word, value, next):
        self.key = word
        self.value = value
        self.next = next
class Routine(object):
    def __init__(self, startingPosition, context): # ends by ";"
        self.startingPosition = startingPosition
        self.context = context
""" TODO:
error handling:
  * usage of verb not in dict: "<word> ?"
  * stack empty: "<word> STACK!"
  * out of range access: "<word> LIMIT!"
  * auto-hex numbers, i.e. -B
  * fixed-point numbers.
  * return stack (for exits).
  * IF ELSE THEN (instruction-generating words).
  * begin ... <bool> end (while false, continue looping)
  * a b DO ... CONTINUE; first is the stopping value. If args are equal, nothing is done.
"""

def nounP(word):
    return(word[0] in "0123456789-.E")
def typeOut(stack):
    print(stack.pop())
def evalNoun(stack, word):
    return(int(word))
def dup(stack):
    value = stack[-1]
    push(stack, value)
def dereference(stack):
    addr = stack.pop()
    stack.push(addr.value)
def assign(stack):
    addr = stack.pop()
    value = stack.pop()
    addr.value = value
def swap(stack):
    a = stack.pop()
    b = stack.pop()
    push(stack, a)
    push(stack, b)
def over(stack):
    topItem = stack.pop()
    lowerItem = stack.pop()
    push(stack, lowerItem)
    push(stack, topItem)
    push(stack, lowerItem)
rootDictEntry = None
def addDictEntry(key, value):
    global rootDictEntry
    rootDictEntry = DictEntry(key, value, rootDictEntry)
def findDictEntry(key):
    e = rootDictEntry
    while e is not None:
        if e.key == key:
            return(e)
        e = e.next
    return(None)
def getDictContext():
    return(rootDictEntry)
def makeVariable(initialValue):
    name = parseWord()
    addDictEntry(name, initialValue)
def parseConstant(stack):
    value = stack.pop()
    name = parseWord()
    addDictEntry(name, value)
def remember(stack): # re-member!
    # FIXME should both define a dict entry AND delete old stuff.
    value = stack.pop()
    name = parseWord()
    addDictEntry(name, value)
def define(stack):
    word = parseWord()
    name = word
    startingPosition = position
    while word != ";":
        word = parseWord()
    addDictEntry(name, Routine(startingPosition, getDictContext()))
def negate(stack):
    value = stack.pop()
    push(stack, -value)
def divide(stack):
    a = stack.pop()
    b = stack.pop()
    stack.push(b/a)
def modulate(stack):
    a = stack.pop()
    b = stack.pop()
    stack.push(b%a)
def power(stack):
    a = stack.pop()
    b = stack.pop()
    stack.push(b ** a)
verbs = {
    "+": lambda stack: push(stack, stack.pop() + stack.pop()),
    "-": lambda stack: push(stack, stack.pop() - stack.pop()),
    "*": lambda stack: push(stack, stack.pop() * stack.pop()),
    "/": divide,
    #lambda stack: push(stack, stack.pop() / stack.pop()), 
    "mod": modulate,
    "**": power,
    #lambda stack: push(stack, stack.pop() % stack.pop()),
    "@": dereference,
    ",": typeOut,
    "dup": dup,
    "drop": lambda stack: stack.pop(),
    "swap": swap,
    "over": over,
    "constant": parseConstant,
    "integer": lambda stack: makeVariable(int(stack.pop())),
    "real": lambda stack: makeVariable(float(stack.pop())),
    "=": assign,
    "quit": lambda stack: sys.exit(0),
    "remember": remember,
    "minus": negate,
    "abs": lambda stack: push(stack, abs(stack.pop())),
    # FIXME max, min
    # TODO not, or, and, imp, xor
    # TODO comparisons
    ":": define,
}
def evalVerb(stack, word):
    entry = findDictEntry(word)
    if entry is None:
        return(verbs[word](stack))
    else:
        return(entry.value)
stack = []
def push(stack, value):
    stack.append(value)
def eval(startingPosition = 0, context = None):
    global position
    global rootDictEntry
    oldPosition = position
    oldRootDictEntry = rootDictEntry
    position = startingPosition
    rootDictEntry = context
    while True:
        word = parseWord()
        if word == ";":
            break
        if nounP(word):
            push(stack, evalNoun(stack, word))
        else: # verb
            evalVerb(stack, word)
    position = oldPosition
    rootDictEntry = oldRootDictEntry
eval()
