1. #!/usr/bin/python
  2.  
  3. ################ Lispy: Scheme Interpreter in Python
  4.  
  5. ## (c) Peter Norvig, 2010; See http://norvig.com/lispy.html
  6.  
  7. ################ Symbol, Env classes
  8.  
  9. from __future__ import division
  10.  
  11. Symbol = str
  12.  
  13. class Env(dict):
  14. "An environment: a dict of {'var':val} pairs, with an outer Env."
  15. def __init__(self, parms=(), args=(), outer=None):
  16. self.update(zip(parms,args))
  17. self.outer = outer
  18. def find(self, var):
  19. "Find the innermost Env where var appears."
  20. return self if var in self else self.outer.find(var)
  21. def depth(self):
  22. n = 0
  23. x = self.outer
  24. while x:
  25. n += 1
  26. x = x.outer
  27. return n
  28. def add_globals(env):
  29. "Add some Scheme standard procedures to an environment."
  30. import math, operator as op
  31. env.update(vars(math)) # sin, sqrt, ...
  32. env.update(
  33. {'say': lambda x: say(x), 'quit' : goodbye,
  34. '+':op.add, '-':op.sub, '*':op.mul, '/':op.div, 'not':op.not_,
  35. '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
  36. 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':lambda x,y:[x]+y,
  37. 'car':lambda x:x[0],'cdr':lambda x:x[1:], 'append':op.add,
  38. 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list),
  39. 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol)})
  40. return env
  41.  
  42. def say(x): print x
  43. def goodbye(): print ";; Bye."; quit()
  44.  
  45. global_env = add_globals(Env())
  46.  
  47. isa = isinstance
  48.  
  49. ################ eval
  50.  
  51. def eval(x, env=global_env,lvl=0):
  52. "Evaluate an expression in an environment."
  53. this = x[0] if isa(x,list) else x
  54. print "|.. " * lvl + str(this)
  55. if isa(x, Symbol): # variable reference
  56. return env.find(x)[x]
  57. elif not isa(x, list): # constant literal
  58. return x
  59. elif x[0] == 'load':
  60. tmp=eval(x[1],env,lvl+1)
  61. return eload(tmp)
  62. elif x[0] == 'quote' or x[0] == "'":
  63. (_, exp) = x
  64. return exp
  65. elif x[0] == 'if': # (if test conseq alt)
  66. (_, test, conseq, alt) = x
  67. return eval((conseq if eval(test, env) else alt), env,lvl+1)
  68. elif x[0] == 'set!': # (set! var exp)
  69. (_, var, exp) = x
  70. env.find(var)[var] = eval(exp, env,lvl+1)
  71. elif x[0] == 'define': # (define var exp)
  72. (_, var, exp) = x
  73. env[var] = eval(exp, env,lvl+1)
  74. elif x[0] == 'lambda': # (lambda (var*) exp)
  75. (_, vars, exp) = x
  76. return lambda *args: eval(exp, Env(vars, args, env),lvl+1)
  77. elif x[0] == 'begin': # (begin exp*)
  78. for exp in x[1:]:
  79. val = eval(exp, env,lvl+1)
  80. return val
  81. else: # (proc exp*)
  82. exps = [eval(exp, env,lvl+1) for exp in x]
  83. proc = exps.pop(0)
  84. #print ">calling", proc
  85. return proc(*exps)
  86.  
  87. ################ parse, read, and user interaction
  88.  
  89. def read(s):
  90. "Read a Scheme expression from a string."
  91. return read_from(tokenize(s))
  92.  
  93. parse = read
  94.  
  95. def tokenize(s):
  96. "Convert a string into a list of tokens."
  97. return s.replace('(',' ( ').replace(')',' ) ').split()
  98.  
  99. def read_from(tokens):
  100. "Read an expression from a sequence of tokens."
  101. if len(tokens) == 0:
  102. raise SyntaxError('unexpected EOF while reading')
  103. token = tokens.pop(0)
  104. if '(' == token:
  105. L = []
  106. while tokens[0] != ')':
  107. L.append(read_from(tokens))
  108. tokens.pop(0) # pop off ')'
  109. return L
  110. elif ')' == token:
  111. raise SyntaxError('unexpected )')
  112. else:
  113. return atom(token)
  114.  
  115. def atom(token):
  116. "Numbers become numbers; every other token is a symbol."
  117. try: return int(token)
  118. except ValueError:
  119. try: return float(token)
  120. except ValueError:
  121. return Symbol(token)
  122.  
  123. def to_string(exp):
  124. "Convert a Python object back into a Lisp-readable string."
  125. return '('+' '.join(map(to_string, exp))+')' if isa(exp, list) else str(exp)
  126.  
  127. def repl(prompt='lis.py> '):
  128. "A prompt-read-eval-print loop."
  129. print ";; LITHP ITH LITHTENING ...(v0.1)"
  130. while True:
  131. val = eval(parse(raw_input(prompt)))
  132. if val is not None: print to_string(val)
  133.  
  134. import string
  135. def sexp(s) :
  136. level,keep = 0,""
  137. while s:
  138. if s[0] == ";":
  139. while s and s[0] != "\n": s=s[1:]
  140. if not s: break
  141. if s[0] == "(": level += 1
  142. if level > 0 : keep += s[0]
  143. if s[0] == ")":
  144. level -= 1
  145. if level==0:
  146. yield keep
  147. keep=""
  148. s = s[1:]
  149. if keep:
  150. yield keep
  151.  
  152.  
  153. def eload(f) :
  154. with open(f) as contents:
  155. code = contents.read()
  156. for part in sexp(code):
  157. eval(parse(part))
  158.  
  159. import sys
  160. if len(sys.argv) > 1:
  161. eload(sys.argv[1])
  162. else:
  163. repl()
  164. quit()