#! /bin/env python
class EvalObj(object):
def __str__(self):
return "#<Object>"
class UnspecObj(EvalObj):
def __str__(self):
return "#<Unspecified>"
def ext_repr(self):
return self.__str__()
class NumberObj(EvalObj):
def __str__(selfl):
return "#<Number>"
class IntObj(NumberObj):
def __init__(self, num):
self.val = int(num)
def __str__(self):
return "#<Integer>"
def ext_repr(self):
return str(self.val)
class FloatObj(NumberObj):
def __init__(self, num):
self.val = float(num)
def __str__(self):
return "#<Float>"
def ext_repr(self):
return str(self.val)
class BoolObj(EvalObj):
def __init__(self, b):
self.val = b
def __str__(self):
return "#<Boolean>"
def ext_repr(self):
if self.val:
return "#t"
else:
return "#f"
class IdObj(EvalObj):
def __init__(self, string):
self.name = string
def __str__(self):
return "#<Identifier: " + self.name + ">"
def get_name():
return self.name
class OptObj(EvalObj):
pass
class ProcObj(OptObj):
def __init__(self, body, envt, para_list):
self.body = body
self.envt = envt
self.para_list = para_list
def ext_repr(self):
return "#<Procedure>"
def __str__(self):
return self.ext_repr()
class SpecialOptObj(OptObj):
def prepare(self, pc):
pass
def call(self, arg_list, pc, envt, cont):
pass
class BuiltinProcObj():
def __init__(self, f, name):
self.handler = f
self.name = name
def ext_repr(self):
return "#<Builtin Procedure: " + self.name + ">"
def __str__(self):
return self.ext_repr()
def call(self, arg_list):
return self.handler(arg_list)
# Convert an obj to boolean
def to_bool(obj):
if obj.val is False:
return BoolObj(False)
else:
return BoolObj(True)
# Mark all children of pc as flag
def _fill_marks(pc, flag):
pc = pc.chd
while pc:
pc.skip = flag
pc = pc.sib
class _builtin_if(SpecialOptObj):
def prepare(self, pc):
# TODO: check number of arguments
# Evaluate the condition first
self.state = 0 # Prepared
pc = pc.chd
pc.skip = False
pc.sib.skip = True
if pc.sib.sib:
pc.sib.sib.skip = True
def pre_call(self, arg_list, pc, envt, cont):
# Condition evaluated and the decision is made
self.state = 1
if (to_bool(arg_list[0])).val:
pc = pc.chd
pc.skip = True
pc.sib.skip = False
if pc.sib.sib:
# Eval the former
pc.sib.sib.skip = True
return (None, True) # Re-eval
else:
pc = pc.chd
pc.skip = True
pc.sib.skip = True
if pc.sib.sib:
# Eval the latter
pc.sib.sib.skip = False
return (None, True) # Re-eval
def post_call(self, arg_list, pc, envt, cont):
# Value already evaluated, so just return it
return (arg_list[0], False)
def call(self, arg_list, pc, envt, cont):
if self.state == 0:
return self.pre_call(arg_list, pc, envt, cont)
else:
return self.post_call(arg_list, pc, envt, cont)
def ext_repr(self):
return "#<Builtin Macro: if>"
def __str__(self):
return self.ext_repr()
class _builtin_lambda(SpecialOptObj):
def prepare(self, pc):
# TODO: check number of arguments
# Do not evaulate anything
_fill_marks(pc, True)
def call(self, arg_list, pc, envt, cont):
para_list = list() # paramter list
par = pc.chd # Switch to the first parameter
if par.obj.obj: # If there is at least one parameter
para_list.append(par.obj.obj)
if par.chd: # More paramters?
par = par.chd
while par:
para_list.append(par.obj)
par = par.sib
# Clear the flag to avoid side-effects (e.g. proc calling)
_fill_marks(pc, False)
pc = pc.chd.sib # Move pc to procedure body
#TODO: check body
body = list() # store a list of expressions inside <body>
while pc:
body.append(pc)
pc.next = None # Make each expression a orphan
# in order to ease the exit checking
pc = pc.sib
return (ProcObj(body, envt, para_list), False)
def ext_repr(self):
return "#<Builtin Macro: lambda>"
def __str__(self):
return self.ext_repr()
class _builtin_define(SpecialOptObj):
def prepare(self, pc):
if is_arg(pc.chd): # Simple value assignment
pc.chd.skip = True # Skip the identifier
pc.chd.sib.skip = False
else: # Procedure definition
_fill_marks(pc, True) # Skip all parts
def call(self, arg_list, pc, envt, cont):
# TODO: check identifier
if is_arg(pc.chd): # Simple value assignment
id = pc.chd.obj
obj = arg_list[0]
else: # Procedure definition
id = pc.chd.obj.obj
para_list = list() # Parameter list
par = pc.chd
if par.chd: # If there's at least one parameter
par = par.chd
while par:
para_list.append(par.obj)
par = par.sib
# Clear the flag to avoid side-effects (e.g. proc calling)
_fill_marks(pc, False)
pc = pc.chd.sib # Move pc to procedure body
#TODO: check body
body = list() # store a list of expressions inside <body>
while pc:
body.append(pc)
pc.next = None
pc = pc.sib
obj = ProcObj(body, envt, para_list)
envt.add_binding(id, obj)
return (UnspecObj(), False)
def ext_repr(self):
return "#<Builtin Macro: define>"
def __str__(self):
return self.ext_repr()
class _builtin_set(SpecialOptObj):
def prepare(self, pc):
# TODO: check number of arguments
pc = pc.chd
pc.skip = True # Skip the identifier
pc.sib.skip = False
def call(self, arg_list, pc, envt, cont):
id = pc.chd.obj
if envt.has_obj(id):
envt.add_binding(id, arg_list[0])
return (UnspecObj(), False)
def ext_repr(self):
return "#<Builtin Macro: set!>"
def __str__(self):
return self.ext_repr()
class Tokenizor():
def __init__(self):
self.data = "" # string buffer
self.tokenized = list() # tokens
def feed(self, data): # Store the data in the buffer
self.data = data
def read(self):
if len(self.tokenized) == 0: # no tokens available, let's produce
if len(self.data) == 0: # I'm hungry, feed me!
return None
self.tokenized = self.data.replace('(', '( ')\
.replace(')', ' )')\
.split()
self.data = "" # Clear the buffer
if len(self.tokenized) == 0: # You feed me with the air, bastard!
return None
return self.tokenized.pop(0)
class Node(object): # AST Node
def __init__(self, obj, sib):
self.obj = obj
self.sib = sib
self.skip = None