Esempio n. 1
0
def make_string(arguments):
    check_argument_number('make-string', arguments, 1, 2)

    string_length_atom = arguments[0]

    if not isinstance(string_length_atom, Integer):
        raise SchemeTypeError("String length must be an integer, "
                              "got %d." % string_length_atom.__class__)

    string_length = string_length_atom.value

    if string_length < 0:
        raise InvalidArgument("String length must be non-negative, "
                              "got %d." % string_length)

    if len(arguments) == 1:
        return String(' ' * string_length)

    else:
        repeated_character_atom = arguments[1]

        if not isinstance(repeated_character_atom, Character):
            raise SchemeTypeError("The second argument to make-string must be"
                                  " a character, got a %s." %
                                  repeated_character_atom.__class__)

        repeated_character = repeated_character_atom.value
        return String(repeated_character * string_length)
Esempio n. 2
0
def is_char(arguments):
    check_argument_number('char?', arguments, 1, 1)

    if isinstance(arguments[0], Character):
        return Boolean(True)

    return Boolean(False)
Esempio n. 3
0
def pair(arguments):
    check_argument_number('pair?', arguments, 1, 1)

    if isinstance(arguments[0], Cons):
        return Boolean(True)

    return Boolean(False)
Esempio n. 4
0
def subtract(arguments):
    check_argument_number('-', arguments, 1)

    if len(arguments) == 1:
        # we just negate a single argument
        if isinstance(arguments[0], Integer):
            return Integer(-1 * arguments[0].value)
        elif isinstance(arguments[0], FloatingPoint):
            return FloatingPoint(-1 * arguments[0].value)
        else:
            raise SchemeTypeError("Subtraction is only defined for integers and "
                                  "floating point, you gave me %s." % arguments[0].__class__)

    total = copy(arguments[0])

    for argument in arguments.tail:
        if not isinstance(argument, Number):
            raise SchemeTypeError("Subtraction is only defined for numbers, "
                                  "you gave me %s." % argument.__class__)

        # subtracting a float from an integer gives us a float
        if isinstance(total, Integer) and isinstance(argument, FloatingPoint):
            total = FloatingPoint(float(total.value))

        total.value -= argument.value

    return total
Esempio n. 5
0
def is_procedure(arguments):
    check_argument_number("procedure?", arguments, 1, 1)

    if callable(arguments[0]):
        return Boolean(True)

    return Boolean(False)
Esempio n. 6
0
def is_string(arguments):
    check_argument_number('string?', arguments, 1, 1)

    if isinstance(arguments[0], String):
        return Boolean(True)

    return Boolean(False)
Esempio n. 7
0
def make_string(arguments):
    check_argument_number('make-string', arguments, 1, 2)

    string_length_atom = arguments[0]

    if not isinstance(string_length_atom, Integer):
        raise SchemeTypeError("String length must be an integer, "
                              "got %d." % string_length_atom.__class__)

    string_length = string_length_atom.value

    if string_length < 0:
        raise InvalidArgument("String length must be non-negative, "
                              "got %d." % string_length)

    if len(arguments) == 1:
        return String(' ' * string_length)

    else:
        repeated_character_atom = arguments[1]

        if not isinstance(repeated_character_atom, Character):
            raise SchemeTypeError("The second argument to make-string must be"
                                  " a character, got a %s." % repeated_character_atom.__class__)

        repeated_character = repeated_character_atom.value
        return String(repeated_character * string_length)
Esempio n. 8
0
def is_procedure(arguments):
    check_argument_number('procedure?', arguments, 1, 1)

    if callable(arguments[0]):
        return Boolean(True)

    return Boolean(False)
Esempio n. 9
0
def vector_length(arguments):
    check_argument_number('vector-length', arguments, 1, 1)

    # todo: check type
    vector = arguments[0]

    return Integer(len(vector))
Esempio n. 10
0
def define(arguments, environment):
    check_argument_number('define', arguments, 2)

    if isinstance(arguments[0], Atom):
        return define_variable(arguments, environment)
    else:
        return define_function(arguments, environment)
Esempio n. 11
0
def string_set(arguments):
    check_argument_number('string-set!', arguments, 3, 3)

    string_atom = arguments[0]
    if not isinstance(string_atom, String):
        raise SchemeTypeError("string-set! takes a string as its first argument, "
                              "not a %s." % string_atom.__class__)

    char_index_atom = arguments[1]
    if not isinstance(char_index_atom, Integer):
        raise SchemeTypeError("string-set! takes an integer as its second argument, "
                              "not a %s." % char_index_atom.__class__)

    replacement_char_atom = arguments[2]
    if not isinstance(replacement_char_atom, Character):
        raise SchemeTypeError("string-set! takes a character as its third argument, "
                              "not a %s." % replacement_char_atom.__class__)

    string = string_atom.value
    char_index = char_index_atom.value

    if char_index >= len(string):
        # FIXME: this will say 0--1 if string is ""
        raise InvalidArgument("String index out of bounds: index must be in"
                              " the range 0-%d, got %d." % (len(string) - 1, char_index))

    characters = list(string)
    characters[char_index] = replacement_char_atom.value
    new_string = "".join(characters)

    string_atom.value = new_string

    return None
