def for_stmt(node):

    (FOR, (IN_EXP, in_exp), (STMT_LIST, stmt_list)) = node
    assert_match(FOR, 'for')

    (IN, pattern, list_term) = in_exp

    # expand the list_term in case the list is expressed as a constructor
    (LIST, list_val) = walk(list_term)

    # for each term on the list unfiy with pattern, declare the bound variables,
    # and execute the loop body in that context
    # NOTE: just like Python, loop bodies do not create a new scope!
    # NOTE: we can use unification as a filter of elements:
    #
    #      for (2,y) in [(1,11), (1,12), (1,13), (2,21), (2,22), (2,23)]  do
    #             print y.
    #      end for
    try:
        for term in list_val:
            try:
                unifiers = unify(term, pattern)
            except PatternMatchFailed:
                pass
            else:
                declare_unifiers(unifiers)
                walk(stmt_list)
    except Break:
        pass
def assert_stmt(node):

    (ASSERT, exp) = node
    assert_match(ASSERT, 'assert')

    exp_val = walk(exp)
    # mapping asteroid assert into python assert
    assert exp_val[1], 'assert failed'
def unify_stmt(node):

    (UNIFY, pattern, exp) = node
    assert_match(UNIFY, 'unify')

    term = walk(exp)
    unifiers = unify(term, pattern)
    declare_unifiers(unifiers)
def process_lineinfo(node):

    (LINEINFO, lineinfo_val) = node
    assert_match(LINEINFO, 'lineinfo')

    #lhh
    #print("lineinfo: {}".format(lineinfo_val))

    state.lineinfo = lineinfo_val
def store_at_ix(structure_val, index, value):

    (INDEX, ix) = index
    assert_match(INDEX, 'index')

    # find the actual memory we need to access
    # for lists it is just the python list
    if structure_val[0] == 'list':
        memory = structure_val[1]
        # compute the index
        ix_val = walk(ix)

    # for objects we access the object memory
    elif structure_val[0] == 'object':
        (OBJECT, (CLASS_ID, (ID, class_id)), (OBJECT_MEMORY,
                                              (LIST, memory))) = structure_val
        # compute the index -- for objects this has to be done
        # in the context of the class scope
        class_val = state.symbol_table.lookup_sym(class_id)
        # unpack the class value
        (CLASS, (MEMBER_NAMES, (LIST, member_names)),
         (CLASS_MEMORY, (LIST, class_memory)), (CLASS_SCOPE,
                                                class_scope)) = class_val
        state.symbol_table.push_scope(class_scope)
        ix_val = walk(ix)
        state.symbol_table.pop_scope()

    # constructor: return the argument list as memory
    # NOTE: indexing a constructor that is applied to a constant
    #       will fail since there is no memory here.
    elif structure_val[0] == 'apply-list':
        (APPLY_LIST, (LIST, [(ID, constructor_id),
                             (CHILDREN_TYPE, children)])) = structure_val
        if CHILDREN_TYPE != 'tuple':
            # if children is not a tuple make it look like a list
            memory = [(CHILDREN_TYPE, children)]
        else:
            memory = children
        # compute the index
        ix_val = walk(ix)

    else:
        raise ValueError("'{}' is not a structure".format(structure_val[0]))

    # index into memory and set the value
    if ix_val[0] == 'integer':
        memory[ix_val[1]] = value
        return

    elif ix_val[0] == 'list':
        raise ValueError("slicing in patterns not supported")

    else:
        raise ValueError("index op '{}' in patterns not supported".format(
            ix_val[0]))
def tuple_exp(node):

    (TUPLE, intuple) = node
    assert_match(TUPLE, 'tuple')

    outtuple = []

    for e in intuple:
        outtuple.append(walk(e))

    return ('tuple', outtuple)
def escape_exp(node):

    (ESCAPE, s) = node
    assert_match(ESCAPE, 'escape')

    global __retval__
    __retval__ = ('none', None)

    exec(s)

    return __retval__
def otherwise_exp(node):

    (OTHERWISE, e1, e2) = node
    assert_match(OTHERWISE, 'otherwise')

    val = walk(e1)

    if val[0] == 'none':
        return walk(e2)
    else:
        return val
