(plain text)

"""This module implements the primitives of the Scheme language."""

import math
import operator
import sys
from scheme_reader import Pair, nil

    import turtle
    print("warning: could not import the turtle module.", file=sys.stderr)

class SchemeError(Exception):
    """Exception indicating an error in a Scheme program."""

class okay(object):
    """Signifies an undefined value."""
    def __repr__(self):
        return "okay"

okay = okay() # Assignment hides the okay class; there is only one instance

# Primitive Operations #

class PrimitiveProcedure:
    """A Scheme procedure defined as a Python function."""

    def __init__(self, fn, use_env=False):
        self.fn = fn
        self.use_env = use_env

    def __str__(self):
        return '#[primitive]'


def primitive(*names):
    """An annotation to convert a Python function into a PrimitiveProcedure."""
    def add(fn):
        proc = PrimitiveProcedure(fn)
        for name in names:
        return fn
    return add

def add_primitives(frame):
    """Enter bindings in _PRIMITIVES into FRAME, an environment frame."""
    for name, proc in _PRIMITIVES:
        frame.define(name, proc)

def check_type(val, predicate, k, name):
    """Returns VAL.  Raises a SchemeError if not PREDICATE(VAL)
    using "argument K of NAME" to describe the offending value."""
    if not predicate(val):
        msg = "argument {0} of {1} has wrong type ({2})"
        raise SchemeError(msg.format(k, name, type(val).__name__))
    return val

def scheme_booleanp(x):
    return x is True or x is False

def scheme_true(val):
    """All values in Scheme are true except False."""
    return val is not False

def scheme_false(val):
    """Only False is false in Scheme."""
    return val is False

def scheme_not(x):
    return not scheme_true(x)

@primitive("eq?", "equal?")
def scheme_eqp(x, y):
    return x == y

def scheme_pairp(x):
    return isinstance(x, Pair)

def scheme_nullp(x):
    return x is nil

def scheme_listp(x):
    """Return whether x is a well-formed list. Assumes no cycles."""
    while x is not nil:
        if not isinstance(x, Pair):
            return False
        x = x.second
    return True

def scheme_length(x):
    if x is nil:
        return 0
    check_type(x, scheme_listp, 0, 'length')
    return len(x)

def scheme_cons(x, y):
    return Pair(x, y)

def scheme_car(x):
    check_type(x, scheme_pairp, 0, 'car')
    return x.first

def scheme_cdr(x):
    check_type(x, scheme_pairp, 0, 'cdr')
    return x.second

def scheme_list(*vals):
    result = nil
    for i in range(len(vals)-1, -1, -1):
        result = Pair(vals[i], result)
    return result

def scheme_append(*vals):
    if len(vals) == 0:
        return nil
    result = vals[-1]
    for i in range(len(vals)-2, -1, -1):
        v = vals[i]
        if v is not nil:
            check_type(v, scheme_pairp, i, "append")
            r = p = Pair(v.first, result)
            v = v.second
            while scheme_pairp(v):
                p.second = Pair(v.first, result)
                p = p.second
                v = v.second
            result = r
    return result

def scheme_stringp(x):
    return isinstance(x, str) and x.startswith('"')

def scheme_symbolp(x):
    return isinstance(x, str) and not scheme_stringp(x)

def scheme_numberp(x):
    return isinstance(x, int) or isinstance(x, float)

def scheme_integerp(x):
    return isinstance(x, int) or (scheme_numberp(x) and round(x) == x)

def _check_nums(*vals):
    """Check that all arguments in VALS are numbers."""
    for i, v in enumerate(vals):
        if not scheme_numberp(v):
            msg = "operand {0} ({1}) is not a number"
            raise SchemeError(msg.format(i, v))

def _arith(fn, init, vals):
    """Perform the fn fneration on the number values of VALS, with INIT as
    the value when VALS is empty. Returns the result as a Scheme value."""
    s = init
    for val in vals:
        s = fn(s, val)
    if round(s) == s:
        s = round(s)
    return s

def scheme_add(*vals):
    return _arith(operator.add, 0, vals)

def scheme_sub(val0, *vals):
    if len(vals) == 0:
        return -val0
    return _arith(operator.sub, val0, vals)

def scheme_mul(*vals):
    return _arith(operator.mul, 1, vals)

def scheme_div(val0, val1):
        return _arith(operator.truediv, val0, [val1])
    except ZeroDivisionError as err:
        raise SchemeError(err)

def scheme_quo(val0, val1):
        return _arith(operator.floordiv, val0, [val1])
    except ZeroDivisionError as err:
        raise SchemeError(err)