Esempio n. 12
0
def car(arguments):
    # TODO: check type as well as arity
    check_argument_number('car', arguments, 1)

    list_given = arguments[0]

    return list_given[0]
Esempio n. 13
0
def string_ref(arguments):
    check_argument_number('string-ref', arguments, 2, 2)

    string_atom = arguments[0]
    if not isinstance(string_atom, String):
        raise SchemeTypeError(
            "string-ref takes a string as its first argument, "
            "not a %s." % string_atom.__class__)

    char_index_atom = arguments[1]
    if not isinstance(char_index_atom, Integer):
        raise SchemeTypeError(
            "string-ref takes an integer as its second argument, "
            "not a %s." % char_index_atom.__class__)

    string = string_atom.value
    char_index = char_index_atom.value

    if char_index >= len(string):
        # FIXME: this will say 0--1 if string is ""
        raise InvalidArgument("String index out of bounds: index must be in"
                              " the range 0-%d, got %d." %
                              (len(string) - 1, char_index))

    return Character(string[char_index])
Esempio n. 14
0
def number(arguments):
    check_argument_number('number?', arguments, 1, 1)

    if isinstance(arguments[0], Number):
        return Boolean(True)

    return Boolean(False)
Esempio n. 15
0
def is_vector(arguments):
    check_argument_number('make-vector', arguments, 1, 1)

    if isinstance(arguments[0], Vector):
        return Boolean(True)

    return Boolean(False)
Esempio n. 16
0
def vector_ref(arguments):
    check_argument_number('vector-ref', arguments, 2, 2)

    vector = arguments[0]
    index = arguments[1].value

    return vector[index]
Esempio n. 17
0
def is_string(arguments):
    check_argument_number('string?', arguments, 1, 1)

    if isinstance(arguments[0], String):
        return Boolean(True)

    return Boolean(False)
Esempio n. 18
0
    def named_function(_arguments, _environment):
        check_argument_number(function_name.value, _arguments,
                              len(function_parameters),
                              len(function_parameters))

        local_environment = {}

        # evaluate arguments
        _arguments = deepcopy(_arguments)
        for i in range(len(_arguments)):
            (_arguments[i],
             _environment) = eval_s_expression(_arguments[i], _environment)

        # assign to parameters
        for (parameter_name, parameter_value) in zip(function_parameters,
                                                     _arguments):
            local_environment[parameter_name.value] = parameter_value

        # create new environment, where local variables mask globals
        new_environment = dict(_environment, **local_environment)

        # evaluate the function block
        for s_exp in function_body:
            result, new_environment = eval_s_expression(s_exp, new_environment)

        # update any global variables that weren't masked
        for variable_name in _environment:
            if variable_name not in local_environment:
                _environment[variable_name] = new_environment[variable_name]

        return (result, _environment)
Esempio n. 19
0
def is_char(arguments):
    check_argument_number('char?', arguments, 1, 1)

    if isinstance(arguments[0], Character):
        return Boolean(True)

    return Boolean(False)
Esempio n. 20
0
def display(arguments):
    check_argument_number('display', arguments, 1, 1)

    atom = arguments[0]
    # FIXME: should we check type? Is this funcction only for strings?
    print(atom.value, end='')

    return None
Esempio n. 21
0
def greater_or_equal(arguments):
    check_argument_number('>=', arguments, 2)

    for i in range(len(arguments) - 1):
        if not arguments[i].value >= arguments[i+1].value:
            return Boolean(False)

    return Boolean(True)
Esempio n. 22
0
def set_cdr(arguments):
    # TODO: check type as well as arity
    check_argument_number('set-cdr!', arguments, 2, 2)

    list_given = arguments[0]
    list_given.tail = arguments[1]

    return Nil()
Esempio n. 23
0
def less_than(arguments):
    check_argument_number('<', arguments, 2)

    for i in range(len(arguments) - 1):
        if not arguments[i].value < arguments[i+1].value:
            return Boolean(False)

    return Boolean(True)
Esempio n. 24
0
def exp(arguments):
    check_argument_number('exp', arguments, 1, 1)

    if arguments[0].__class__ not in [Integer, FloatingPoint]:
        raise SchemeTypeError("exp only takes integers or floats, "
                              "got %s" % arguments[0].__class__)

    x1 = arguments[0].value
    return FloatingPoint(math.exp(x1))