def list_exp(node):

    (LIST, inlist) = node
    assert_match(LIST, 'list')

    outlist = []

    for e in inlist:
        outlist.append(walk(e))

    return ('list', outlist)
def if_exp(node):

    (IF_EXP, cond_exp, then_exp, else_exp) = node
    assert_match(IF_EXP, 'if-exp')

    (BOOLEAN, cond_val) = map2boolean(walk(cond_exp))

    if cond_val:
        return walk(then_exp)
    else:
        return walk(else_exp)
def global_stmt(node):

    (GLOBAL, (LIST, id_list)) = node
    assert_match(GLOBAL, 'global')
    assert_match(LIST, 'list')

    for id_tuple in id_list:
        (ID, id_val) = id_tuple
        if state.symbol_table.is_symbol_local(id_val):
            raise ValueError(
                "{} is already local, cannot be declared global".format(
                    id_val))
        state.symbol_table.enter_global(id_val)
def head_tail_exp(node):

    (HEAD_TAIL, head, tail) = node
    assert_match(HEAD_TAIL, 'head-tail')

    head_val = walk(head)
    (TAIL_TYPE, tail_val) = walk(tail)

    if TAIL_TYPE != 'list':
        raise ValueError(
            "unsuported tail type {} in head-tail operator".format(TAIL_TYPE))

    return ('list', [head_val] + tail_val)
def is_exp(node):

    (IS, term, pattern) = node
    assert_match(IS, 'is')

    term_val = walk(term)

    try:
        unifiers = unify(term_val, pattern)
    except PatternMatchFailed:
        return ('boolean', False)
    else:
        declare_unifiers(unifiers)
        return ('boolean', True)
def if_stmt(node):

    (IF, (LIST, if_list)) = node
    assert_match(IF, 'if')
    assert_match(LIST, 'list')

    for if_clause in if_list:

        (IF_CLAUSE, (COND, cond), (STMT_LIST, stmts)) = if_clause

        (BOOLEAN, cond_val) = map2boolean(walk(cond))

        if cond_val:
            walk(stmts)
            break
def while_stmt(node):

    (WHILE, cond_exp, body_stmts) = node
    assert_match(WHILE, 'while')

    (COND_EXP, cond) = cond_exp
    (STMT_LIST, body) = body_stmts

    try:
        (COND_TYPE, cond_val) = map2boolean(walk(cond))
        while cond_val:
            walk(body)
            (COND_TYPE, cond_val) = map2boolean(walk(cond))
    except Break:
        pass
def in_exp(node):

    (IN, exp, exp_list) = node
    assert_match(IN, 'in')

    exp_val = walk(exp)
    (EXP_LIST_TYPE, exp_list_val, *_) = walk(exp_list)

    if EXP_LIST_TYPE != 'list':
        raise ValueError("right argument to in operator has to be a list")

    # we simply map our in operator to the Python in operator
    if exp_val in exp_list_val:
        return ('boolean', True)
    else:
        return ('boolean', False)
def repeat_stmt(node):

    (REPEAT, body_stmts, cond_exp) = node
    assert_match(REPEAT, 'repeat')

    (COND_EXP, cond) = cond_exp
    (STMT_LIST, body) = body_stmts

    try:
        while True:
            walk(body)
            (COND_TYPE, cond_val) = map2boolean(walk(cond))
            if cond_val:
                break

    except Break:
        pass
def attach_stmt(node):

    (ATTACH, (FUN_EXP, fexp), (CONSTR_ID, sym)) = node
    assert_match(ATTACH, 'attach')
    assert_match(FUN_EXP, 'fun-exp')
    assert_match(CONSTR_ID, 'constr-id')

    fval = walk(fexp)

    if fval[0] != 'function':
        raise ValueError("expected a function in attach for '{}'".format(sym))
    else:
        state.symbol_table.attach_to_sym(sym, fval)
