Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

converted to python3 #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/py/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ def __init__(self, d):

def __eq__(self, rhs):
if isinstance(rhs, Atom):
return (self.data == rhs.data)
return self.data == rhs.data
else:
return False


#### Symbols

# The symbol was the basic atom in Lisp 1 and served as the basic unit of data. In his early
Expand All @@ -44,6 +45,7 @@ def __hash__(self):
def eval(self, env, args=None):
return env.get(self.data)


#### Truth

# The first symbol created is `t`, corresponding to logical true. It's a little unclear to me
Expand Down Expand Up @@ -82,7 +84,9 @@ def eval(self, env, args=None):
# I originally added the ability to `combine` strings and symbols, but I might pull that back.
def cons(self, e):
if e.__class__ != self.__class__ and e.__class__ != Symbol.__class__:
raise UnimplementedFunctionError("Cannot cons a string and a ", e.__class__.__name__)
raise UnimplementedFunctionError(
"Cannot cons a string and a ", e.__class__.__name__
)

return String(e.data + self.data)

Expand Down
26 changes: 13 additions & 13 deletions src/py/env.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# The `Environment` class represents the dynamic environment of McCarthy's original Lisp. The creation of
# this class is actually an interesting story. As many of you probably know, [Paul Graham wrote a paper and
# code for McCarthy's original Lisp](http://www.paulgraham.com/rootsoflisp.html) and it was my first exposure to
# The `Environment` class represents the dynamic environment of McCarthy's original Lisp. The creation of
# this class is actually an interesting story. As many of you probably know, [Paul Graham wrote a paper and
# code for McCarthy's original Lisp](http://www.paulgraham.com/rootsoflisp.html) and it was my first exposure to
# the stark simplicity of the language. The simplicity is breath-taking!
#
# However, while playing around with the code I found that in using the core functions (i.e. `null.`, `not.`, etc.)
# I was not experiencing the full effect of the original. That is, the original Lisp was dynamically scoped, but
# the Common Lisp used to implement and run (CLisp in the latter case) Graham's code was lexically scoped. Therefore,
# However, while playing around with the code I found that in using the core functions (i.e. `null.`, `not.`, etc.)
# I was not experiencing the full effect of the original. That is, the original Lisp was dynamically scoped, but
# the Common Lisp used to implement and run (CLisp in the latter case) Graham's code was lexically scoped. Therefore,
# by attempting to write high-level functions using only the magnificent 7 and Graham's core functions in the Common Lisp
# I was taking advantage of lexical scope; something not available to McCarthy and company. Of course, the whole reason
# that Graham wrote `eval.` was to enforce dynamic scoping (he used a list of symbol-value pairs where the dynamic variables
# were added to its front when introduced). However, that was extremely cumbersome to use:
#
#
# (eval. 'a '((a 1) (a 2)))
# ;=> 1
#
# So I then implemented a simple REPL in Common Lisp that fed input into `eval.` and maintained the current environment list.
# That was fun, but I wasn't sure that I was learning anything at all. Therefore, years later I came across the simple
# REPL and decided to try to implement my own core environment for the magnificent 7 to truly get a feel for what it took
# to build a simple language up from scratch. I suppose if I were a real manly guy then I would have found an IBM 704, but
# to build a simple language up from scratch. I suppose if I were a real manly guy then I would have found an IBM 704, but
# that would be totally insane. (email me if you have one that you'd like to sell for cheap)
#
# Anyway, the point of this is that I needed to start with creating an `Environment` that provided dynamic scoping, and the
Expand Down Expand Up @@ -51,7 +51,7 @@ def set(self, key, value):
if key in self.binds:
self.binds[key] = value
elif self.parent:
self.parent.set(key,value)
self.parent.set(key, value)
else:
self.binds[key] = value

Expand All @@ -60,7 +60,7 @@ def definedp(self, key):
return True

return False

