# Python Lisp: Common Lisp in Python 3.3.0!
# @author Shinobi N.
macros = ["if", "define", "defun", "setf", "let"]
_globals = {}
func_locals = [{}]
from fractions import Fraction
class Function(object):
def __init__(self, name, parameters, body):
self.name = name
self.params = parameters
self.body = body
def execute(self, arguments):
self.saved_body = str(self.body)
global func_locals
func_locals.append({})
i = 0
for p in self.params:
func_locals[-1][p] = arguments[i]
i += 1
for line in self.body:
result = evaluate_lisp(line)
self.body = eval(self.saved_body)
func_locals.pop()
return result
def find_matching_close_paren(lst, start=0):
i = start
open_parens = 0
while i < len(lst):
if lst[i] == "(":
open_parens += 1
if lst[i] == ")":
open_parens -= 1
if open_parens < 0:
return i
i += 1
return -1
def eval_parens(lst):
i = 0
while i < len(lst):
item = lst[i]
if item == "(" and lst[i + 1] not in macros:
end = find_matching_close_paren(lst, i + 1)
lst[i:end + 1] = [eval_function(lst[i + 1], get_args(lst, i + 2))]
elif item == "(":
end = find_matching_close_paren(lst, i + 1)
lst[i:end + 1] = [eval_macro(lst[i + 1], get_raw_args(lst, i + 2))]
elif item == "/":
lst[i - 1:i + 2] = [Fraction(evaluate(lst[i - 1]), evaluate(lst[i + 1]))]
i += 1
return lst
def eval_expr(lst, index):
index += 1
i = 0
last_i = 0
while i < len(lst):
item = lst[i]
if item == "(":
last_i = i
i = find_matching_close_paren(lst, i + 1)
index -= 1
if index == 0 and item == "(":
if lst[last_i + 1] not in macros:
args = get_args(lst, last_i + 2)
i = 0
while i < len(args):
args[i] = evaluate(args[i])
i += 1
return eval_function(lst[last_i + 1], args)
else:
return eval_macro(lst[last_i + 1], get_raw_args(lst, last_i + 2))
elif index == 0:
return item
i += 1
return None
def get_expr(lst, index):
index += 1
i = 0
last_i = 0
while i < len(lst):
item = lst[i]
if item == "(":
last_i = i
i = find_matching_close_paren(lst, i + 1)
index -= 1
if index == 0 and item == "(":
return lst[last_i + 1:i]
elif index == 0:
return item
i += 1
return None
def get_raw_args(tokens, i):
end = find_matching_close_paren(tokens, i)
args = tokens[i:end]
return args
def get_args(tokens, i):
end = find_matching_close_paren(tokens, i)
args = tokens[i:end]
args = eval_parens(args)
i = 0
while i < len(args):
args[i] = evaluate(args[i])
i += 1
return args
def to_boolean(value):
if type(value) == bool:
return value
if value == []:
return False
return bool(value)
def to_lisp_boolean(value):
if value is False:
return []
return value
def eval_function(func_name, args):
global _globals
global func_locals
i = 0
while i < len(args):
args[i] = evaluate(args[i])
i += 1
if func_name == "+":
for arg in args:
if type(arg) is str:
concat = ""
for arg in args:
concat += arg
return concat
return sum(args)
elif func_name == "*":
product = 1
for arg in args:
product *= arg
return product
elif func_name == "/":
if type(args[0]) is int and type(args[1]) is int:
return Fraction(args[0], args[1])
return args[0] / args[1]
elif func_name == "-":
if len(args) == 1:
return -args[0]
return args[0] - args[1]
elif func_name == "%":
return args[0] % args[1]
elif func_name == "1+":
return args[0] + 1
elif func_name == "-1+" or func_name == "1-":
return args[0] - 1
elif func_name == "upper":
return args[0].upper()
elif func_name == "lower":
return args[0].lower()
elif func_name == "reverse":
if type(args[0]) is list:
new_list = []
i = len(args[0]) - 1
while i >= 0:
new_list.append(args[0][i])
i -= 1
return new_list
elif type(args[0]) is str:
new_str = ""
i = len(args[0]) - 1
while i >= 0:
new_str += args[0][i]
i -= 1
return new_str
elif func_name in ["pow", "**", "exp"]:
result = args[0]
for arg in args[1:]:
result **= arg
return result
elif func_name == "=":
return [] if args[0] != args[1] else True
elif func_name == "!=":
return True if args[0] != args[1] else []
elif func_name == ">":
return True if args[0] > args[1] else []
elif func_name == "<":
return True if args[0] < args[1] else []
elif func_name == ">=":
return True if args[0] >= args[1] else []
elif func_name == "<=":
return True if args[0] <= args[1] else []
elif func_name == "and":
return to_lisp_boolean(to_boolean(args[0]) and to_boolean(args[1]))
elif func_name == "or":
return to_lisp_boolean(to_boolean(args[0]) or to_boolean(args[1]))
elif func_name == "not":
return to_lisp_boolean(not to_boolean(args[0]))
elif func_name == "xor":
bool1 = to_boolean(args[0])
bool2 = to_boolean(args[1])
return to_lisp_boolean((bool1 and not bool2) or (not bool1 and bool2))
elif func_name == "length":
return len(args[0])
elif func_name == "sqrt":
from math import sqrt
return sqrt(args[0])
elif func_name == "numberp":
return to_lisp_boolean(type(args[0]) is int or type(args[0]) is float)
elif func_name == "stringp":
return to_lisp_boolean(type(args[0]) is str)
elif func_name == "evenp":
return to_lisp_boolean((args[0] % 2) == 0)
elif func_name == "oddp":
return to_lisp_boolean((args[0] % 2) != 0)
elif func_name == "ln":
from math import log
return log(args[0])
elif func_name == "log":
from math import log
return log(args[0], args[1])
elif func_name == "sin":
from math import sin
return sin(args[0])
elif func_name == "cos":
from math import cos
return cos(args[0])
elif func_name == "block" or func_name == "progn":
return args[-1]
elif func_name == "list":
return args
elif func_name == "cons":
return [args[0]] + args[1]
elif func_name == "car" or func_name == "first":
return args[0][0]
elif func_name == "cdr" or func_name == "rest":
return args[0][1:]
elif func_name == "append":
result = []
for item in args:
result += item
return result
elif func_name in _globals.keys() and type(_globals[func_name]) is Function:
return _globals[func_name].execute(args)
else:
if func_name in macros:
return eval_macro(func_name, args)
print("Undefined function " + func_name)
return
def evaluate(token, _locals=None):
if type(token) is not str:
return token
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
if token.startswith('"') and token.endswith('"'):
return token[1:-1]
global _globals
global func_locals
if token in _globals.keys():
return _globals[token]
if _locals is not None and token in _locals.keys():
return _locals[token]
i = len(func_locals) - 1
while i >= 0:
frame = func_locals[i]
if token in frame.keys():
return frame[token]
i -= 1
return token
def eval_macro(macro_name, raw_args):
global _globals
if macro_name == "if":
if to_boolean(evaluate(eval_expr(raw_args, 0))):
return eval_expr(raw_args, 1)
else:
return eval_expr(raw_args, 2)
elif macro_name == "setf":
result = evaluate(eval_expr(raw_args, 1))
_globals[eval_expr(raw_args, 0)] = result
return result
elif macro_name == "let":
_locals = {}
declarations = get_expr(raw_args, 0)
i = 0
declaration = 1
while True:
declaration = get_expr(declarations, i)
if declaration is None:
break
_locals[get_expr(declaration, 0)] = eval_expr(declaration, 1)
i += 1
i = 1
expr = 1
j = 0
while j < len(raw_args):
raw_args[j] = evaluate(raw_args[j], _locals)
j += 1
while expr is not None:
last_expr = expr
expr = eval_expr(raw_args, i)
i += 1
return last_expr
elif macro_name == "defun" or macro_name == "define":
func_name = get_expr(raw_args, 0)
parameters = get_expr(raw_args, 1)
i = 2
body = []
next_line = ""
while True:
next_line = get_expr(raw_args, i)
if next_line is None:
break
if type(next_line) is list:
body.append(['('] + next_line + [')'])
else:
body.append(next_line)
i += 1
_globals[func_name] = Function(func_name, parameters, body)
return func_name
def split_on_no_quotes(line, operators):
def interleave_quotes(lst):
new_list = []
odd = False
for each_elem in lst:
if odd:
new_list.append('"' + each_elem + '"')
else:
new_list.append(each_elem)
odd = not odd
return new_list
lst = line.split('"')
lst = interleave_quotes(lst)
new_list = []
for each_elem in lst:
if '"' in each_elem:
new_list.append(each_elem)
else:
for each_operator in operators:
each_elem = each_elem.replace(each_operator, " " + each_operator + " ")
new_list += each_elem.split()
return new_list
def evaluate_lisp(code):
if type(code) is str:
operators = ["(", ")", "/"]
tokens = split_on_no_quotes(code, operators)
else:
tokens = code
i = 0
simplification_level = 0
simplification_limit = 10000
while type(tokens) is list and len(tokens) > 1:
if simplification_level > simplification_limit:
return "Error: Unable to simplify exp[b][/b]ression " + str(tokens)
simplification_level += 1
token = tokens[i]
if token == "(" and tokens[i + 1] not in macros:
end = find_matching_close_paren(tokens, i + 1)
tokens[i:end + 1] = [eval_function(tokens[i + 1], get_args(tokens, i + 2))]
elif token == "(":
end = find_matching_close_paren(tokens, i + 1)
tokens[i:end + 1] = [eval_macro(tokens[i + 1], get_raw_args(tokens, i + 2))]
elif token == "/":
tokens[i - 1:i + 2] = [Fraction(evaluate(tokens[i - 1]), evaluate(tokens[i + 1]))]
i += 1
if i >= len(tokens):
i = 0
return evaluate(tokens[0]) if type(tokens) is list else evaluate(tokens)
def read_eval_print_loop():
while True:
command = input("PyLisp> ")
result = None
lines = command.split(";")
for line in lines:
if len(command) > 0:
result = evaluate_lisp(line)
if result is True:
print('T')
elif result == []:
print('NIL')
elif type(result) == list:
i = 0
print('(', end="")
for item in result:
print(item, end="")
if i != len(result) - 1:
print(' ', end="")
i += 1
print(')')
elif result is not None:
print(result)
read_eval_print_loop()