def to_list_exp(node):

    (TOLIST, (START, start), (STOP, stop), (STEP, step)) = node

    assert_match(TOLIST, 'to-list')
    assert_match(START, 'start')
    assert_match(STOP, 'stop')
    assert_match(STEP, 'step')

    (START_TYPE, start_val, *_) = walk(start)
    (STOP_TYPE, stop_val, *_) = walk(stop)
    (STEP_TYPE, step_val, *_) = walk(step)

    if START_TYPE != 'integer' or STOP_TYPE != 'integer' or STEP_TYPE != 'integer':
        raise ValueError("only integer values allowed in start, stop, or step")

    out_list_val = []

    # TODO: check out the behavior with step -1 -- is this what we want?
    # the behavior is start and stop included
    if int(step_val) > 0:  # generate the list
        ix = int(start_val)
        while ix <= int(stop_val):
            out_list_val.append(('integer', ix))
            ix += int(step_val)

    elif int(step_val) == 0:  # error
        raise ValueError("step size of 0 not supported")

    elif int(step_val) < 0:  # generate the list
        ix = int(start_val)
        while ix >= int(stop_val):
            out_list_val.append(('integer', ix))
            ix += int(step_val)

    else:
        raise ValueError("{} not a valid step value".format(step_val))

    return ('list', out_list_val)
def class_def_stmt(node):

    (CLASS_DEF, (ID, class_id), (MEMBER_LIST, (LIST, member_list))) = node
    assert_match(CLASS_DEF, 'class-def')
    assert_match(ID, 'id')
    assert_match(MEMBER_LIST, 'member-list')
    assert_match(LIST, 'list')

    # declare members
    # member names are declared as variables whose value is the slot
    # in a class object
    class_memory = [
    ]  # this will serve as a template for instanciating objects
    member_names = []
    class_scope = {}
    state.symbol_table.push_scope(class_scope)

    for member_ix in range(len(member_list)):
        member = member_list[member_ix]
        if member[0] == 'data':
            (DATA, (ID, member_id), (INIT_VAL, value)) = member
            state.symbol_table.enter_sym(member_id, ('integer', member_ix))
            class_memory.append(walk(value))
            member_names.append(member_id)
        elif member[0] == 'unify':
            (UNIFY, (ID, member_id), function_value) = member
            state.symbol_table.enter_sym(member_id, ('integer', member_ix))
            class_memory.append(function_value)
            member_names.append(member_id)
        else:
            raise ValueError("unsupported class member '{}'".format(member[0]))

    state.symbol_table.pop_scope()

    class_type = ('class', ('member-names', ('list', member_names)),
                  ('class-memory', ('list', class_memory)), ('class-scope',
                                                             class_scope))

    state.symbol_table.enter_sym(class_id, class_type)
def structure_ix_exp(node):

    (STRUCTURE_IX, structure, (INDEX_LIST, (LIST, index_list))) = node

    assert_match(STRUCTURE_IX, 'structure-ix')
    assert_match(INDEX_LIST, 'index-list')
    assert_match(LIST, 'list')

    # look at the semantics of 'structure'
    structure_val = walk(structure)

    # indexing/slicing
    # iterate over the indexes: ('index', index)
    # NOTE: index operations are left assoc. each index op produces
    # a new memory object cast as a list.  this memory object
    # is fed to the following index op.
    for ix_ix in range(0, len(index_list)):
        structure_val = read_at_ix(structure_val, index_list[ix_ix])

    return structure_val
def return_stmt(node):

    (RETURN, e) = node
    assert_match(RETURN, 'return')

    raise ReturnValue(walk(e))
def detach_stmt(node):

    (DETACH, (ID, id)) = node
    assert_match(DETACH, 'detach')

    state.symbol_table.detach_from_sym(id)
def break_stmt(node):

    (BREAK, ) = node
    assert_match(BREAK, 'break')

    raise Break()
