...
 
Commits (7)
......@@ -11,7 +11,7 @@ all:
@echo
test:
./pyle -e tests.l < data.l
./pyle -se tests.l < data.l
tags: *.py
etags *.py *.l
......
;;; base macros and functions for pyle
(defmacro defun (symbol args . bodyforms)
"Define a function named SYMBOL with ARGS list and BODYFORMS."
(let ((funcsym (gensym)))
`(let ((,funcsym (lambda ,args ,@bodyforms)))
(putprop ',symbol ,funcsym 'sys:function)
(set-function-name ,funcsym ',symbol))))
;; (defun car (l)
;; (let (((first) l))
;; first))
;; (defun cdr (l)
;; (let (((first . rest) l))
;; rest))
(defmacro setq (var value)
`(set ',var ,value))
......@@ -119,13 +134,6 @@ symbol definition chain explicitly."
(defmacro symbol-name (sym)
`(string ,sym))
(defmacro defun (symbol args . bodyforms)
"Define a function named SYMBOL with ARGS list and BODYFORMS."
(let ((funcsym (gensym)))
`(let ((,funcsym (lambda ,args ,@bodyforms)))
(putprop ',symbol ,funcsym 'sys:function)
(set-function-name ,funcsym ',symbol))))
(defmacro dolist ((var listform resultform) . bodyforms)
"Iterate over LISTFORM, binding symbol VAR to each element in turn.
Return the value of RESULTFORM (if specified) or nil."
......@@ -198,14 +206,12 @@ FROM, TO, STEP, and TEST are evaluated just once."
(defmacro incf (place . rest)
"Increment PLACE by optional DELTA (or 1) and return the new value."
(let (((delta) rest))
(if delta
`(setf ,place (+ ,place ,delta))
`(setf ,place (1+ ,place)))))
(let ((delta (or (car rest) 1)))
`(setf ,place (+ ,place ,delta))))
(defmacro decf (place . rest)
"Decrement place by optional DELTA (or 1) and return the new value."
(let (((delta) rest))
(let ((delta (car rest)))
(if delta
`(setf ,place (- ,place ,delta))
`(setf ,place (1- ,place)))))
......@@ -479,9 +485,11 @@ bigger than the previous one by optional STEP (default 1)."
(defun list-length (l)
"Return the length of list L."
(if (null l)
0
(+ 1 (list-length (cdr l)))))
(let ((len 0))
(while l
(pop l)
(incf len))
len))
(defun length (ob)
"Return the length of string or list OB."
......
......@@ -322,8 +322,8 @@ def Pyle_let(arglist):
savedEnv = enterEnvironment()
try:
bindings, bodyforms = arglist.cxr()
dbg.p(dbg.let, "bindings:", bindings)
dbg.p(dbg.let, "body:", bodyforms)
# dbg.p(dbg.let, "bindings:", bindings)
# dbg.p(dbg.let, "body:", bodyforms)
params = ListCollector()
args = ListCollector()
for binding in bindings:
......@@ -352,8 +352,8 @@ def Pyle_letrec(arglist):
savedEnv = enterEnvironment()
try:
bindings, bodyforms = arglist.cxr()
dbg.p(dbg.let, "bindings:", bindings)
dbg.p(dbg.let, "body:", bodyforms)
# dbg.p(dbg.let, "bindings:", bindings)
# dbg.p(dbg.let, "body:", bodyforms)
params = ListCollector() # need them in proper order
args = ListCollector()
for binding in bindings:
......@@ -397,6 +397,13 @@ def Pyle_macroexpand(arglist):
"""
return macroexpandForm(arglist.car())
def Pyle_macroexpand1(arglist):
"""@desc args: EXPR; result: expanded form
Expand one layer of macro calls in EXPR and return the expanded form.
"""
exp, hasepanded = expandFormRecurse(arglist.car())
return exp
def Pyle_minus(arglist):
"""@desc name: -; args: NUMBER1 ...; result: subtraction
Subtract the number arguments from the first.
......@@ -527,7 +534,7 @@ def Pyle_read(arglist):
else:
port = sys.stdin
fname = "*stdin*"
rdr = reader.Reader(port, fname)
rdr = reader.Reader(port, fname, None)
obj = rdr.read()
rdr.close()
return obj
......@@ -738,7 +745,7 @@ def make_builtin(func):
builtin_func = Builtin(builtin_name, func, minargs, maxargs, isspecial)
sym = intern(builtin_name)
sym.setFunction(builtin_func)
dbg.p(dbg.bi, "builtin", sym, minargs, maxargs, sym.getFunction())
# dbg.p(dbg.bi, "builtin", sym, minargs, maxargs, sym.getFunction())
# must copy this to avoid "RuntimeError: dictionary changed size
# during iteration" :-(
......
......@@ -12,14 +12,14 @@ import dep
from utils import *
def evalProgn(oblist):
dbg.p(dbg.eval, "evalprogn", oblist)
# dbg.p(dbg.eval, "evalprogn", oblist)
result = Nil
for ob in oblist:
result = Eval(ob)
return result
def evalFun(funob):
dbg.p(dbg.eval, "evalFun", funob)
# dbg.p(dbg.eval, "evalFun", funob)
if funob.isSymbol():
func = funob.getFunction()
if func:
......@@ -31,13 +31,13 @@ def evalFun(funob):
raise PyleNoFunctionError(funob)
def evalArgs(oblist):
dbg.p(dbg.eval, "evalargs", oblist)
# dbg.p(dbg.eval, "evalargs", oblist)
length = 0
lc = ListCollector()
for ob in oblist:
length += 1
lc.add(Eval(ob))
dbg.p(dbg.eval, "evalArgs returns", lc.list(), length)
# dbg.p(dbg.eval, "evalArgs returns", lc.list(), length)
return lc.list(), length
level = 0
......@@ -50,8 +50,8 @@ def Eval(ob):
level += 1
envs.append(CurrentEnv)
try:
dbg.p(dbg.eval, "{l}Eval {o} in {e}".format(o=ob, e=CurrentEnv,
l=makeString(2 * level)))
# dbg.p(dbg.eval, "{l}Eval {o} in {e}".format(o=ob, e=CurrentEnv,
# l=makeString(2 * level)))
if ob.isSymbol():
result = ob.value()
elif ob.isPair():
......@@ -64,7 +64,7 @@ def Eval(ob):
result = function.call(arglist, length)
else:
result = ob
dbg.p(dbg.eval, "{l}<= {r}".format(r=result, l=makeString(2 * level)))
# dbg.p(dbg.eval, "{l}<= {r}".format(r=result, l=makeString(2 * level)))
return result
except PyleThrow as throw:
raise throw
......
......@@ -9,25 +9,26 @@ import dep
from object import *
from stdsyms import *
def expandList(list): # return new list, haveExpanded
dbg.p(dbg.mac, "expandList:", list)
def expandList(list, level): # return new list, haveExpanded
# dbg.p(dbg.mac, "expandList:", list)
haveExpanded = False
lc = ListCollector()
# cannot use simple iteration over the list, which would not
# handle improper lists
while list.isPair():
elem, did = expandFormRecurse(list.car())
elem, did = expandFormRecurse(list.car(), level)
lc.add(elem)
haveExpanded = haveExpanded or did
list = list.cdr()
if list: # last cdr of improper list
elem, did = expandFormRecurse(list)
elem, did = expandFormRecurse(list, level)
lc.addEnd(elem)
haveExpanded = haveExpanded or did
return lc.list(), haveExpanded
def expandFormRecurse(form): # return expanded form, haveExpanded
dbg.p(dbg.mac, "expandFormRecurse:", form)
def expandFormRecurse(form, level=""): # return expanded form, haveExpanded
# print(level, "expandFormRecurse:", form)
# dbg.p(dbg.mac, "expandFormRecurse:", form)
if form.isPair():
head = form.car()
if head.isSymbol():
......@@ -35,15 +36,15 @@ def expandFormRecurse(form): # return expanded form, haveExpanded
maybeMacro = head.getFunction()
if maybeMacro and maybeMacro.isMacro():
result = maybeMacro.expand(args), True
return result
else:
expargs, did = expandList(args)
return cons(head, expargs), did
expargs, did = expandList(args, level + " ")
result = cons(head, expargs), did
else:
result = expandList(form)
return result
result = expandList(form, level + " ")
else:
return form, False
result = form, False
# print(level, "=>", result)
return result
def macroexpandForm(form):
needExpand = True
......
......@@ -126,8 +126,8 @@ class Symbol(Object):
return Symbol(name)
def __init__(self, name, immutable=False, selfval=False):
dbg.p(dbg.sym, "init Symbol({n}, imm={i}, self={s})".
format(n=name, i=immutable, s=selfval))
# dbg.p(dbg.sym, "init Symbol({n}, imm={i}, self={s})".
# format(n=name, i=immutable, s=selfval))
if name == NamePropSymbolName:
namesym = self
else:
......@@ -171,7 +171,7 @@ class Symbol(Object):
def bind(self, ob):
envbind(self, ob)
def boundp(self):
dbg.p(dbg.sym, "boundp", self, CurrentEnv.getvalOrNone(self))
# dbg.p(dbg.sym, "boundp", self, CurrentEnv.getvalOrNone(self))
return CurrentEnv.getvalOrNone(self) is not None
def value(self):
return envget(self)
......@@ -247,12 +247,11 @@ class Function(Object):
class Macro(Function):
def __init__(self, name, params, body):
self.env = CurrentEnv
self.name = name
self.params = params
self.minargs = params.lengthI()
self.maxargs = self.minargs if params.isProper() else None
dbg.p(dbg.form, name, "has minargs", self.minargs)
# dbg.p(dbg.form, name, "has minargs", self.minargs)
self.special = False
maybeDocstring, restBody = body.cxr()
......@@ -273,16 +272,16 @@ class Macro(Function):
raise PyleMacroCallError(self.name)
def expand(self, arglist):
self.checkArgs(arglist.length())
savedEnv = enterEnvironment(self.env)
savedEnv = enterEnvironment()
try:
bind_params(self.params, arglist, self.name)
dbg.p(dbg.call, "evalProgn in", CurrentEnv, self.body)
# dbg.p(dbg.call, "evalProgn in", CurrentEnv, self.body)
return dep.evalProgn(self.body)
finally:
backtoEnvironment(savedEnv)
def describe(self):
return p2l({ "type": self.__class__.__name__, "id": id(self),
"env": self.env, "name": self.name, "params": self.params,
"name": self.name, "params": self.params,
"minargs": Number(self.minargs),
"maxargs": Number(self.maxargs),
"synopsis": self.synopsis(),
......@@ -292,7 +291,7 @@ class Macro(Function):
# this is used for parameter bindings on function calls and variable
# bindings in let/let*; destructuring binds will go here, too
def bind_params(params, args, function=None, needEval=False, strict=True):
dbg.p(dbg.bind, "bind_params:", params, args, function, needEval)
# dbg.p(dbg.bind, "bind_params:", params, args, function, needEval)
nargs = args.length() if args.isList() else 1
while params.isPair() and args.isPair():
param, params = params.cxr()
......@@ -304,7 +303,7 @@ def bind_params(params, args, function=None, needEval=False, strict=True):
if param.isSymbol():
if param is Nil:
continue # skip bindings if param symbol is nil
dbg.p(dbg.bind, "envbind:", param, arg)
# dbg.p(dbg.bind, "envbind:", param, arg)
envbind(param, arg)
elif param.isPair():
bind_params(param, arg, function, False, False)
......@@ -315,7 +314,7 @@ def bind_params(params, args, function=None, needEval=False, strict=True):
return # standard situation, proper params
# list, also with let and let*
elif params.isSymbol():
dbg.p(dbg.bind, "envbind:", params, Nil)
# dbg.p(dbg.bind, "envbind:", params, Nil)
envbind(params, Nil)
elif strict and function:
raise PyleArgCountError(function, nargs, function.minargs)
......@@ -338,7 +337,7 @@ def bind_params(params, args, function=None, needEval=False, strict=True):
# improper arameter list
if needEval:
args, _ = dep.evalList(args)
dbg.p(dbg.bind, "envbind:", params, args)
# dbg.p(dbg.bind, "envbind:", params, args)
envbind(params, args)
else:
assert False, "binding wtf 3? {f}: params {p}, args left {a}".\
......@@ -352,7 +351,7 @@ class Form(Function):
self.params = params
self.minargs = params.lengthI()
self.maxargs = self.minargs if params.isProper() else None
dbg.p(dbg.form, name, "has minargs", self.minargs)
# dbg.p(dbg.form, name, "has minargs", self.minargs)
self.special = False
maybeDocstring, restBody = body.cxr()
......@@ -375,7 +374,7 @@ class Form(Function):
savedEnv = enterEnvironment(self.env)
try:
bind_params(self.params, arglist)
dbg.p(dbg.call, "evalProgn in", CurrentEnv, self.body)
# dbg.p(dbg.call, "evalProgn in", CurrentEnv, self.body)
return dep.evalProgn(self.body)
finally:
backtoEnvironment(savedEnv)
......@@ -401,8 +400,8 @@ class Builtin(Function):
self._synopsis = doclines[0]
def call(self, arglist, nargs):
self.checkArgs(nargs)
dbg.p(dbg.call, "call {s} with {a} [{n}]".
format(s=self, a=arglist, n=nargs))
# dbg.p(dbg.call, "call {s} with {a} [{n}]".
# format(s=self, a=arglist, n=nargs))
return self.code(arglist)
def __str__(self):
return "#<{s}>".format(s=self.synopsis())
......@@ -490,14 +489,14 @@ def enterEnvironment(parent=None):
parent = CurrentEnv
savedEnv = CurrentEnv
CurrentEnv = Environment(parent)
dbg.p(dbg.env, "enter environment", CurrentEnv,
"saved", savedEnv, "parent", parent)
# dbg.p(dbg.env, "enter environment", CurrentEnv,
# "saved", savedEnv, "parent", parent)
return savedEnv
def backtoEnvironment(env):
global CurrentEnv
assert CurrentEnv != RootEnv
dbg.p(dbg.env, "drop environment", CurrentEnv, "back to", env)
# dbg.p(dbg.env, "drop environment", CurrentEnv, "back to", env)
CurrentEnv = env
def envbind(symbol, value, env=None):
......@@ -505,18 +504,18 @@ def envbind(symbol, value, env=None):
env = CurrentEnv
if symbol.immutable():
raise PyleModifyImmutableError(symbol)
dbg.p(dbg.env, "bind {s} <= {v}".format(s=symbol, v=value))
# dbg.p(dbg.env, "bind {s} <= {v}".format(s=symbol, v=value))
env.bind(symbol, value)
def envget(symbol, env=None):
global CurrentEnv
if not env:
env = CurrentEnv
dbg.p(dbg.env, "envget", symbol, env)
# dbg.p(dbg.env, "envget", symbol, env)
return env.getval(symbol)
def envset(symbol, value, env=None):
dbg.p(dbg.env, "envset", symbol, value, env)
# dbg.p(dbg.env, "envset", symbol, value, env)
if env is None:
env = CurrentEnv
if symbol.immutable():
......@@ -527,11 +526,11 @@ def intern(name, immutable=False, selfval=False):
assert str(name) != "None"
if name in symbolTable:
sym = symbolTable[name]
dbg.p(dbg.sym, "intern", name, "has", sym)
# dbg.p(dbg.sym, "intern", name, "has", sym)
else:
sym = Symbol(name, immutable, selfval)
symbolTable[name] = sym
dbg.p(dbg.sym, "intern new", sym)
# dbg.p(dbg.sym, "intern new", sym)
return sym
def internSelf(name):
......@@ -607,7 +606,14 @@ class Pair(Object):
self._cdr = cdr
def dump(self):
return "#<{n}:{s}>".format(n=self.__class__.__name__, s=self)
def __repr__(self):
def __repr__(self, obset=None):
if obset is None:
# print("start anew with pair", id(self))
obset = set()
# if self in obset:
# # print("already seen pair", id(self))
# return "<...>"
obset.add(self)
result = ["("]
current = self
first = True
......@@ -617,7 +623,10 @@ class Pair(Object):
else:
result.append(" ")
el, current = current.cxr()
result.append(repr(el))
if el.isPair():
result.append(el.__repr__(obset))
else:
result.append(repr(el))
if current is not Nil:
result.append(" . ")
result.append(str(current))
......@@ -679,11 +688,11 @@ class ListCollector():
def __init__(self):
self._first = Nil
self._last = Nil
dbg.p(dbg.lc, self, "initialized with", self._first, self._last)
# dbg.p(dbg.lc, self, "initialized with", self._first, self._last)
def __repr__(self):
return "#<{n} {l}>".format(n=self.__class__.__name__, l=self._first)
def add(self, ob):
dbg.p(dbg.lc, "lc gets", ob)
# dbg.p(dbg.lc, "lc gets", ob)
newcons = cons(ob, Nil)
if self._first is Nil:
self._first = newcons
......@@ -694,14 +703,14 @@ class ListCollector():
"self._last._cdr is {0}".format(self._last._cdr)
self._last.rplacd(newcons)
self._last = newcons
dbg.p(dbg.lc, "lc now has", self._first)
# dbg.p(dbg.lc, "lc now has", self._first)
def addEnd(self, ob):
if self._first:
self._last.rplacd(ob)
else:
self._first = ob
def list(self):
dbg.p(dbg.lc, "lc returns", self._first)
# dbg.p(dbg.lc, "lc returns", self._first)
return self._first
def last(self):
return self._last
......@@ -716,7 +725,7 @@ def list(*args):
return lc.list()
def init_env():
dbg.p(dbg.env, "init env")
# dbg.p(dbg.env, "init env")
global RootEnv
RootEnv = Environment()
global CurrentEnv
......
......@@ -42,7 +42,7 @@ try:
except get_opts.OptionError as e:
sys.exit(program + ": " + str(e) + "\n" + usage)
dbg.p(dbg.main, sys.argv[0], "starting up")
# dbg.p(dbg.main, sys.argv[0], "starting up")
for flags in ovc.debugflags:
for flag in flags.split(","):
......@@ -67,11 +67,11 @@ for file in ovc.loadfiles:
if len(argv) > 0:
dbg.p(dbg.main, "args are", argv)
# dbg.p(dbg.main, "args are", argv)
for arg in argv:
dbg.p(dbg.main, "loading", arg)
# dbg.p(dbg.main, "loading", arg)
reader.loadFile(arg)
else:
dbg.p(dbg.main, "starting interactive repl...")
# dbg.p(dbg.main, "starting interactive repl...")
reader.repl(sys.stdin, "*stdin*", sys.stdout)
......@@ -22,7 +22,7 @@ suffixes = ['', '.l', str(base64.b64decode('Lmxpc3A='))]
def loadFile(fname, missing_ok=False):
try:
dbg.p(dbg.load, "try to load", fname)
# dbg.p(dbg.load, "try to load", fname)
with open(str(fname)) as f:
info("; loading", fname)
dep.repl(f, fname)
......@@ -56,27 +56,29 @@ def load(name, missing_ok=False):
return Nil
raise PyleFileNotFoundError(str(name), load=True)
def is_interactive(in_port, out_port):
return in_port.isatty() and out_port and out_port.isatty()
def repl(inp, fname, out=None):
reader = Reader(inp, fname)
dbg.p(dbg.repl, "start repl on", fname)
dep.interactive = inp.isatty() and out and out.isatty()
reader = Reader(inp, fname, out)
# dbg.p(dbg.repl, "start repl on", fname)
try:
while True:
if dep.interactive:
if reader.interactive:
print("> ", end='', file=out, flush=True)
ob = reader.read()
if ob is not None:
dbg.p(dbg.repl, "=> {o} ({t})".format(o=ob, t=type(ob)))
# dbg.p(dbg.repl, "=> {o} ({t})".format(o=ob, t=type(ob)))
try:
expr = macroexpandForm(ob)
dbg.p(dbg.repl, "exp {e} ({t})".
format(e=expr, t=type(expr)))
# dbg.p(dbg.repl, "exp {e} ({t})".
# format(e=expr, t=type(expr)))
value = Eval(expr)
if out:
print("\n" + repr(value), file=out)
else:
dbg.p(dbg.load, "value:", value)
# dbg.p(dbg.load, "value:", value)
pass
except PyleThrow as e:
print("\nERR throw without catch, tag: {t}; value: {v}".
format(t=repr(e.tag()), v=repr(e.value())))
......@@ -90,7 +92,7 @@ def repl(inp, fname, out=None):
if dep.exit_on_error:
sys.exit("exit due to error")
else:
if dep.interactive:
if reader.interactive:
print(file=out)
return
finally:
......@@ -175,9 +177,20 @@ class StringPort():
substr = self.string[self.position:newpos]
self.position = newpos
return substr
def readline(self):
result = ""
while True:
ch = self.read(1)
if not ch:
return result
result += ch
if ch == "\n":
return result
def isatty(self):
return False
class Reader():
def __init__(self, inp, fname):
def __init__(self, inp, fname, out):
self.inp = inp
self.fname = fname
self.unreadchar = None
......@@ -186,6 +199,7 @@ class Reader():
self.currentLine = None
self.lineLen = 0
self.linePos = 0
self.interactive = is_interactive(inp, out)
def close(self):
pass
def unread(self, ch):
......@@ -196,19 +210,18 @@ class Reader():
dbg.p(dbg.read, "push back token", token)
def getline(self):
"Get a new line; return truish iff there was one."
if dep.interactive:
if self.interactive:
try:
self.line = input()
line = input()
except EOFError:
return False
self.line += "\n"
line += "\n"
else:
self.line = self.inp.readline()
print(self.line)
self.currentLine = self.line
line = self.inp.readline()
self.currentLine = line
self.linePos = 0
self.lineLen = len(self.line)
return self.line
self.lineLen = len(line)
return line
def getchar(self):
if self.linePos >= self.lineLen:
hadOne = self.getline()
......@@ -270,7 +283,7 @@ class Reader():
result = QuasiquoteToken()
else:
result = self.collectAtom(ch)
dbg.p(dbg.read, "next token is", result)
# dbg.p(dbg.read, "next token is", result)
return result
def unquoteOrUnquoteSplicingToken(self):
ch = self.nextChar()
......@@ -336,10 +349,10 @@ class Reader():
lc = ListCollector()
while True:
t = self.nextToken()
dbg.p(dbg.read, "collectList sees", t)
# dbg.p(dbg.read, "collectList sees", t)
ttype = type(t)
if ttype is CparenToken:
dbg.p(dbg.read, "collectList done with", lc.list())
# dbg.p(dbg.read, "collectList done with", lc.list())
return lc.list()
if ttype is DotToken:
end = self.read()
......
......@@ -14,7 +14,7 @@
(exp (string ',expect))
(thename (string ',name)))
(setq testcount (1+ testcount))
(format t "{}: {} => {}\n" testcount thename value)
(format t "{}: {:20} => {}\n" testcount thename value)
;; (when (member thename test-names)
;; (format t "\nWARN test {} already seen\n" thename))
;; (push thename test-names)
......@@ -833,7 +833,7 @@
(setf b '(3 4 5 6 7))
;; (test incf0 (progn (incf (car b) 2)
;; b)
;; b)
;; (5 4 5 6 7))
;; (test decf0 (progn (decf (cddr b))
......