Esempio n. 25
0
def log(arguments):
    check_argument_number('log', arguments, 1, 1)

    if not isinstance(arguments[0], Number):
        raise SchemeTypeError("Log is only defined for numbers, "
                              "you gave me %s." % arguments[0].__class__)

    x1 = arguments[0].value
    return FloatingPoint(math.log(x1))
Esempio n. 26
0
def modulo(arguments):
    check_argument_number('modulo', arguments, 2, 2)

    if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer):
        raise SchemeTypeError("modulo is only defined for integers, "
                              "got %s and %s." % (arguments[0].__class__,
                                                  arguments[1].__class__))

    return Integer(arguments[0].value % arguments[1].value)
Esempio n. 27
0
def quasiquote(arguments, environment):
    """Returns the arguments unevaluated, except for any occurrences
    of unquote.

    """
    def recursive_eval_unquote(s_expression, _environment):
        """Return a copy of s_expression, with all occurrences of
        unquoted s-expressions replaced by their evaluated values.

        Note that we can only have unquote-splicing in a sublist,
        since we can only return one value, e.g `,@(1 2 3).

        """
        if isinstance(s_expression, Atom):
            return (s_expression, _environment)

        elif isinstance(s_expression, Nil):
            return (s_expression, _environment)

        elif s_expression[0] == Symbol("unquote"):
            check_argument_number('unquote', arguments, 1, 1)
            return eval_s_expression(s_expression[1], _environment)

        else:
            # return a list of s_expressions that have been
            # recursively checked for unquote
            list_elements = []

            for element in s_expression:
                if isinstance(element, Cons) and \
                        element[0] == Symbol('unquote-splicing'):
                    check_argument_number('unquote-splicing', element.tail, 1,
                                          1)

                    (result,
                     _environment) = eval_s_expression(element[1],
                                                       _environment)

                    if not isinstance(result, Cons) and not isinstance(
                            result, Nil):
                        raise SchemeArityError(
                            "unquote-splicing requires a list.")

                    for item in result:
                        list_elements.append(item)

                else:
                    (result, _environment) = recursive_eval_unquote(
                        element, _environment)
                    list_elements.append(result)

            return (Cons.from_list(list_elements), _environment)

    check_argument_number('quasiquote', arguments, 1, 1)

    return recursive_eval_unquote(arguments[0], environment)
Esempio n. 28
0
def string_length(arguments):
    check_argument_number('string-length', arguments, 1, 1)

    string_atom = arguments[0]
    if not isinstance(string_atom, String):
        raise SchemeTypeError("string-length takes a string as its argument, "
                              "not a %s." % string_atom.__class__)

    string_length = len(string_atom.value)
    return Integer(string_length)
Esempio n. 29
0
def vector_set(arguments):
    check_argument_number('vector-ref', arguments, 3, 3)

    vector = arguments[0]
    index = arguments[1].value
    new_value = arguments[2]

    vector[index] = new_value

    return Nil()
Esempio n. 30
0
def string_length(arguments):
    check_argument_number('string-length', arguments, 1, 1)

    string_atom = arguments[0]
    if not isinstance(string_atom, String):
        raise SchemeTypeError("string-length takes a string as its argument, "
                              "not a %s." % string_atom.__class__)

    string_length = len(string_atom.value)
    return Integer(string_length)
Esempio n. 31
0
    def named_variadic_function(_arguments, _environment):
        # a function that takes a variable number of arguments
        if dot_position == 0:
            explicit_parameters = Nil()
        else:
            explicit_parameters = deepcopy(function_parameters)

            # create a linked list holding all the parameters before the dot
            current_head = explicit_parameters

            # find the position in the list just before the dot
            for i in range(dot_position - 2):
                current_head = current_head.tail

            # then remove the rest of the list
            current_head.tail = Nil()

        improper_list_parameter = function_parameters[dot_position + 1]

        # check we have been given sufficient arguments for our explicit parameters
        check_argument_number(function_name.value, _arguments,
                              len(explicit_parameters))

        local_environment = {}

        # evaluate arguments
        _arguments = deepcopy(_arguments)
        for i in range(len(_arguments)):
            (_arguments[i],
             _environment) = eval_s_expression(_arguments[i], _environment)

        # assign parameters
        for (parameter, parameter_value) in zip(explicit_parameters,
                                                _arguments):
            local_environment[parameter] = parameter_value

        # put the remaining arguments in our improper parameter
        remaining_arguments = _arguments
        for i in range(len(explicit_parameters)):
            remaining_arguments = remaining_arguments.tail

        local_environment[improper_list_parameter.value] = remaining_arguments

        new_environment = dict(_environment, **local_environment)

        # evaluate our function_body in this environment
        for s_exp in function_body:
            result, new_environment = eval_s_expression(s_exp, new_environment)

        # update global variables that weren't masked by locals
        for variable_name in _environment:
            if variable_name not in local_environment:
                _environment[variable_name] = new_environment[variable_name]

        return (result, _environment)
