literature, attchment material, general comments
i've added some literature that seems to be interesting. furthermore, i've updated the comments about what's actually working.
This commit is contained in:
parent
a6a66503b9
commit
ccfa109b06
Binary file not shown.
|
@ -0,0 +1,77 @@
|
|||
Quelle: http://www.cs.cmu.edu/Groups/AI/html/faqs/lang/lisp/part1/faq-doc-6.html
|
||||
Abruf: 04.08.2019
|
||||
-----------------------------------------------------------------------------
|
||||
[1-5] What is the "minimal" set of primitives needed for a Lisp interpreter?
|
||||
|
||||
Many Lisp functions can be defined in terms of other Lisp functions.
|
||||
For example, CAAR can be defined in terms of CAR as
|
||||
(defun caar (list) (car (car list)))
|
||||
It is then natural to ask whether there is a "minimal" or smallest set
|
||||
of primitives necessary to implement the language.
|
||||
|
||||
There is no single "best" minimal set of primitives; it all depends on
|
||||
the implementation. For example, even something as basic as numbers
|
||||
need not be primitive, and can be represented as lists. One possible
|
||||
set of primitives might include CAR, CDR, and CONS for manipulation of
|
||||
S-expressions, READ and PRINT for the input/output of S-expressions
|
||||
and APPLY and EVAL for the guts of an interpreter. But then you might
|
||||
want to add LAMBDA for functions, EQ for equality, COND for
|
||||
conditionals, SET for assignment, and DEFUN for definitions. QUOTE
|
||||
might come in handy as well. If you add more specialized datatypes,
|
||||
such as integers, floats, arrays, characters, and structures, you'll
|
||||
need to add primitives to construct and access each.
|
||||
|
||||
AWKLisp is a Lisp interpreter written in awk, available by anonymous
|
||||
ftp from ftp.cs.cmu.edu:/user/ai/lang/lisp/impl/awk/. It has thirteen
|
||||
built-in functions: CAR, CDR, CONS, EQ, ATOM, SET, EVAL, ERROR, QUOTE,
|
||||
COND, AND, OR, LIST.
|
||||
|
||||
A more practical notion of a "minimal" set of primitives might be to
|
||||
look at the implementation of Scheme. While many Scheme functions can
|
||||
be derived from others, the language is much smaller than Common Lisp.
|
||||
See Dybvig's PhD thesis,
|
||||
R. Kent Dybvig, "Three Implementation Models for Scheme", Department
|
||||
of Computer Science Technical Report #87-011, University of North
|
||||
Carolina at Chapel Hill, Chapel Hill, North Carolina, April 1987.
|
||||
for a justification of a particularly practical minimal set of
|
||||
primitives for Scheme.
|
||||
|
||||
In a language like Common Lisp, however, there are a lot of low-level
|
||||
primitive functions that cannot be written in terms of the others,
|
||||
such as GET-UNIVERSAL-TIME, READ-CHAR, WRITE-CHAR, OPEN, and CLOSE,
|
||||
for starters. Moreover, real Common Lisp implementations are often
|
||||
built upon primitives that aren't part of the language, per se, and
|
||||
certainly not intended to be user-accessible, such as SYS:%POINTER-REF.
|
||||
|
||||
Beside the references listed in [1-4], some other relevant references
|
||||
include:
|
||||
|
||||
McCarthy, John, "Recursive Functions of Symbolic Expressions and
|
||||
their Computation by Machine, Part I", CACM 3(4):185-195, April 1960.
|
||||
[Defines five elementary functions on s-expressions.]
|
||||
|
||||
McCarthy, John, "A Micro-Manual for Lisp -- not the whole Truth",
|
||||
ACM SIGPLAN Notices, 13(8):215-216, August 1978.
|
||||
[Defines the Lisp programming language in 10 rules and gives
|
||||
a small interpreter (eval) written in this Lisp.]
|
||||
|
||||
McCarthy, John, et al., "LISP 1.5 Programmer's Manual", 2nd edition,
|
||||
MIT Press, 1965, ISBN 0-262-13011-4 (paperback).
|
||||
[Gives five basic functions, CAR, CDR, CONS, EQ, and ATOM.
|
||||
Using composition, conditional expressions (COND), and
|
||||
recursion, LAMBDA, and QUOTE, these basic functions may be used
|
||||
to construct the entire class of computable functions of
|
||||
S-expressions. Gives the functions EVAL and APPLY in
|
||||
M-expression syntax.]
|
||||
|
||||
Abelson and Sussman's SICP, especially chapters 4 and 5 on the
|
||||
implementation of meta-circular and explicit-control evaluators.
|
||||
|
||||
Steele and Gabriel's "The Evolution of LISP".
|
||||
|
||||
Go Back Up
|
||||
|
||||
Go To Previous
|
||||
|
||||
Go To Next
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
################ Lispy: Scheme Interpreter in Python
|
||||
|
||||
## (c) Peter Norvig, 2010-16; See http://norvig.com/lispy.html
|
||||
|
||||
from __future__ import division
|
||||
import math
|
||||
import operator as op
|
||||
|
||||
################ Types
|
||||
|
||||
Symbol = str # A Lisp Symbol is implemented as a Python str
|
||||
List = list # A Lisp List is implemented as a Python list
|
||||
Number = (int, float) # A Lisp Number is implemented as a Python int or float
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
def parse(program):
|
||||
"Read a Scheme expression from a string."
|
||||
return read_from_tokens(tokenize(program))
|
||||
|
||||
def tokenize(s):
|
||||
"Convert a string into a list of tokens."
|
||||
return s.replace('(',' ( ').replace(')',' ) ').split()
|
||||
|
||||
def read_from_tokens(tokens):
|
||||
"Read an expression from a sequence of tokens."
|
||||
if len(tokens) == 0:
|
||||
raise SyntaxError('unexpected EOF while reading')
|
||||
token = tokens.pop(0)
|
||||
if '(' == token:
|
||||
L = []
|
||||
while tokens[0] != ')':
|
||||
L.append(read_from_tokens(tokens))
|
||||
tokens.pop(0) # pop off ')'
|
||||
return L
|
||||
elif ')' == token:
|
||||
raise SyntaxError('unexpected )')
|
||||
else:
|
||||
return atom(token)
|
||||
|
||||
def atom(token):
|
||||
"Numbers become numbers; every other token is a symbol."
|
||||
try: return int(token)
|
||||
except ValueError:
|
||||
try: return float(token)
|
||||
except ValueError:
|
||||
return Symbol(token)
|
||||
|
||||
################ Environments
|
||||
|
||||
def standard_env():
|
||||
"An environment with some Scheme standard procedures."
|
||||
env = Env()
|
||||
env.update(vars(math)) # sin, cos, sqrt, pi, ...
|
||||
env.update({
|
||||
'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv,
|
||||
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
|
||||
'abs': abs,
|
||||
'append': op.add,
|
||||
'apply': apply,
|
||||
'begin': lambda *x: x[-1],
|
||||
'car': lambda x: x[0],
|
||||
'cdr': lambda x: x[1:],
|
||||
'cons': lambda x,y: [x] + y,
|
||||
'eq?': op.is_,
|
||||
'equal?': op.eq,
|
||||
'length': len,
|
||||
'list': lambda *x: list(x),
|
||||
'list?': lambda x: isinstance(x,list),
|
||||
'map': map,
|
||||
'max': max,
|
||||
'min': min,
|
||||
'not': op.not_,
|
||||
'null?': lambda x: x == [],
|
||||
'number?': lambda x: isinstance(x, Number),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, Symbol),
|
||||
})
|
||||
return env
|
||||
|
||||
class Env(dict):
|
||||
"An environment: a dict of {'var':val} pairs, with an outer Env."
|
||||
def __init__(self, parms=(), args=(), outer=None):
|
||||
self.update(zip(parms, args))
|
||||
self.outer = outer
|
||||
def find(self, var):
|
||||
"Find the innermost Env where var appears."
|
||||
return self if (var in self) else self.outer.find(var)
|
||||
|
||||
global_env = standard_env()
|
||||
|
||||
################ Interaction: A REPL
|
||||
|
||||
def repl(prompt='lis.py> '):
|
||||
"A prompt-read-eval-print loop."
|
||||
while True:
|
||||
val = eval(parse(raw_input(prompt)))
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
def lispstr(exp):
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if isinstance(exp, List):
|
||||
return '(' + ' '.join(map(lispstr, exp)) + ')'
|
||||
else:
|
||||
return str(exp)
|
||||
|
||||
################ Procedures
|
||||
|
||||
class Procedure(object):
|
||||
"A user-defined Scheme procedure."
|
||||
def __init__(self, parms, body, env):
|
||||
self.parms, self.body, self.env = parms, body, env
|
||||
def __call__(self, *args):
|
||||
return eval(self.body, Env(self.parms, args, self.env))
|
||||
|
||||
################ eval
|
||||
|
||||
def eval(x, env=global_env):
|
||||
"Evaluate an expression in an environment."
|
||||
if isinstance(x, Symbol): # variable reference
|
||||
return env.find(x)[x]
|
||||
elif not isinstance(x, List): # constant literal
|
||||
return x
|
||||
elif x[0] == 'quote': # (quote exp)
|
||||
(_, exp) = x
|
||||
return exp
|
||||
elif x[0] == 'if': # (if test conseq alt)
|
||||
(_, test, conseq, alt) = x
|
||||
exp = (conseq if eval(test, env) else alt)
|
||||
return eval(exp, env)
|
||||
elif x[0] == 'define': # (define var exp)
|
||||
(_, var, exp) = x
|
||||
env[var] = eval(exp, env)
|
||||
elif x[0] == 'set!': # (set! var exp)
|
||||
(_, var, exp) = x
|
||||
env.find(var)[var] = eval(exp, env)
|
||||
elif x[0] == 'lambda': # (lambda (var...) body)
|
||||
(_, parms, body) = x
|
||||
return Procedure(parms, body, env)
|
||||
else: # (proc arg...)
|
||||
proc = eval(x[0], env)
|
||||
args = [eval(exp, env) for exp in x[1:]]
|
||||
return proc(*args)
|
|
@ -0,0 +1,318 @@
|
|||
################ Scheme Interpreter in Python
|
||||
|
||||
## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html
|
||||
|
||||
################ Symbol, Procedure, classes
|
||||
|
||||
from __future__ import division
|
||||
import re, sys, StringIO
|
||||
|
||||
class Symbol(str): pass
|
||||
|
||||
def Sym(s, symbol_table={}):
|
||||
"Find or create unique Symbol entry for str s in symbol table."
|
||||
if s not in symbol_table: symbol_table[s] = Symbol(s)
|
||||
return symbol_table[s]
|
||||
|
||||
_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym,
|
||||
"quote if set! define lambda begin define-macro".split())
|
||||
|
||||
_quasiquote, _unquote, _unquotesplicing = map(Sym,
|
||||
"quasiquote unquote unquote-splicing".split())
|
||||
|
||||
class Procedure(object):
|
||||
"A user-defined Scheme procedure."
|
||||
def __init__(self, parms, exp, env):
|
||||
self.parms, self.exp, self.env = parms, exp, env
|
||||
def __call__(self, *args):
|
||||
return eval(self.exp, Env(self.parms, args, self.env))
|
||||
|
||||
################ parse, read, and user interaction
|
||||
|
||||
def parse(inport):
|
||||
"Parse a program: read and expand/error-check it."
|
||||
# Backwards compatibility: given a str, convert it to an InPort
|
||||
if isinstance(inport, str): inport = InPort(StringIO.StringIO(inport))
|
||||
return expand(read(inport), toplevel=True)
|
||||
|
||||
eof_object = Symbol('#<eof-object>') # Note: uninterned; can't be read
|
||||
|
||||
class InPort(object):
|
||||
"An input port. Retains a line of chars."
|
||||
tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"""
|
||||
def __init__(self, file):
|
||||
self.file = file; self.line = ''
|
||||
def next_token(self):
|
||||
"Return the next token, reading new text into line buffer if needed."
|
||||
while True:
|
||||
if self.line == '': self.line = self.file.readline()
|
||||
if self.line == '': return eof_object
|
||||
token, self.line = re.match(InPort.tokenizer, self.line).groups()
|
||||
if token != '' and not token.startswith(';'):
|
||||
return token
|
||||
|
||||
def readchar(inport):
|
||||
"Read the next character from an input port."
|
||||
if inport.line != '':
|
||||
ch, inport.line = inport.line[0], inport.line[1:]
|
||||
return ch
|
||||
else:
|
||||
return inport.file.read(1) or eof_object
|
||||
|
||||
def read(inport):
|
||||
"Read a Scheme expression from an input port."
|
||||
def read_ahead(token):
|
||||
if '(' == token:
|
||||
L = []
|
||||
while True:
|
||||
token = inport.next_token()
|
||||
if token == ')': return L
|
||||
else: L.append(read_ahead(token))
|
||||
elif ')' == token: raise SyntaxError('unexpected )')
|
||||
elif token in quotes: return [quotes[token], read(inport)]
|
||||
elif token is eof_object: raise SyntaxError('unexpected EOF in list')
|
||||
else: return atom(token)
|
||||
# body of read:
|
||||
token1 = inport.next_token()
|
||||
return eof_object if token1 is eof_object else read_ahead(token1)
|
||||
|
||||
quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing}
|
||||
|
||||
def atom(token):
|
||||
'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.'
|
||||
if token == '#t': return True
|
||||
elif token == '#f': return False
|
||||
elif token[0] == '"': return token[1:-1].decode('string_escape')
|
||||
try: return int(token)
|
||||
except ValueError:
|
||||
try: return float(token)
|
||||
except ValueError:
|
||||
try: return complex(token.replace('i', 'j', 1))
|
||||
except ValueError:
|
||||
return Sym(token)
|
||||
|
||||
def to_string(x):
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if x is True: return "#t"
|
||||
elif x is False: return "#f"
|
||||
elif isa(x, Symbol): return x
|
||||
elif isa(x, str): return '"%s"' % x.encode('string_escape').replace('"',r'\"')
|
||||
elif isa(x, list): return '('+' '.join(map(to_string, x))+')'
|
||||
elif isa(x, complex): return str(x).replace('j', 'i')
|
||||
else: return str(x)
|
||||
|
||||
def load(filename):
|
||||
"Eval every expression from a file."
|
||||
repl(None, InPort(open(filename)), None)
|
||||
|
||||
def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout):
|
||||
"A prompt-read-eval-print loop."
|
||||
sys.stderr.write("Lispy version 2.0\n")
|
||||
while True:
|
||||
try:
|
||||
if prompt: sys.stderr.write(prompt)
|
||||
x = parse(inport)
|
||||
if x is eof_object: return
|
||||
val = eval(x)
|
||||
if val is not None and out: print >> out, to_string(val)
|
||||
except Exception as e:
|
||||
print '%s: %s' % (type(e).__name__, e)
|
||||
|
||||
################ Environment class
|
||||
|
||||
class Env(dict):
|
||||
"An environment: a dict of {'var':val} pairs, with an outer Env."
|
||||
def __init__(self, parms=(), args=(), outer=None):
|
||||
# Bind parm list to corresponding args, or single parm to list of args
|
||||
self.outer = outer
|
||||
if isa(parms, Symbol):
|
||||
self.update({parms:list(args)})
|
||||
else:
|
||||
if len(args) != len(parms):
|
||||
raise TypeError('expected %s, given %s, '
|
||||
% (to_string(parms), to_string(args)))
|
||||
self.update(zip(parms,args))
|
||||
def find(self, var):
|
||||
"Find the innermost Env where var appears."
|
||||
if var in self: return self
|
||||
elif self.outer is None: raise LookupError(var)
|
||||
else: return self.outer.find(var)
|
||||
|
||||
def is_pair(x): return x != [] and isa(x, list)
|
||||
def cons(x, y): return [x]+y
|
||||
|
||||
def callcc(proc):
|
||||
"Call proc with current continuation; escape only"
|
||||
ball = RuntimeWarning("Sorry, can't continue this continuation any longer.")
|
||||
def throw(retval): ball.retval = retval; raise ball
|
||||
try:
|
||||
return proc(throw)
|
||||
except RuntimeWarning as w:
|
||||
if w is ball: return ball.retval
|
||||
else: raise w
|
||||
|
||||
def add_globals(self):
|
||||
"Add some Scheme standard procedures."
|
||||
import math, cmath, operator as op
|
||||
self.update(vars(math))
|
||||
self.update(vars(cmath))
|
||||
self.update({
|
||||
'+':op.add, '-':op.sub, '*':op.mul, '/':op.div, 'not':op.not_,
|
||||
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
|
||||
'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons,
|
||||
'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add,
|
||||
'list':lambda *x:list(x), 'list?': lambda x:isa(x,list),
|
||||
'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol),
|
||||
'boolean?':lambda x: isa(x, bool), 'pair?':is_pair,
|
||||
'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l),
|
||||
'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc,
|
||||
'open-input-file':open,'close-input-port':lambda p: p.file.close(),
|
||||
'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(),
|
||||
'eof-object?':lambda x:x is eof_object, 'read-char':readchar,
|
||||
'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)),
|
||||
'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))})
|
||||
return self
|
||||
|
||||
isa = isinstance
|
||||
|
||||
global_env = add_globals(Env())
|
||||
|
||||
################ eval (tail recursive)
|
||||
|
||||
def eval(x, env=global_env):
|
||||
"Evaluate an expression in an environment."
|
||||
while True:
|
||||
if isa(x, Symbol): # variable reference
|
||||
return env.find(x)[x]
|
||||
elif not isa(x, list): # constant literal
|
||||
return x
|
||||
elif x[0] is _quote: # (quote exp)
|
||||
(_, exp) = x
|
||||
return exp
|
||||
elif x[0] is _if: # (if test conseq alt)
|
||||
(_, test, conseq, alt) = x
|
||||
x = (conseq if eval(test, env) else alt)
|
||||
elif x[0] is _set: # (set! var exp)
|
||||
(_, var, exp) = x
|
||||
env.find(var)[var] = eval(exp, env)
|
||||
return None
|
||||
elif x[0] is _define: # (define var exp)
|
||||
(_, var, exp) = x
|
||||
env[var] = eval(exp, env)
|
||||
return None
|
||||
elif x[0] is _lambda: # (lambda (var*) exp)
|
||||
(_, vars, exp) = x
|
||||
return Procedure(vars, exp, env)
|
||||
elif x[0] is _begin: # (begin exp+)
|
||||
for exp in x[1:-1]:
|
||||
eval(exp, env)
|
||||
x = x[-1]
|
||||
else: # (proc exp*)
|
||||
exps = [eval(exp, env) for exp in x]
|
||||
proc = exps.pop(0)
|
||||
if isa(proc, Procedure):
|
||||
x = proc.exp
|
||||
env = Env(proc.parms, exps, proc.env)
|
||||
else:
|
||||
return proc(*exps)
|
||||
|
||||
################ expand
|
||||
|
||||
def expand(x, toplevel=False):
|
||||
"Walk tree of x, making optimizations/fixes, and signaling SyntaxError."
|
||||
require(x, x!=[]) # () => Error
|
||||
if not isa(x, list): # constant => unchanged
|
||||
return x
|
||||
elif x[0] is _quote: # (quote exp)
|
||||
require(x, len(x)==2)
|
||||
return x
|
||||
elif x[0] is _if:
|
||||
if len(x)==3: x = x + [None] # (if t c) => (if t c None)
|
||||
require(x, len(x)==4)
|
||||
return map(expand, x)
|
||||
elif x[0] is _set:
|
||||
require(x, len(x)==3);
|
||||
var = x[1] # (set! non-var exp) => Error
|
||||
require(x, isa(var, Symbol), "can set! only a symbol")
|
||||
return [_set, var, expand(x[2])]
|
||||
elif x[0] is _define or x[0] is _definemacro:
|
||||
require(x, len(x)>=3)
|
||||
_def, v, body = x[0], x[1], x[2:]
|
||||
if isa(v, list) and v: # (define (f args) body)
|
||||
f, args = v[0], v[1:] # => (define f (lambda (args) body))
|
||||
return expand([_def, f, [_lambda, args]+body])
|
||||
else:
|
||||
require(x, len(x)==3) # (define non-var/list exp) => Error
|
||||
require(x, isa(v, Symbol), "can define only a symbol")
|
||||
exp = expand(x[2])
|
||||
if _def is _definemacro:
|
||||
require(x, toplevel, "define-macro only allowed at top level")
|
||||
proc = eval(exp)
|
||||
require(x, callable(proc), "macro must be a procedure")
|
||||
macro_table[v] = proc # (define-macro v proc)
|
||||
return None # => None; add v:proc to macro_table
|
||||
return [_define, v, exp]
|
||||
elif x[0] is _begin:
|
||||
if len(x)==1: return None # (begin) => None
|
||||
else: return [expand(xi, toplevel) for xi in x]
|
||||
elif x[0] is _lambda: # (lambda (x) e1 e2)
|
||||
require(x, len(x)>=3) # => (lambda (x) (begin e1 e2))
|
||||
vars, body = x[1], x[2:]
|
||||
require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars))
|
||||
or isa(vars, Symbol), "illegal lambda argument list")
|
||||
exp = body[0] if len(body) == 1 else [_begin] + body
|
||||
return [_lambda, vars, expand(exp)]
|
||||
elif x[0] is _quasiquote: # `x => expand_quasiquote(x)
|
||||
require(x, len(x)==2)
|
||||
return expand_quasiquote(x[1])
|
||||
elif isa(x[0], Symbol) and x[0] in macro_table:
|
||||
return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...)
|
||||
else: # => macroexpand if m isa macro
|
||||
return map(expand, x) # (f arg...) => expand each
|
||||
|
||||
def require(x, predicate, msg="wrong length"):
|
||||
"Signal a syntax error if predicate is false."
|
||||
if not predicate: raise SyntaxError(to_string(x)+': '+msg)
|
||||
|
||||
_append, _cons, _let = map(Sym, "append cons let".split())
|
||||
|
||||
def expand_quasiquote(x):
|
||||
"""Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """
|
||||
if not is_pair(x):
|
||||
return [_quote, x]
|
||||
require(x, x[0] is not _unquotesplicing, "can't splice here")
|
||||
if x[0] is _unquote:
|
||||
require(x, len(x)==2)
|
||||
return x[1]
|
||||
elif is_pair(x[0]) and x[0][0] is _unquotesplicing:
|
||||
require(x[0], len(x[0])==2)
|
||||
return [_append, x[0][1], expand_quasiquote(x[1:])]
|
||||
else:
|
||||
return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])]
|
||||
|
||||
def let(*args):
|
||||
args = list(args)
|
||||
x = cons(_let, args)
|
||||
require(x, len(args)>1)
|
||||
bindings, body = args[0], args[1:]
|
||||
require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol)
|
||||
for b in bindings), "illegal binding list")
|
||||
vars, vals = zip(*bindings)
|
||||
return [[_lambda, list(vars)]+map(expand, body)] + map(expand, vals)
|
||||
|
||||
macro_table = {_let:let} ## More macros can go here
|
||||
|
||||
eval(parse("""(begin
|
||||
|
||||
(define-macro and (lambda args
|
||||
(if (null? args) #t
|
||||
(if (= (length args) 1) (car args)
|
||||
`(if ,(car args) (and ,@(cdr args)) #f)))))
|
||||
|
||||
;; More macros can also go here
|
||||
|
||||
)"""))
|
||||
|
||||
if __name__ == '__main__':
|
||||
repl()
|
||||
|
29
llm.l
29
llm.l
|
@ -35,8 +35,8 @@
|
|||
* Data types:
|
||||
* #fn Functions
|
||||
* t,nil boolean (nand <bool> <bool>) -> boolean
|
||||
* "str" Strings (streq <string> <string>) -> boolean
|
||||
* abc Symboles (symeq <sym> <sym>) -> boolean
|
||||
* "str" Strings
|
||||
* abc Symboles
|
||||
* 1234 Number/Integer (inv <num>) -> -num
|
||||
* (add <num> <num>) -> num
|
||||
* (lt <num> <num>) -> boolean
|
||||
|
@ -48,7 +48,7 @@
|
|||
* (lm (<PARAM>) (<BODY>))
|
||||
* create function with one or more PARAMs and
|
||||
* BODY.
|
||||
* (if <BOOL> <CASETRUE> <CASEFALSE>)
|
||||
* (if <BOOL> <CASE_TRUE> <CASE_FALSE>)
|
||||
* Evaluate BOOL and execute CASETRUE if true,
|
||||
* otherwise evaulate CASEFALSE
|
||||
* (quote a)
|
||||
|
@ -60,6 +60,29 @@
|
|||
* systems)
|
||||
* BUGS:
|
||||
* no freeing of memory (yet)
|
||||
*
|
||||
* Literature:
|
||||
* [1] https://news.ycombinator.com/item?id=8714988
|
||||
* Discussion about "minimal" set of primitives
|
||||
* for a LISP interpreter.
|
||||
* [2] https://stackoverflow.com/questions/3482389/
|
||||
* How many primitives does it take to build a
|
||||
* LISP machine? Ten, seven or five?
|
||||
* [3] http://www.cs.cmu.edu/Groups/AI/html/faqs/lang/ \
|
||||
* lisp/part1/faq-doc-6.html
|
||||
* Discussion about the minimal set of primitives
|
||||
* (see attachment)
|
||||
* [4] https://www.cs.cmu.edu/afs/cs/project/ai-repository \
|
||||
* /ai/lang/lisp/impl/awk/0.html
|
||||
* LISP Interpreter in AWK. (see attachments)
|
||||
* [5] http://norvig.com/lispy.html
|
||||
* Similar project, with Python instead of C
|
||||
* [6] http://norvig.com/lispy2.html
|
||||
* Improved version of [5] in Python. (attachment)
|
||||
* [7] Structure and Interpretation of Computer Programs,
|
||||
* by Abelson, Sussman, and Sussman
|
||||
* [8] FernUniversität Hagen: Logisches und funktionales
|
||||
* Programmieren, by Prof.Dr.Beierle.
|
||||
*/
|
||||
%{
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
|
Loading…
Reference in New Issue