Welcome, guest | Sign In | My Account | Store | Cart
import sys
import os
import unittest

EOF = ""
SEGMENT_GAP = "\xff"

class TracException(Exception):
    pass

def list_get(list_, index, default=EOF):
    try:
        return list_[index]
    except:
        return default

class Io(object):
    def __init__(self, s=""):
        self.buffer = []
        self.buffer += s
        
    def pop(self, default=EOF):
        try:
            return self.buffer.pop(0)
        except:
            return default

    def push(self, s):
        self.buffer[0:0] += s

    def delete(self, i, j):
        del self.buffer[i:j]

    def peek(self, count=1):
        try:
            chars = self.buffer[0:0 + count]
        except:
            chars = [EOF]
        return "".join(chars)
    
    def get_trac_string(self):
        chars = []
        c = self.pop()
        while True:
            if c == '#':
                if self.peek(1) == '(' or self.peek(2) == '#(':
                    break
            elif c in "(),":
                break
            else:
                chars.append(c)
                c = self.pop()
        if c != EOF: 
            self.push(c)
        s = "".join(chars)
        return s

    def get_trac_protected_string(self):
        chars = []
        paren_count = 0
        matched = False
        c = self.pop()
        while c != EOF and not matched:
            if c == '(':
                paren_count += 1
            elif c == ')':
                paren_count -= 1
            chars.append(c)
            if paren_count != 0:
                c = self.pop()
            else:
                matched = True                 
        if not matched:
            raise TracException, "%s: can't find matching end parenthesis" %("get_trac_protected_string") 
        s = "".join(chars[1:-1])
        return s

class Tag(object):
    CLOSE_PAREN = ')'
    COMMA = ','    
    NONE = -1
    STRING = 300
    OPEN_ACTIVE_FUNC = 400
    OPEN_NEUTRAL_FUNC = 500
    DONE = 600    
    
class Token(object):
    def __init__(self, t, value=''):
        self.tag = t
        self.value = value
        
    def __str__(self):
        return "Token([%s] [%s])" % (str(self.tag), str(self.value))
    
class String(Token):
    def __init__(self, v):
        Token.__init__(self, Tag.STRING, v)
      