def read_at_ix(structure_val, index):

    (INDEX, ix) = index
    assert_match(INDEX, 'index')

    # find the actual memory we need to access
    # list: return the actual list
    if structure_val[0] in ['list', 'tuple', 'string']:
        if structure_val[0] == 'list' \
        and ix[0] == 'id' \
        and ix[1] in list_member_functions:
            # we are looking at the function name of a list member
            # function - find the implementation and return it.
            impl_name = list_member_functions[ix[1]]
            return state.symbol_table.lookup_sym(impl_name)
        elif structure_val[0] == 'string' \
        and ix[0] == 'id' \
        and ix[1] in string_member_functions:
            # we are looking at the function name of a tring member
            # function - find the implementation and return it.
            impl_name = string_member_functions[ix[1]]
            return state.symbol_table.lookup_sym(impl_name)
        else:
            # get a reference to the memory
            memory = structure_val[1]
            # compute the index
            ix_val = walk(ix)

    # for objects we access the object memory
    elif structure_val[0] == 'object':
        (OBJECT, (CLASS_ID, (ID, class_id)), (OBJECT_MEMORY,
                                              (LIST, memory))) = structure_val
        # compute the index -- for objects this has to be done
        # in the context of the class scope
        class_val = state.symbol_table.lookup_sym(class_id)
        # unpack the class value
        (CLASS, (MEMBER_NAMES, (LIST, member_names)),
         (CLASS_MEMORY, (LIST, class_memory)), (CLASS_SCOPE,
                                                class_scope)) = class_val
        state.symbol_table.push_scope(class_scope)
        ix_val = walk(ix)
        state.symbol_table.pop_scope()

    # constructor: return the argument list as memory
    elif structure_val[0] == 'apply-list':
        (APPLY_LIST, (LIST, [
            (ID, constructor_id), (CHILDREN_TYPE, children)
        ])) = structure_val  # get a reference to the memory
        if CHILDREN_TYPE != 'tuple':
            # if children is not a tuple make it look like a list
            memory = [(CHILDREN_TYPE, children)]
        else:
            memory = children
        # compute the index
        ix_val = walk(ix)

    else:
        raise ValueError("'{}' is not a structure".format(structure_val[0]))

    # index into memory and get value(s)
    if ix_val[0] == 'integer':
        if structure_val[0] == 'string':
            return ('string', memory[ix_val[1]])
        else:
            return memory[ix_val[1]]

    elif ix_val[0] == 'list':
        if len(ix_val[1]) == 0:
            raise ValueError("index list is empty")

        return_memory = []
        for i in ix_val[1]:
            (IX_EXP_TYPE, ix_exp) = i

            if IX_EXP_TYPE == 'integer':
                return_memory.append(memory[ix_exp])
            else:
                raise ValueError("unsupported list index")

        if structure_val[0] == 'string':
            return ('string', "".join(return_memory))
        else:
            return ('list', return_memory)

    else:
        raise ValueError("index op '{}' not supported".format(ix_val[0]))
def throw_stmt(node):

    (THROW, object) = node
    assert_match(THROW, 'throw')

    raise ThrowValue(walk(object))
def handle_call(fval, actual_val_args):

    (FUNCTION, body_list) = fval
    assert_match(FUNCTION, 'function')

    #lhh
    #print('in handle_call')
    #print("calling: {}\nwith: {}\n\n".format(fval,actual_val_args))

    # iterate over the bodies to find one that unifies with the actual parameters
    (BODY_LIST, (LIST, body_list_val)) = body_list
    unified = False

    for body in body_list_val:

        (BODY, (PATTERN, p), (STMT_LIST, stmts)) = body

        try:
            unifiers = unify(actual_val_args, p)
            unified = True
        except PatternMatchFailed:
            unifiers = []
            unified = False

        if unified:
            break

    if not unified:
        raise ValueError(
            "none of the function bodies unified with actual parameters")

    #lhh
    #print("function unified with:")
    #print(unifiers)

    # dynamic scoping for functions!!!
    save_symtab = state.symbol_table.get_config()
    state.symbol_table.push_scope({})
    declare_formal_args(unifiers)

    # execute the function
    # function calls transfer control - save our caller's lineinfo
    old_lineinfo = state.lineinfo

    try:
        walk(stmts)
    except ReturnValue as val:
        return_value = val.value
    else:
        return_value = ('none', None
                        )  # need that in case function has no return statement

    # coming back from a function call - restore caller's lineinfo
    state.lineinfo = old_lineinfo

    # NOTE: popping the function scope is not necessary because we
    # are restoring the original symtab configuration. this is necessary
    # because a return statement might come out of a nested with statement
    state.symbol_table.set_config(save_symtab)

    return return_value
