#!/usr/bin/env python
# I, Danny Milosavljevic, hereby place this file into the public domain

import scanners
import symbols
import StringIO
from core import Label
from core import registers
from core import Operation

def removedChars(list, chars):
    list = list[:]
    for char in chars:
        if char in list:
            list.remove(char)
    return(list)
class Parser(scanners.Scanner):
    def __init__(self, *args, **kwargs):
        scanners.Scanner.__init__(self, *args, **kwargs)
        self.labels = {}
    def parse(self):
        self.consume()
        while True:
            self.parseOptionalWhitespace()
            if self.input is None: # EOF
                break
            if self.input == ":":
                yield(self.parseLabelDefinition())
            else:
                yield(self.parseOperation())
            self.parseOptionalComment()
            if self.input == "\n":
                self.consume()
                self.parseOptionalWhitespace()
    def parseOptionalComment(self):
        self.parseOptionalWhitespace()
        while self.input != "\n":
            self.consume()
    def ensureLabel(self, ID):
        if ID in self.labels:
            return(self.labels[ID])
        l = Label(ID)
        self.labels[l.ID] = l
        return(l)
    def parseLabelDefinition(self):
        self.consume(":")
        l = self.ensureLabel(self.parseID())
        assert(not l.bResolved) # dupe
        return(l)
    #IDchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
    #ID1chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
    IDchars = set(removedChars([chr(i) for i in range(33, 255)], "%$"))
    ID1chars = set(removedChars([chr(i) for i in range(33, 255)], "%$0123456789"))
    def parseID(self):
        text = StringIO.StringIO()
        if (self.input not in self.__class__.ID1chars):
            self.err("<ID>")
        while self.input in self.__class__.IDchars:
            text.write(self.consume())
        return(symbols.intern(text.getvalue()))
    def parseOperator(self):
        return(self.parseID())
    @classmethod
    def getOperationArgumentCount(self, op): # override this
        return(2)
    def parseOperation(self):
        op = self.parseOperator()
        count = self.__class__.getOperationArgumentCount(op)
        arguments = [self.parseWArgument() for i in range(count)]
        return(Operation(op, arguments))
    def parseWArgument(self):
        self.parseWhitespace()
        return(self.parseArgument())
    def parseArgument(self):
        if self.input == "%":
            return(self.parseRegisterReference())
        elif self.input in "0123456789$":
            return(self.parseNumeral())
        elif self.input == "\"":
            return(self.parseString())
        else:
            return(self.parseLabelReference())
    def parseString(self):
        result = StringIO.StringIO()
        assert(self.input == '"')
        self.consume()
        while self.input and self.input != '"': # FIXME escape
            result.write(self.consume())
        self.consume('"')
        return(result.getvalue())
    def parseNumeral(self):
        assert(self.input in "0123456789$")
        if self.input == "$":
            self.consume()
            digits = "0123456789ABCDEF"
        else:
            digits = "0123456789"
        result = 0
        while self.input in digits:
            result = result * len(digits) + digits.index(self.consume())
        return(result)
    def parseLabelReference(self):
        ID = self.parseID()
        l = self.labels.get(ID) or self.ensureLabel(ID)
        return(l)
    def parseRegisterReference(self):
        self.consume("%")
        return(registers[self.parseID()])