Esempio n. 32
0
def inexact(arguments):
    check_argument_number('inexact?', arguments, 1, 1)

    if isinstance(arguments[0], FloatingPoint):
        return Boolean(True)
    elif isinstance(arguments[0], Integer):
        return Boolean(False)
    else:
        raise SchemeTypeError("exact? only takes integers or floating point "
                              "numbers as arguments, you gave me ""%s." % \
                                  len(arguments))
Esempio n. 33
0
def char_less_than(arguments):
    check_argument_number('char<?', arguments, 2, 2)

    if not isinstance(arguments[0], Character) or not isinstance(arguments[1], Character):
        raise SchemeTypeError("char<? takes only character arguments, got a "
                              "%s and a %s." % (arguments[0].__class__,
                                                arguments[1].__class__))

    if arguments[0].value < arguments[1].value:
        return Boolean(True)

    return Boolean(False)
Esempio n. 34
0
def if_function(arguments, environment):
    check_argument_number('if', arguments, 2, 3)

    condition, environment = eval_s_expression(arguments[0], environment)

    # everything except an explicit false boolean is true
    if not condition == Boolean(False):
        then_expression = arguments[1]
        return eval_s_expression(then_expression, environment)
    else:
        if len(arguments) == 3:
            else_expression = arguments[2]
            return eval_s_expression(else_expression, environment)
Esempio n. 35
0
def test_equivalence(arguments):
    check_argument_number('eqv?', arguments, 2, 2)

    if isinstance(arguments[0], Atom):
        # __eq__ is defined on Atom
        return Boolean(arguments[0] == arguments[1])
    if isinstance(arguments[0], Cons):
        # __eq__ on Cons is deep equality
        return Boolean(arguments[0] is arguments[1])
    else:
        # __eq__ is defined on Nil
        # todo: test vectors
        return Boolean(arguments[0] == arguments[1])
Esempio n. 36
0
def char_less_than(arguments):
    check_argument_number('char<?', arguments, 2, 2)

    if not isinstance(arguments[0], Character) or not isinstance(
            arguments[1], Character):
        raise SchemeTypeError("char<? takes only character arguments, got a "
                              "%s and a %s." %
                              (arguments[0].__class__, arguments[1].__class__))

    if arguments[0].value < arguments[1].value:
        return Boolean(True)

    return Boolean(False)
Esempio n. 37
0
def remainder(arguments):
    check_argument_number('remainder', arguments, 2, 2)

    if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer):
        raise SchemeTypeError("remainder is only defined for integers, "
                              "got %s and %s." % (arguments[0].__class__,
                                                  arguments[1].__class__))

    # as with quotient, we can't use Python's integer division here because it floors rather than truncates
    x1 = arguments[0].value
    x2 = arguments[1].value
    value = x1 - (math.trunc(x1 / x2) * x2)

    return Integer(value)
Esempio n. 38
0
def divide(arguments):
    # TODO: support exact fractions
    # TODO: return integer if all arguments were integers and result is whole number
    check_argument_number('/', arguments, 1)

    if len(arguments) == 1:
        return FloatingPoint(1 / arguments[0].value)
    else:
        result = FloatingPoint(arguments[0].value)

        for argument in arguments.tail:
            result.value /= argument.value

        return result
Esempio n. 39
0
def quotient(arguments):
    # integer division
    check_argument_number('quotient', arguments, 2, 2)

    if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer):
        raise SchemeTypeError("quotient is only defined for integers, "
                              "got %s and %s." % (arguments[0].__class__,
                                                  arguments[1].__class__))

    # Python's integer division floors, whereas Scheme rounds towards zero
    x1 = arguments[0].value
    x2 = arguments[1].value
    result = math.trunc(x1 / x2)

    return Integer(result)
Esempio n. 40
0
def string_ref(arguments):
    check_argument_number('string-ref', arguments, 2, 2)

    string_atom = arguments[0]
    if not isinstance(string_atom, String):
        raise SchemeTypeError("string-ref takes a string as its first argument, "
                              "not a %s." % string_atom.__class__)

    char_index_atom = arguments[1]
    if not isinstance(char_index_atom, Integer):
        raise SchemeTypeError("string-ref takes an integer as its second argument, "
                              "not a %s." % char_index_atom.__class__)

    string = string_atom.value
    char_index = char_index_atom.value

    if char_index >= len(string):
        # FIXME: this will say 0--1 if string is ""
        raise InvalidArgument("String index out of bounds: index must be in"
                              " the range 0-%d, got %d." % (len(string) - 1, char_index))

    return Character(string[char_index])
Esempio n. 41
0
def cons(arguments):
    # TODO: check type as well as arity
    check_argument_number('cons', arguments, 2, 2)

    return Cons(arguments[0], arguments[1])