# Push a new binding by creating a new Env
#
# Dynamic scope works like a stack. Whenever a variable is created it's binding is pushed onto a
Expand All @@ -69,7 +69,7 @@ def definedp(self, key):
#
# (label a nil)
# (label frobnicate (lambda () (cons a nil)))
#
#
# ((lambda (a)
# (frobnicate))
# (quote x))
Expand All @@ -82,7 +82,7 @@ def definedp(self, key):
# | ------- |
# | a = nil |
# +---------+
#
#
# Meaning that when accessing `a`, `frobnicate` will get the binding at the top of the stack, producing the result `(x)`. This push/pop
# can become difficult, so people have to do all kinds of tricks to avoid confusion (i.e. pseudo-namespace via variable naming schemes).
#
Expand All @@ -92,7 +92,7 @@ def push(self, bnd=None):
def pop(self):
return self.parent

def __repr__( self):
def __repr__(self):
ret = "\nEnvironment %s:\n" % self.level
keys = [i for i in self.binds.keys() if not i[:2] == "__"]

Expand Down
8 changes: 7 additions & 1 deletion src/py/error.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# I one day plan to create a whole battery of errors so that the REPL provides a detailed report whenever
# something goes wrong. That day is not now.


class Error(Exception):
"""Base class for exceptions in this module."""

pass


class UnimplementedFunctionError(Error):
def __init__(self, message, thing):
self.thing = thing
Expand All @@ -13,11 +16,14 @@ def __init__(self, message, thing):
def __str__(self):
return self.message + repr(self.thing)


class EvaluationError(Error):
def __init__(self, env, args, message):
self.env = env
self.args = args
self.message = message

def __str__(self):
return self.message + ", " + repr(self.args) + " in environment " + self.env.level
return (
self.message + ", " + repr(self.args) + " in environment " + self.env.level
)
16 changes: 12 additions & 4 deletions src/py/fun.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ def __init__(self, fn):
self.fn = fn
self.hint = "fun"

def __repr__( self):
def __repr__(self):
return "<built-in function %s>" % id(self.fn)

# Evaluation just delegates out to the builtin.
def eval(self, env, args):
return self.fn(env, args)


# &lambda; &lambda; &lambda;

# The real power of McCarthy's Lisp srpings from Alonzo Chruch's &lambda;-calculus.
Expand All @@ -26,7 +27,7 @@ def __init__(self, n, b):
# The names that occur in the arg list of the lambda are bound (or dummy) variables
self.names = n
# Unlike the builtin functions, lambdas have arbitrary bodies
self.body = b
self.body = b

def __repr__(self):
return "<lambda %s>" % id(self)
Expand All @@ -47,7 +48,9 @@ def push_bindings(self, containing_env, values):
# The bindings are set one by one corresponding to the input values.
def set_bindings(self, containing_env, values):
for i in range(len(values)):
containing_env.environment.binds[self.names[i].data] = values[i].eval(containing_env.environment)
containing_env.environment.binds[self.names[i].data] = values[i].eval(
containing_env.environment
)

# The evaluation of a lambda is not much more complicated than a builtin function, except that it will
# establish bindings in the root context. Additionally, the root context will hold all bindings, so free
Expand All @@ -56,7 +59,11 @@ def eval(self, env, args):
values = [a for a in args]

if len(values) != len(self.names):
raise ValueError("Wrong number of arguments, expected {0}, got {1}".format(len(self.names), len(args)))
raise ValueError(
"Wrong number of arguments, expected {0}, got {1}".format(
len(self.names), len(args)
)
)

# Dynamic scope requires that names be bound on the global environment stack ...
LITHP = env.get("__lithp__")
Expand All @@ -73,6 +80,7 @@ def eval(self, env, args):
LITHP.pop()
return ret


# Closures

# McCarthy's Lisp does not define closures and in fact the precense of closures in the context of a pervasive dynamic
Expand Down
2 changes: 1 addition & 1 deletion src/py/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Eval:
def eval(self, environment, args=None):
raise EvaluationError(environment, args, "Evaluation error")


# I read Henry Baker's paper *Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same*
# and got a wild hair about `egal`. However, it turns out that in McCarthy's Lisp the idea is trivial to the extreme. Oh well...
# it's still a great paper. [Clojure](http://clojure.org)'s creator Rich Hickey summarizes `egal` much more succinctly than I ever could:
Expand All @@ -18,4 +19,3 @@ def eval(self, environment, args=None):
class Egal:
def __eq__(self, rhs):
raise UnimplementedFunctionError("Function not yet implemented", rhs)

Loading