class Lexer(object):
    def __init__(self):
        self.lookahead = None
        self.output = ""         # last string printed to output by ps for unit testing
        self.trace = True        # flag for printing trace results of function evaluation     
        self.primitives = {"ds":self.ds, \
                           "ps":self.ps, \
                           "ss":self.ss, \
                           "cl":self.cl, \
                           "ad":self.ad, \
                           "su":self.su, \
                           "ml":self.ml, \
                           "dv":self.dv, \
                           "tn":self.tn, \
                           "tf":self.tf, \
                           "eq":self.eq \
                            }

    def initialize(self, program=""):
        self.forms = {}
        self.io = Io(program)

    def tn(self, args):
        self.trace = True
        return ""

    def tf(self, args):
        self.trace = False
        return ""

    def ds(self, args):
        key = list_get(args, 0)
        value = list_get(args, 1)
        self.forms[key] = value
        return ""

    def ps(self, args):
        try:
            s = list_get(args, 0)
            print s
            self.output = s
        except:
            pass
        return ""
    
    def ad(self, args):
        try:
            num1 = int(list_get(args, 0))
            num2 = int(list_get(args, 1))
            return str(num1 + num2)
        except:
            return ""
    
    def su(self, args):
        try:
            num1 = int(list_get(args, 0))
            num2 = int(list_get(args, 1))
            return str(num1 - num2)
        except:
            return ""
    
    def ml(self, args):
        try:
            num1 = int(list_get(args, 0))
            num2 = int(list_get(args, 1))
            return str(num1 * num2)
        except:
            return ""
    
    def dv(self, args):
        try:
            num1 = int(list_get(args, 0))
            num2 = int(list_get(args, 1))
            return str(num1 / num2)
        except:
            return ""

    def eq(self, args):
        try:
            s1 = list_get(args, 0)
            s2 = list_get(args, 1)
            eq_result = list_get(args, 2)
            neq_result = list_get(args, 3)
            if s1 == s2:
                return eq_result
            else:
                return neq_result
        except:
            return ""
    
    def ss(self, args):
        try:
            form_key = args.pop(0)
            form = self.forms[form_key]
            form_marked = form
            for i in range(len(args)):
                arg = args[i]
                marker = "%s%s" % (SEGMENT_GAP, chr(i))
                form_marked = form_marked.replace(arg, marker)
            self.forms[form_key] = form_marked
            form_list = []
            form_list += form_marked
            return ""
        except:
            return ""
            
    def cl(self, args):
        try:
            form_key = args.pop(0)
            form = self.forms[form_key]
            form_processed = form
            for i in range(len(args)):
                arg = args[i]
                marker = "%s%s" % (SEGMENT_GAP, chr(i))
                form_processed = form_processed.replace(marker, arg)
            return form_processed
        except:
            return ""

    def lexan(self):
        token = None
        while not token:
            c = self.io.pop()
            if c == '\t' or  c == '\n':
                pass # strip out white space
            elif c == '(':
                self.io.push(c)
                s = self.io.get_trac_protected_string()
                token = String(s)       
            elif c == ')':
                token = Token(Tag.CLOSE_PAREN)
            elif c == '#' and self.io.peek(1) == '(':
                self.io.delete(0,1)
                token = Token(Tag.OPEN_ACTIVE_FUNC)
            elif c == '#' and self.io.peek(2) == '#(':
                self.io.delete(0,2)
                token = Token(Tag.OPEN_NEUTRAL_FUNC)
            elif c == ',':
                token = Token(Tag.COMMA)
            elif c == EOF:
                token = Token(Tag.DONE)
            else:
                self.io.push(c)
                s = self.io.get_trac_string()
                token = String(s)       
        
        return token
            
    def func(self, tag=""):
        result = ""
        args = []
        token = self.lookahead
        if token.tag == Tag.STRING:
            args.append(token.value)        
        self.factor()
        while True:
            token = self.lookahead
            if token.tag in (Tag.STRING, Tag.OPEN_ACTIVE_FUNC, 
                             Tag.OPEN_NEUTRAL_FUNC, Tag.COMMA):
                if token.tag == Tag.STRING:
                    args.append(token.value)
                result = self.factor()
                if token.tag == Tag.OPEN_NEUTRAL_FUNC:
                    args.append(result)
            else:
                if tag:
                    result = self.eval_func(args)
                break
        return result
            
    def factor(self):
        result = None
        token = self.lookahead
        if token.tag == Tag.OPEN_ACTIVE_FUNC:
            self.match(Tag.OPEN_ACTIVE_FUNC)
            result = self.func(token.tag)
            self.match(Tag.CLOSE_PAREN, result)
        elif token.tag == Tag.OPEN_NEUTRAL_FUNC:
            self.match(Tag.OPEN_NEUTRAL_FUNC)
            result = self.func(token.tag)
            self.match(Tag.CLOSE_PAREN)
        elif token.tag == Tag.COMMA:
            self.match(Tag.COMMA)
        elif token.tag == Tag.STRING:
            self.match(Tag.STRING)
        elif self.lookahead.tag == Tag.DONE:
            self.match(Tag.DONE)
        else:
            raise TracException("factor: bad token tag - %s" % token)     
        return result
   
    def match(self, tag, result=None):
        if self.lookahead.tag == tag:
            if result != None:
                self.io.push(result)
            self.lookahead = self.lexan()
        else:
            raise TracException("match: tag %s doesn't match %s" % (tag, self.lookahead))

    def eval_func(self, args):
        result = ""
        try:
            func_name = args[0]
            args = args[1:]
            primitive = self.primitives.get(func_name, None)
            if primitive:
                result = primitive(args)
                if self.trace:
                    print "eval: %s %s -> [%s]" % (func_name, args, result)
        except Exception, e:
            raise TracException, "%s: failed - %s" %("eval", e) 
        return result
        
    def parse(self):
        self.lookahead = self.lexan()
        while self.lookahead.tag != Tag.DONE:
            self.func()
            self.match(Tag.DONE)
        print "Forms: %s" % self.forms
        print "Output: %s" % self.output
        return self
            
class TestTrac(unittest.TestCase):
        
    def setUp(self):
        pass
        
    def __test(self, program, output):
        self.lexer = Lexer()
        self.lexer.initialize(program)
        self.lexer.parse()
        self.assertEqual(self.lexer.output, output)

    def test_1_ps(self):
        self.__test("#(ps,Hello world)", "Hello world")
    
    def test_2_equal(self):
        self.__test("#(ps,#(eq,A,A,=,!=))", "=")

    def test_3_not_equal(self):
        self.__test("#(ps,#(eq,Cat,Dog,equal,not equal))", "not equal")

    def test_4_ds(self):
        self.__test("#(ds,AA,Cat)#(ps,#(cl,AA))", "Cat")

    def test_5_protect_parens(self):
        self.__test("#(ds,AA,Cat)#(ds,BB,(#(cl,AA)))#(ps,(#(cl,BB)))", "#(cl,BB)")

    def test_6_neutral_func(self):
        self.__test("#(ds,AA,Cat)#(ds,BB,(#(cl,AA)))#(ps,##(cl,BB))", "#(cl,AA)")

    def test_7_indirection(self):
        self.__test("#(ds,AA,Cat)#(ds,BB,(#(cl,AA)))#(ps,#(cl,BB))", "Cat")

    def test_8_ss(self):
        self.__test("#(ds,AA,Hello X)#(ss,AA,X)#(ps,#(cl,AA,world))", "Hello world")

    def test_9_factorial(self):
        self.__test("""
#(ds,Factorial,(#(eq,X,1,1,(#(ml,X,#(cl,Factorial,#(su,X,1)))))))
#(ss,Factorial,X)
#(ps,#(cl,Factorial,5))
""", "120")

if __name__ == "__main__":
    print __file__
    unittest.main()

History