@primitive("modulo", "remainder")
def scheme_modulo(val0, val1):
        return _arith(operator.mod, val0, [val1])
    except ZeroDivisionError as err:
        raise SchemeError(err)

def scheme_floor(val):
    return math.floor(val)

def scheme_ceil(val):
    return math.ceil(val)

def _numcomp(op, x, y):
    _check_nums(x, y)
    return op(x, y)

def scheme_eq(x, y):
    return _numcomp(operator.eq, x, y)

def scheme_lt(x, y):
    return _numcomp(, x, y)

def scheme_gt(x, y):
    return _numcomp(, x, y)

def scheme_le(x, y):
    return _numcomp(operator.le, x, y)

def scheme_ge(x, y):
    return _numcomp(, x, y)

def scheme_evenp(x):
    return x % 2 == 0

def scheme_oddp(x):
    return x % 2 == 1

def scheme_zerop(x):
    return x == 0

## Other operations

def scheme_atomp(x):
    if scheme_booleanp(x):
        return True
    if scheme_numberp(x):
        return True
    if scheme_symbolp(x):
        return True
    if scheme_nullp(x):
        return True
    return False

def scheme_display(val):
    if scheme_stringp(val):
        val = eval(val)
    print(str(val), end="")
    return okay

def scheme_print(val):
    return okay

def scheme_newline():
    return okay

def scheme_error(msg = None):
    msg = "" if msg is None else str(msg)
    raise SchemeError(msg)

def scheme_exit():
    raise EOFError

## Turtle graphics (non-standard)

_turtle_screen_on = False

def turtle_screen_on():
    return _turtle_screen_on

def _tscheme_prep():
    global _turtle_screen_on
    if not _turtle_screen_on:
        _turtle_screen_on = True
        turtle.title("Scheme Turtles")

@primitive("forward", "fd")
def tscheme_forward(n):
    """Move the turtle forward a distance N units on the current heading."""
    return okay

@primitive("backward", "back", "bk")
def tscheme_backward(n):
    """Move the turtle backward a distance N units on the current heading,
    without changing direction."""
    return okay

@primitive("left", "lt")
def tscheme_left(n):
    """Rotate the turtle's heading N degrees counterclockwise."""
    return okay

@primitive("right", "rt")
def tscheme_right(n):
    """Rotate the turtle's heading N degrees clockwise."""
    return okay

def tscheme_circle(r, extent = None):
    """Draw a circle with center R units to the left of the turtle (i.e.,
    right if N is negative.  If EXTENT is not None, then draw EXTENT degrees
    of the circle only.  Draws in the clockwise direction if R is negative,
    and otherwise counterclockwise, leaving the turtle facing along the
    arc at its end."""
    if extent is None:
        _check_nums(r, extent)
    _tscheme_prep(), extent and extent)
    return okay

@primitive("setposition", "setpos", "goto")
def tscheme_setposition(x, y):
    """Set turtle's position to (X,Y), heading unchanged."""
    _check_nums(x, y)
    turtle.setposition(x, y)
    return okay

@primitive("setheading", "seth")
def tscheme_setheading(h):
    """Set the turtle's heading H degrees clockwise from north (up)."""
    return okay

@primitive("penup", "pu")
def tscheme_penup():
    """Raise the pen, so that the turtle does not draw."""
    return okay

@primitive("pendown", "pd")
def tscheme_pendown():
    """Lower the pen, so that the turtle starts drawing."""
    return okay

@primitive("showturtle", "st")
def tscheme_showturtle():
    """Make turtle visible."""
    return okay

@primitive("hideturtle", "ht")
def tscheme_hideturtle():
    """Make turtle visible."""
    return okay

def tscheme_clear():
    """Clear the drawing, leaving the turtle unchanged."""
    return okay

def tscheme_color(c):
    """Set the color to C, a string such as '"red"' or '"#ffc0c0"' (representing
    hexadecimal red, green, and blue values."""
    check_type(c, scheme_stringp, 0, "color")
    return okay

def tscheme_begin_fill():
    """Start a sequence of moves that outline a shape to be filled."""
    return okay

def tscheme_end_fill():
    """Fill in shape drawn since last begin_fill."""
    return okay

def tscheme_exitonclick():
    """Wait for a click on the turtle window, and then close it."""
    global _turtle_screen_on
    if _turtle_screen_on:
        print("Close or click on turtle window to complete exit")
        _turtle_screen_on = False
    return okay

def tscheme_speed(s):
    """Set the turtle's animation speed as indicated by S (an integer in
    0-10, with 0 indicating no animation (lines draw instantly), and 1-10
    indicating faster and faster movement."""
    check_type(s, scheme_integerp, 0, "speed")
    return okay