def apply_list_exp(node):

    (APPLY_LIST, (LIST, apply_list)) = node
    assert_match(APPLY_LIST, 'apply-list')

    # handle a list of apply terms:
    # e.g. inc inc 1
    # function application happens from right to left, therefore we reverse
    # a shallow copy of the apply-list
    rev_list = list(apply_list)
    rev_list.reverse()

    #lhh
    #print("rev-list: {}".format(rev_list))

    # first element must be a value to pass to a function
    arg_val = walk(rev_list[0])

    #lhh
    #print("arg_val: {}".format(arg_val))

    # step thru all the apply terms each of which needs to produce a
    # function/constructor value - the current function application
    # will produce the input value (arg_val) for the next application
    for apply_ix in range(1, len(rev_list)):
        ftree = rev_list[apply_ix]
        fval = walk(ftree)

        # object member function
        # NOTE: object member functions and functions that are embedded
        #       in a term structure built from constructors look the
        #       the same, but only object member functions are passed
        #       an object reference.
        if fval[0] == 'function' and ftree[0] == 'structure-ix':
            (STRUCTURE_IX, obj_tree, index_list) = ftree
            obj_ref = walk(obj_tree)
            # Note: lists are objects/mutable data structures, they
            # have member functions defined in the Asteroid prologue.
            if obj_ref[0] in ['object', 'list', 'string']:  # insert object ref
                if arg_val[0] == 'none':
                    arg_val = handle_call(fval, obj_ref)
                elif arg_val[0] != 'tuple':
                    new_arg_val = ('tuple', [obj_ref, arg_val])
                    arg_val = handle_call(fval, new_arg_val)
                elif arg_val[0] == 'tuple':
                    arg_val[1].insert(0, obj_ref)
                    arg_val = handle_call(fval, arg_val)
                else:
                    raise ValueError(
                        "unknown parameter type '{}' in apply".format(
                            arg_val[0]))
            else:  # it is a function embedded in a term structure - just call it
                arg_val = handle_call(fval, arg_val)

        # regular function call
        elif fval[0] == 'function':
            arg_val = handle_call(fval, arg_val)

        # constructor call
        elif fval[0] == 'constructor':  # return structure
            (ID, constructor_id) = ftree
            (ARITY, arity) = fval[1]
            # check arity match
            if arg_val[0] == 'none' and arity != 0:
                raise ValueError(
                    "constructor '{}' arity mismatch, expected {} got 0".
                    format(constructor_id, arity))
            elif arg_val[0] == 'tuple' and (len(arg_val[1]) != arity):
                raise ValueError(
                    "constructor '{}' arity mismatch, expected {} got {}".
                    format(constructor_id, arity, len(arg_val[1])))
            elif arg_val[0] != 'tuple' and arity != 1:
                raise ValueError(
                    "constructor '{}' arity mismatch, expected {} got 1".
                    format(constructor_id, arity))

            arg_val = ('apply-list', ('list', [(ID, constructor_id), arg_val]))

        # class constructor call
        elif fval[0] == 'class':
            (ID, class_id) = ftree
            (CLASS, (MEMBER_NAMES, (LIST, member_names)),
             (CLASS_MEMORY, (LIST, class_memory)), (CLASS_SCOPE,
                                                    class_scope)) = fval

            # create our object memory - memory cells now have initial values
            # need to make a deep copy
            object_memory = deepcopy(class_memory)
            # create our object
            obj_ref = ('object', ('class-id', ('id', class_id)),
                       ('object-memory', ('list', object_memory)))
            # if the class has an __init__ function call it on the object
            # NOTE: constructor functions do not have return values.
            if '__init__' in member_names:
                slot_ix = member_names.index('__init__')
                init_fval = class_memory[slot_ix]
                # calling a member function - push class scope
                state.symbol_table.push_scope(class_scope)
                if arg_val[0] == 'none':
                    handle_call(init_fval, obj_ref)
                elif arg_val[0] != 'tuple':
                    arg_val = ('tuple', [obj_ref, arg_val])
                    handle_call(init_fval, arg_val)
                elif arg_val[0] == 'tuple':
                    arg_val[1].insert(0, obj_ref)
                    handle_call(init_fval, arg_val)
                state.symbol_table.pop_scope()

            # return the new object as the next arg_val
            arg_val = obj_ref

        else:
            raise ValueError("unknown apply term '{}'".format(fval[0]))

    return arg_val