Alex Vong 92f580152f Add a minimal scm module to provide Scheme-like data types and procedures.
primitives.py implements a minimal subset of Scheme primitives.
srfi_1.py implements a minimal subset of SRFI-1: List Library.

* devscripts/scm/__init__.py: New file.
* devscripts/scm/primitives.py: New file.
* devscripts/scm/srfi_1.py: New file.
2015-09-13 18:14:05 +08:00

197 lines
5.2 KiB
Python

from __future__ import unicode_literals
import re
import sys
"""
Module implemeting a minimal subset of Scheme primitives in term of Python
In Scheme:
apply car cdr cons display even? length list list->lst lst->list lst->tuple
list? null? object->string odd? pair? string? string->symbol symbol->string
symbol? tuple->lst
In Python:
apply car cdr cons display is_even length list list_to_lst lst_to_list
lst_to_tuple is_list is_null object_to_string is_odd is_pair is_string
string_to_symbol symbol_to_string is_symbol tuple_to_lst
"""
# standalone primitives
if sys.version_info < (3, 0):
def display(obj): print(obj.encode("utf-8"))
else:
def display(obj): print(obj)
def is_even(x): return x % 2 == 0
def is_odd(x): return x % 2 != 0
if sys.version_info < (3, 0):
is_string = lambda obj: isinstance(obj, (str, basestring))
else:
is_string = lambda obj: isinstance(obj, str)
if sys.version_info < (3, 0):
object_to_string = lambda obj: unicode(obj)
else:
object_to_string = lambda obj: str(obj)
# nil-related primitives
class _Nil:
"""internal class implementing the empty lst type"""
def __repr__(self): return "()"
def __str__(self): return "()"
# Many Scheme implementations don't have nil and use '() instead,
# but we can't do that as we don't know how to get quoting works in Python...
nil = _Nil()
def is_null(x): return x is nil
# pair/lst-related primitives
class _Pair:
"""internal class implementing the pair type"""
def __init__(self, car, cdr):
self.car = car
self.cdr = cdr
if cdr is nil or is_list(cdr):
self.is_list = True
else:
self.is_list = False
def __repr__(self):
"""
Simulate representation of pair and list in Scheme REPL
In general, cons(x, y) is called a pair and is represented by (x . y)
However, if y is the empty lst, then cons(x, y) is called a lst
(avoid confusing with the procedure list,
treat list as a verb and lst as a noun)
and is represented by (x)
Moroever, if y is a lst and represented by (foo),
then cons(x,y) is also a lst and is represented by (x foo)
"""
pattern = r"^\(|\)$"
if self.cdr is nil:
return "(" + repr(self.car) + ")"
elif self.is_list:
return "(" + repr(self.car) + " " + \
re.sub(pattern, "", repr(self.cdr)) + ")"
else:
return "(" + repr(self.car) + " . " + repr(self.cdr) + ")"
def __str__(self):
"""
Same as __repr__.
Except repr(self.car) and repr(self.cdr) are replaced by
object_to_string(self.car) and object_to_string(self.cdr) respectively.
"""
pattern = r"^\(|\)$"
if self.is_list:
return "(" + object_to_string(self.car) + " " + \
re.sub(pattern, "", object_to_string(self.cdr)) + ")"
else:
return "(" + object_to_string(self.car) + " . " + \
object_to_string(self.cdr) + ")"
def __eq__(self, x):
return isinstance(x, _Pair) and self.car == x.car and self.cdr == x.cdr
def cons(a, b): return _Pair(a, b)
def car(pair): return pair.car
def cdr(pair): return pair.cdr
def is_pair(x): return isinstance(x, _Pair)
def is_list(x):
if x is nil:
return True
elif isinstance(x, _Pair):
return x.is_list
else:
return False
def list(*arg_tup):
"""build a lst from any number of elements"""
if arg_tup is ():
return nil
else:
return cons(arg_tup[0], list(*arg_tup[1:]))
def tuple_to_lst(tup):
"""convert Python tuple to Scheme lst"""
if not tup:
return nil
else:
return cons(tup[0], tuple_to_lst(tup[1:]))
def lst_to_tuple(lst):
"""convert Scheme lst to Python tuple"""
if lst is nil:
return ()
else:
return (lst.car,) + lst_to_tuple(lst.cdr)
def apply(proc, lst):
"""apply procedure proc to a Scheme lst"""
return proc(*lst_to_tuple(lst))
def list_to_lst(list_):
"""convert Python list to Scheme lst"""
if not list_:
return nil
else:
return cons(list_[0], list_to_lst(list_[1:]))
def lst_to_list(lst):
"""convert Scheme lst to Python list"""
if lst is nil:
return []
else:
return [lst.car,] + lst_to_list(lst.cdr)
# symbol-related primitives
# maintain a dictionary of symbol, to avoid duplication of _Symbol object
_symbol_dict = {}
class _Symbol:
"""internal class implementing the symbol type"""
def __init__(self, string):
self.string = string
_symbol_dict[string] = self
def __repr__(self):
"""remove leading and trailing quote from repr(self.string)"""
pattern = r"^'|^\"|\"$|'$"
return re.sub(pattern, "", repr(self.string))
def __str__(self):
return object_to_string(self.string)
def string_to_symbol(string):
"""convert Python string to Scheme symbol"""
if string not in _symbol_dict:
_symbol_dict[string] = _Symbol(string)
return _symbol_dict[string]
def symbol_to_string(symbol): return object_to_string(symbol)
def is_symbol(x): return isinstance(_Symbol)