0001"""
0002builtins for pylogo
0003Ian Bicking <ianb@colorstudy.com>
0004
0005These implement the builtins, as much as possible using the standard
0006library of `UCBLogo <http://www.cs.berkeley.edu/~bh/logo.html>`_ as a
0007model.  See also the `UCBLogo Manual
0008<http://www.cs.berkeley.edu/~bh/usermanual>`_.
0009
0010Organization of this module follows the organization of the UCB
0011manual.
0012
0013All missing functions are noted in comments and marked with '@@'
0014"""
0015
0016
0017import os, random, sys
0018import operator, math, time
0019import threading
0020try:
0021    from cStringIO import StringIO
0022except ImportError:
0023    from StringIO import StringIO
0024from sets import Set
0025
0026from pylogo.common import *
0027from pylogo import reader
0028
0029class NoDefault:
0030    pass
0031
0032
0033########################################
0034## Data Structure Primitives
0035########################################
0036
0037## Constructors
0038
0039def word(*args):
0040    """
0041    WORD word1 word2
0042    (WORD word1 word2 word3 ...)
0043
0044    outputs a word formed by concatenating its inputs.
0045    """
0046    return ''.join(map(str, args))
0047word.arity = 2
0048
0049@logofunc(name='list', arity=2)
0050def logo_list(*args):
0051    """
0052    LIST thing1 thing2
0053    (LIST thing1 thing2 thing3 ...)
0054
0055    outputs a list whose members are its inputs, which can be any
0056    Logo datum (word, list, or array).
0057    """
0058    return list(args)
0059
0060def logo_repr(arg):
0061    if isinstance(arg, list):
0062        return '[%s]' % _join(map(logo_soft_repr, arg))
0063    elif isinstance(arg, str):
0064        return '"%s' % arg
0065    else:
0066        return repr(arg)
0067
0068def logo_soft_repr(arg):
0069    """
0070    Like logoRepr, only we're already in a quoted context, so
0071    we don't have to quote strings.
0072    """
0073    if isinstance(arg, str):
0074        return arg
0075    else:
0076        return logo_repr(arg)
0077
0078def _join(args):
0079    """
0080    Join the arguments with spaces, except newlines which don't
0081    need to be padded.
0082    """
0083    result = StringIO()
0084    skipSpace = True
0085    for arg in args:
0086        if skipSpace:
0087            skipSpace = False
0088        else:
0089            result.write(' ')
0090        if arg == '\n':
0091            skipSpace = True
0092        result.write(arg)
0093    return result.getvalue()
0094
0095def logo_str(arg):
0096    if isinstance(arg, list):
0097        return ' '.join(map(logo_soft_repr, arg))
0098    else:
0099        return str(arg)
0100
0101@logofunc(aliases=['se'], arity=2)
0102def sentence(*args):
0103    """
0104    SENTENCE thing1 thing2
0105    SE thing1 thing2
0106    (SENTENCE thing1 thing2 thing3 ...)
0107    (SE thing1 thing2 thing3 ...)
0108
0109    outputs a list whose members are its inputs, if those inputs are
0110    not lists, or the members of its inputs, if those inputs are lists.
0111    """
0112    result = []
0113    for arg in args:
0114        if isinstance(arg, list):
0115            result.extend(arg)
0116        else:
0117            result.append(arg)
0118    return result
0119
0120def fput(thing, l):
0121    """
0122    FPUT thing list
0123
0124    outputs a list equal to its second input with one extra member,
0125    the first input, at the beginning.
0126    """
0127    return [thing] + l
0128
0129def lput(thing, l):
0130    """
0131    LPUT thing list
0132    
0133    outputs a list equal to its second input with one extra member,
0134    the first input, at the end.
0135    """
0136    return l + [thing]
0137
0138# @@: array, mdarray, listtoarray, arraytolist
0139
0140def combine(thing1, thing2):
0141    """
0142    COMBINE thing1 thing2
0143
0144    if thing2 is a word, outputs WORD thing1 thing2.  If thing2 is a list,
0145    outputs FPUT thing1 thing2.
0146    """
0147    if wordp(thing2):
0148        return word(thing1, thing2)
0149    elif listp(thing2):
0150        return fput(thing1, thing2)
0151    else:
0152        raise ValueError
0153
0154def reverse(l):
0155    """
0156    REVERSE list
0157
0158    outputs a list whose members are the members of the input list, in
0159    reverse order.
0160    """
0161    l = l[:]
0162    l.reverse()
0163    return l
0164
0165_synnum = 0
0166_synnum_lock = threading.Lock()
0167def gensym():
0168    """
0169    GENSYM
0170
0171    outputs a unique word each time it's invoked.  The words are of the
0172    form G1, G2, etc.
0173    """
0174    global _synnum
0175    _synnum_lock.acquire()
0176    try:
0177        _synnum += 1
0178        return 'G%i' % _synnum
0179    finally:
0180        _synnum_lock.release()
0181
0182## Selectors
0183
0184def first(thing):
0185    """
0186    FIRST thing
0187
0188    if the input is a word, outputs the first character of the word.
0189    If the input is a list, outputs the first member of the list.
0190    If the input is an array, outputs the origin of the array (that
0191    is, the INDEX OF the first member of the array).
0192    """
0193    return thing[0]
0194
0195def firsts(things):
0196    """
0197    FIRSTS list
0198
0199    outputs a list containing the FIRST of each member of the input
0200    list.  It is an error if any member of the input list is empty.
0201    (The input itself may be empty, in which case the output is also
0202    empty.)  This could be written as::
0203
0204        to firsts :list
0205            output map \"first :list
0206        end
0207
0208    but is provided as a primitive in order to speed up the iteration
0209    tools MAP, MAP.SE, and FOREACH::
0210 
0211        to transpose :matrix
0212            if emptyp first :matrix [op []]
0213            op fput firsts :matrix transpose bfs :matrix
0214        end
0215    """
0216    return [first(thing) for thing in things]
0217
0218def last(thing):
0219    """
0220    LAST wordorlist
0221
0222    if the input is a word, outputs the last character of the word.
0223    If the input is a list, outputs the last member of the list.
0224    """
0225    return thing[-1]
0226
0227@logofunc(aliases=['bf'])
0228def butfirst(thing):
0229    """
0230    BUTFIRST wordorlist
0231    BF wordorlist
0232
0233    if the input is a word, outputs a word containing all but the first
0234    character of the input.  If the input is a list, outputs a list
0235    containing all but the first member of the input.
0236    """
0237    if isinstance(thing, str):
0238        return thing[1:]
0239    else:
0240        return thing[1:]
0241
0242@logofunc(aliases=['bfs'])
0243def butfirsts(things):
0244    """
0245    BUTFIRSTS list
0246    BFS list
0247
0248    outputs a list containing the BUTFIRST of each member of the input
0249    list.  It is an error if any member of the input list is empty or an
0250    array.  (The input itself may be empty, in which case the output is
0251    also empty.)  This could be written as::
0252
0253        to butfirsts :list
0254            output map \"butfirst :list
0255        end
0256
0257    but is provided as a primitive in order to speed up the iteration
0258    tools MAP, MAP.SE, and FOREACH.
0259    """
0260    return [butfirst(thing) for thing in things]
0261
0262@logofunc(aliases=['bl'])
0263def butlast(thing):
0264    """
0265    BUTLAST wordorlist
0266    BL wordorlist
0267
0268    if the input is a word, outputs a word containing all but the last
0269    character of the input.  If the input is a list, outputs a list
0270    containing all but the last member of the input.
0271    """
0272    if isinstance(thing, str):
0273        return thing[:-1]
0274    else:
0275        return thing[:-1]
0276
0277def item(index, thing):
0278    """
0279    ITEM index thing
0280
0281    if the ``thing`` is a word, outputs the ``index``th character of
0282    the word.  If the ``thing`` is a list, outputs the ``index``th
0283    member of the list.  If the ``thing`` is an array, outputs the
0284    ``index``th member of the array.  ``Index`` starts at 1 for words
0285    and lists; the starting index of an array is specified when the
0286    array is created.
0287    """
0288    if isinstance(thing, dict):
0289        return thing[index]
0290    else:
0291        return thing[index-1]
0292
0293# @@: mditem
0294
0295def pick(l):
0296    """
0297    PICK list
0298
0299    outputs a randomly chosen member of the input list.
0300    """
0301    return random.choice(l)
0302
0303def remove(thing, l):
0304    """
0305    REMOVE thing list
0306
0307    outputs a copy of ``list`` with every member equal to ``thing``
0308    removed.
0309    """
0310    l = l[:]
0311    l.remove(thing)
0312    return l
0313
0314def remdup(l):
0315    """
0316    REMDUP list
0317
0318    outputs a copy of ``list`` with duplicate members removed.  If two
0319    or more members of the input are equal, the rightmost of those
0320    members is the one that remains in the output.
0321    """
0322    new = []
0323    for item in l:
0324        if item in new:
0325            new.remove(item)
0326        new.append(item)
0327    return new
0328
0329## Mutators
0330
0331def setitem(index, thing, value):
0332    """
0333    SETITEM index array value
0334
0335    command.  Replaces the ``index``th member of ``array`` with the new
0336    ``value``.  Ensures that the resulting array is not circular, i.e.,
0337    ``value`` may not be a list or array that contains ``array``.
0338    """
0339    if isinstance(thing, dict):
0340        thing[index] = value
0341    else:
0342        thing[index-1] = value
0343    return thing
0344
0345@logofunc(name='.setfirst')
0346def dotsetfirst(lst, value):
0347    """
0348    .SETFIRST list value
0349
0350    command.  Changes the first member of ``list`` to be ``value``.
0351    WARNING: Primitives whose names start with a period are DANGEROUS.
0352    Their use by non-experts is not recommended.  The use of .SETFIRST
0353    can lead to circular list structures, which will get some Logo
0354    primitives into infinite loops; and unexpected changes to other data
0355    structures that share storage with the list being modified.
0356    """
0357    lst[0] = value
0358
0359@logofunc(name='.setbf')
0360def dotsetbf(lst, value):
0361    """
0362    .SETBF list value
0363
0364    command.  Changes the butfirst of ``list`` to be ``value``.
0365    WARNING: Primitives whose names start with a period are DANGEROUS.
0366    Their use by non-experts is not recommended.  The use of .SETBF
0367    can lead to circular list structures, which will get some Logo
0368    primitives into infinite loops; unexpected changes to other data
0369    structures that share storage with the list being modified.
0370    list.
0371    """
0372    assert isinstance(value, list), "Only a list may be passed to .SETBF (you gave: %r)" % value
0373    while len(lst) != 1:
0374        lst.pop()
0375    lst.append(value)
0376
0377## Predicates
0378
0379@logofunc(aliases=['word?'])
0380def wordp(thing):
0381    """
0382    WORDP thing
0383    WORD? thing
0384
0385    outputs TRUE if the input is a word, FALSE otherwise.
0386    """
0387    return type(thing) is str
0388
0389@logofunc(aliases=['list?'])
0390def listp(val):
0391    """
0392    LISTP thing
0393    LIST? thing
0394    
0395    outputs TRUE if the input is a list, FALSE otherwise.
0396    """
0397    return isinstance(val, list)
0398
0399@logofunc(aliases=['empty?'])
0400def emptyp(thing):
0401    """
0402    EMPTYP thing
0403    EMPTY? thing
0404
0405    outputs TRUE if the input is the empty word or the empty list,
0406    FALSE otherwise.
0407    """
0408    return thing == '' or thing == [] or thing == () or thing == {}
0409
0410@logofunc(aliases=['equal?'])
0411def equalp(thing1, thing2):
0412    """
0413    EQUALP thing1 thing2
0414    EQUAL? thing1 thing2
0415    thing1 = thing2
0416
0417    outputs TRUE if the inputs are equal, FALSE otherwise.  Two
0418    numbers are equal if they have the same numeric value.  Two
0419    non-numeric words are equal if they contain the same characters in
0420    the same order.  If there is a variable named CASEIGNOREDP whose
0421    value is TRUE, then an upper case letter is considered the same as
0422    the corresponding lower case letter.  (This is the case by
0423    default.)  Two lists are equal if their members are equal.  An
0424    array is only equal to itself; two separately created arrays are
0425    never equal even if their members are equal.  (It is important to
0426    be able to know if two expressions have the same array as their
0427    value because arrays are mutable; if, for example, two variables
0428    have the same array as their values then performing SETITEM on one
0429    of them will also change the other.)
0430    """
0431    return thing1 == thing2
0432
0433@logofunc(aliases=['before?'])
0434def beforep(word1, word2):
0435    """
0436    BEFOREP word1 word2
0437    BEFORE? word1 word2
0438
0439    outputs TRUE if word1 comes before word2 in ASCII collating
0440    sequence (for words of letters, in alphabetical order).
0441    Case-sensitivity is determined by the value of CASEIGNOREDP.  Note
0442    that if the inputs are numbers, the result may not be the same as
0443    with LESSP; for example, BEFOREP 3 12 is false because 3 collates
0444    after 1.
0445    """
0446    return word1 < word2
0447
0448@logofunc(name='.eq')
0449def doteq(thing1, thing2):
0450    """
0451    .EQ thing1 thing2
0452    
0453    outputs TRUE if its two inputs are the same datum, so that
0454    applying a mutator to one will change the other as well.  Outputs
0455    FALSE otherwise, even if the inputs are equal in value.
0456    """
0457    return thing1 is thing2
0458
0459@logofunc(aliases=['member?'])
0460def memberp(thing1, l):
0461    """
0462    MEMBERP thing1 thing2
0463    MEMBER? thing1 thing2
0464
0465    if ``thing2`` is a list or an array, outputs TRUE if ``thing1`` is
0466    EQUALP to a member of ``thing2``, FALSE otherwise.  If ``thing2``
0467    is a word, outputs TRUE if ``thing1`` is a one-character word
0468    EQUALP to a character of ``thing2``, FALSE otherwise.
0469    """
0470    return thing1 in l
0471
0472@logofunc(aliases=['substring?'])
0473def substringp(thing1, thing2):
0474    """
0475    SUBSTRINGP thing1 thing2
0476    SUBSTRING? thing1 thing2
0477
0478    if ``thing1`` or ``thing2`` is a list or an array, outputs FALSE.
0479    If ``thing2`` is a word, outputs TRUE if ``thing1`` is EQUALP to a
0480    substring of ``thing2``, FALSE otherwise.
0481    """
0482    return type(thing2) is str and type(thing1) is str              and thing2.find(thing1) != -1
0484
0485@logofunc(aliases=['number?'])
0486def numberp(thing):
0487    """
0488    NUMBERP thing
0489    NUMBER? thing
0490
0491    outputs TRUE if the input is a number, FALSE otherwise.
0492    """
0493    return type(thing) is int or type(thing) is float
0494
0495## Queries
0496
0497def count(thing):
0498    """
0499    COUNT thing
0500
0501    outputs the number of characters in the input, if the input is a
0502    word; outputs the number of members in the input, if it is a list
0503    or an array.  (For an array, this may or may not be the index of
0504    the last member, depending on the array's origin.)
0505    """
0506    return len(thing)
0507
0508def ascii(char):
0509    """
0510    ASCII char
0511
0512    outputs the integer (between 0 and 255) that represents the input
0513    character in the ASCII code.
0514    """
0515    ## UCB: Interprets control characters as representing backslashed
0516    ## punctuation, and returns the character code for the
0517    ## corresponding punctuation character without backslash.
0518    ## (Compare RAWASCII.)
0519    return ord(char)
0520
0521def char(int):
0522    """
0523    CHAR int
0524    
0525    outputs the character represented in the ASCII code by the input,
0526    which must be an integer between 0 and 255.
0527    """
0528    return chr(int)
0529
0530def member(thing1, thing2):
0531    """
0532    MEMBER thing1 thing2
0533
0534    if ``thing2`` is a word or list and if MEMBERP with these inputs
0535    would output TRUE, outputs the portion of ``thing2`` from the
0536    first instance of ``thing1`` to the end.  If MEMBERP would output
0537    FALSE, outputs the empty word or list according to the type of
0538    ``thing2``.  It is an error for ``thing2`` to be an array.
0539    """
0540    if isinstance(thing2, basestring):
0541        i = thing2.find(thing1)
0542        if i == -1:
0543            return ''
0544        else:
0545            return thing2[i:]
0546    else:
0547        try:
0548            i = thing2.index(thing1)
0549        except ValueError:
0550            return []
0551        else:
0552            return thing2[i:]
0553
0554def lowercase(word):
0555    """
0556    LOWERCASE word
0557
0558    outputs a copy of the input word, but with all uppercase letters
0559    changed to the corresponding lowercase letter.
0560    """
0561    return word.lower()
0562
0563def uppercase(word):
0564    """
0565    UPPERCASE word
0566
0567    outputs a copy of the input word, but with all lowercase letters
0568    changed to the corresponding uppercase letter.
0569    """
0570    return word.upper()
0571
0572# @@: standout, parse, runparse
0573
0574########################################
0575## Communication
0576########################################
0577
0578##############################
0579## Transmitters
0580
0581@logofunc(aliases=['print'], arity=-1)
0582def pr(*args):
0583    """
0584    PRINT thing thing2 thing3 ...
0585    PR thing
0586    (PRINT thing1 thing2 ...)
0587    (PR thing1 thing2 ...)
0588
0589    command.  Prints the input or inputs to the current write stream
0590    (initially the terminal).  All the inputs are printed on a single
0591    line, separated by spaces, ending with a newline.  If an input is a
0592    list, square brackets are not printed around it, but brackets are
0593    printed around sublists.  Braces are always printed around arrays.
0594    """
0595    trans = []
0596    for arg in args:
0597        if isinstance(arg, list):
0598            trans.append(_join(map(logo_soft_repr, arg)))
0599        else:
0600            trans.append(logo_soft_repr(arg))
0601    print ' '.join(trans)
0602
0603@logofunc(name='type', arity=-1)
0604def logo_type(*args):
0605    """
0606    TYPE thing thing2 thing3 ...
0607    (TYPE thing1 thing2 ...)
0608    
0609    command.  Prints the input or inputs like PRINT, except that no
0610    newline character is printed at the end and multiple inputs are
0611    not separated by spaces.
0612    """
0613    trans = []
0614    for arg in args:
0615        if isinstance(arg, list):
0616            trans.append(_join(map(logo_soft_repr, arg)))
0617        else:
0618            trans.append(logo_soft_repr(arg))
0619    sys.stdout.write(' '.join(trans))
0620    sys.stdout.flush()
0621
0622def show(*args):
0623    """
0624    SHOW thing thing2 thing3 ...
0625    (SHOW thing1 thing2 ...)
0626    
0627    command.  Prints the input or inputs like PRINT, except that
0628    if an input is a list it is printed inside square brackets.
0629    """
0630    print ' '.join(map(repr(args)))
0631
0632##############################
0633## Receivers
0634
0635def readlist():
0636    """
0637    READLIST
0638    RL
0639
0640    reads a line from the read stream (initially the terminal) and
0641    outputs that line as a list.  The line is separated into members
0642    as though it were typed in square brackets in an instruction.  If
0643    the read stream is a file, and the end of file is reached,
0644    READLIST outputs the empty word (not the empty list).  READLIST
0645    processes backslash, vertical bar, and tilde characters in the
0646    read stream; the output list will not contain these characters but
0647    they will have had their usual effect.  READLIST does not,
0648    however, treat semicolon as a comment character.
0649    """
0650    tokenizer = reader.FileTokenizer(sys.stdin)
0651    result = []
0652    while 1:
0653        tok = tokenizer.next()
0654        if tok is reader.EOF or tok == '\n':
0655            break
0656        result.append(tok)
0657    return result
0658
0659def readrawline():
0660    """
0661    READRAWLINE
0662
0663    reads a line from the read stream and outputs that line as a word.
0664    The output is a single word even if the line contains spaces,
0665    brackets, etc.  If the read stream is a file, and the end of file is
0666    reached, READRAWLINE outputs the empty list (not the empty word).
0667    READRAWLINE outputs the exact string of characters as they appear
0668    in the line, with no special meaning for backslash, vertical bar,
0669    tilde, or any other formatting characters.
0670    """
0671    try:
0672        v = sys.stdin.readline()
0673        if not v:
0674            return []
0675        # remove trailing newline
0676        return v[:-1]
0677    except EOFError:
0678        return []
0679
0680# @@: readchars/rcs, shell
0681
0682##############################
0683## File access
0684
0685## See pylogo.oobuiltins
0686
0687##############################
0688## Terminal Access
0689
0690# @@: all unimplemented
0691
0692##############################
0693## Arithmetic
0694
0695@logofunc(arity=2)
0696def sum(*args):
0697    """
0698    SUM num1 num2
0699    (SUM num1 num2 num3 ...)
0700    num1 + num2
0701
0702    outputs the sum of its inputs.
0703    """
0704    return reduce(operator.add, args)
0705
0706def difference(num1, num2):
0707    """
0708    DIFFERENCE num1 num2
0709    num1 - num2
0710    
0711    outputs the difference of its inputs.  Minus sign means infix
0712    difference in ambiguous contexts (when preceded by a complete
0713    expression), unless it is preceded by a space and followed
0714    by a nonspace.  (See also MINUS.)
0715    """
0716    return num1 - num2
0717
0718def minus(num):
0719    """
0720    MINUS num
0721    - num
0722
0723    outputs the negative of its input.  Minus sign means unary minus if
0724    the previous token is an infix operator or open parenthesis, or it is
0725    preceded by a space and followed by a nonspace.  There is a difference
0726    in binding strength between the two forms::
0727
0728        MINUS 3 + 4     ; means    -(3+4)
0729        - 3 + 4         ; means    (-3)+4
0730    """
0731    return -num
0732
0733@logofunc(arity=2)
0734def product(*args):
0735    """
0736    PRODUCT num1 num2
0737    (PRODUCT num1 num2 num3 ...)
0738    num1 * num2
0739
0740    outputs the product of its inputs.
0741    """
0742    return reduce(operator.mul, args)
0743
0744def quotient(num1, num2):
0745    """
0746    QUOTIENT num1 num2
0747    (QUOTIENT num)
0748    num1 / num2
0749
0750    outputs the quotient of its inputs.  The quotient of two integers
0751    is an integer if and only if the dividend is a multiple of the divisor.
0752    (In other words, QUOTIENT 5 2 is 2.5, not 2, but QUOTIENT 4 2 is
0753    2, not 2.0 -- it does the right thing.)  With a single input,
0754    QUOTIENT outputs the reciprocal of the input.
0755    """
0756    return num1 / num2
0757
0758def remainder(num1, num2):
0759    """
0760    REMAINDER num1 num2
0761
0762    outputs the remainder on dividing ``num1`` by ``num2``; both must
0763    be integers and the result is an integer with the same sign as
0764    num1.
0765    """
0766    v = num1 % num2
0767    if v < 0 and num1 > 0 or v > 0 and num1 < 0:
0768        v = v + num1
0769    return v
0770
0771def modulo(num1, num2):
0772    """
0773    MODULO num1 num2
0774
0775    outputs the remainder on dividing ``num1`` by ``num2``; both must be
0776    integers and the result is an integer with the same sign as num2.
0777    """
0778    return num1 % num2
0779
0780@logofunc(name='int')
0781def logo_int(num):
0782    """
0783    INT num
0784
0785    outputs its input with fractional part removed, i.e., an integer
0786    with the same sign as the input, whose absolute value is the
0787    largest integer less than or equal to the absolute value of
0788    the input.
0789    """
0790    return int(num)
0791
0792@logofunc(name='round', arity=1)
0793def logo_round(v, *args):
0794    """
0795    ROUND num
0796
0797    outputs the nearest integer to the input.
0798    """
0799    return round(v, *args)
0800
0801@logofunc(name='abs')
0802def logo_abs(v):
0803    return abs(v)
0804
0805def sqrt(v):
0806    """
0807    SQRT num
0808
0809    outputs the square root of the input, which must be nonnegative.
0810    """
0811    return math.sqrt(v)
0812
0813def power(num1, num2):
0814    """
0815    POWER num1 num2
0816
0817    outputs ``num1`` to the ``num2`` power.  If num1 is negative, then
0818    num2 must be an integer.
0819    """
0820    ## @@: integer not required with negative num1...?
0821    return num1**num2
0822
0823def exp(v):
0824    """
0825    EXP num
0826
0827    outputs e (2.718281828+) to the input power.
0828    """
0829    return math.exp(v)
0830
0831def log10(v):
0832    """
0833    LOG10 num
0834
0835    outputs the common logarithm of the input.
0836    """
0837    return math.log10(v)
0838
0839def ln(v):
0840    """
0841    LN num
0842
0843    outputs the natural logarithm of the input.
0844    """
0845    return math.log(v)
0846
0847def _to_degrees(num):
0848    return (num / math.pi) * 180
0849def _to_radians(num):
0850    return (num / 180.0) * math.pi
0851
0852def sin(num):
0853    """
0854    SIN degrees
0855
0856    outputs the sine of its input, which is taken in degrees.
0857    """
0858    return math.sin(_to_radians(num))
0859
0860def radsin(v):
0861    """
0862    RADSIN radians
0863
0864    outputs the sine of its input, which is taken in radians.
0865    """
0866    return math.sin(v)
0867
0868def cos(num):
0869    """
0870    COS degrees
0871
0872    outputs the cosine of its input, which is taken in degrees.
0873    """
0874    return math.cos(_to_radians(num))
0875
0876def radcos(v):
0877    """
0878    RADCOS radians
0879
0880    outputs the cosine of its input, which is taken in radians.
0881    """
0882    return math.cos(v)
0883
0884def arctan(num, second=None):
0885    """
0886    ARCTAN num
0887    (ARCTAN x y)
0888
0889    outputs the arctangent, in degrees, of its input.  With two
0890    inputs, outputs the arctangent of y/x, if x is nonzero, or
0891    90 or -90 depending on the sign of y, if x is zero.
0892    """
0893    return _to_degrees(radarctan(num, second))
0894
0895def radarctan(num, second=None):
0896    """
0897    RADARCTAN num
0898    (RADARCTAN x y)
0899
0900    outputs the arctangent, in radians, of its input.  With two
0901    inputs, outputs the arctangent of y/x, if x is nonzero, or
0902    pi/2 or -pi/2 depending on the sign of y, if x is zero.
0903    
0904    The expression 2*(RADARCTAN 0 1) can be used to get the
0905    value of pi.
0906    """
0907    if second is not None:
0908        return math.atan2(num, second)
0909    else:
0910        return math.atan(num)
0911
0912def iseq(from_num, to_num):
0913    """
0914    ISEQ from to
0915
0916    outputs a list of the integers from FROM to TO, inclusive::
0917
0918        ? show iseq 3 7
0919        [3 4 5 6 7]
0920        ? show iseq 7 3
0921        [7 6 5 4 3]
0922    """
0923    return range(from_num, to_num+1)
0924
0925def rseq(from_num, to_num, length):
0926    """
0927    RSEQ from to count
0928
0929    outputs a list of COUNT equally spaced rational numbers
0930    between FROM and TO, inclusive::
0931
0932        ? show rseq 3 5 9
0933        [3 3.25 3.5 3.75 4 4.25 4.5 4.75 5]
0934        ? show rseq 3 5 5
0935        [3 3.5 4 4.5 5]
0936    """
0937    result = [from_num + (float(i)*(to_num-from_num)/(length-1))
0938              for i in range(length-1)]
0939    result.append(to_num)
0940    return result
0941
0942##############################
0943## Predicates
0944
0945@logofunc(aliases=['less?'])
0946def lessp(num1, num2):
0947    """
0948    LESSP num1 num2
0949    LESS? num1 num2
0950    num1 < num2
0951    
0952    outputs TRUE if its first input is strictly less than its second.
0953    """
0954    return num1 < num2
0955
0956@logofunc(aliases=['greater?'])
0957def greaterp(num1, num2):
0958    """
0959    GREATERP num1 num2
0960    GREATER? num1 num2
0961    num1 > num2
0962
0963    outputs TRUE if its first input is strictly greater than its second.
0964    """
0965    return num1 > num2
0966
0967##############################
0968## Random Numbers
0969
0970@logofunc(name='random')
0971def logo_random(num):
0972    """
0973    RANDOM num
0974
0975    outputs a random nonnegative integer less than its input, which
0976    must be an integer.
0977    """
0978    return random.randint(0, num-1)
0979
0980def rerandom(seed=None):
0981    """
0982    RERANDOM
0983    (RERANDOM seed)
0984
0985    command.  Makes the results of RANDOM reproducible.  Ordinarily
0986    the sequence of random numbers is different each time Logo is
0987    used.  If you need the same sequence of pseudo-random numbers
0988    repeatedly, e.g. to debug a program, say RERANDOM before the
0989    first invocation of RANDOM.  If you need more than one repeatable
0990    sequence, you can give RERANDOM an integer input; each possible
0991    input selects a unique sequence of numbers.
0992    """
0993    if seed is None:
0994        seed = time.time()
0995    random.seed(seed)
0996
0997##############################
0998## Print Formatting
0999
1000# @@: form
1001
1002##############################
1003## Bitwise operations
1004
1005@logofunc(arity=2)
1006def bitand(*args):
1007    """
1008    BITAND num1 num2
1009    (BITAND num1 num2 num3 ...)
1010
1011    outputs the bitwise AND of its inputs, which must be integers.
1012    """
1013    return reduce(operator.and_, args)
1014
1015@logofunc(arity=2)
1016def bitor(*args):
1017    """
1018    BITOR num1 num2
1019    (BITOR num1 num2 num3 ...)
1020
1021    outputs the bitwise OR of its inputs, which must be integers.
1022    """
1023    return reduce(operator.or_, args)
1024
1025@logofunc(arity=2)
1026def bitxor(*args):
1027    """
1028    BITXOR num1 num2
1029    (BITXOR num1 num2 num3 ...)
1030
1031    outputs the bitwise EXCLUSIVE OR of its inputs, which must be
1032    integers.
1033    """
1034    return reduce(operator.xor, args)
1035
1036def bitnot(num):
1037    """
1038    BITNOT num
1039
1040    outputs the bitwise NOT of its input, which must be an integer.
1041    """
1042    return ~num
1043
1044def ashift(num1, num2):
1045    """
1046    ASHIFT num1 num2
1047
1048    outputs ``num1`` arithmetic-shifted to the left by ``num2`` bits.
1049    If num2 is negative, the shift is to the right with sign
1050    extension.  The inputs must be integers.
1051    """
1052    return num1 >> num2
1053
1054def lshift(num1, num2):
1055    """
1056    LSHIFT num1 num2
1057
1058    outputs ``num1`` logical-shifted to the left by ``num2`` bits.
1059    If num2 is negative, the shift is to the right with zero fill.
1060    The inputs must be integers.
1061    """
1062    return num1 << num2
1063
1064##############################
1065## Logical statements
1066
1067@logofunc(name='and', aware=True, arity=2)
1068def logo_and(interp, *vals):
1069    """
1070    AND tf1 tf2
1071    (AND tf1 tf2 tf3 ...)
1072    
1073    outputs TRUE if all inputs are TRUE, otherwise FALSE.  An input
1074    can be a list, in which case it is taken as an expression to run;
1075    that expression must produce a TRUE or FALSE value.  List
1076    expressions are evaluated from left to right; as soon as a FALSE
1077    value is found, the remaining inputs are not examined.  Example::
1078
1079        MAKE \"RESULT AND [NOT (:X = 0)] [(1 / :X) > .5]
1080
1081    to avoid the division by zero if the first part is false.
1082    """
1083    for val in vals:
1084        if isinstance(val, list):
1085            val = interp.eval(val)
1086        if not val:
1087            return False
1088    return True
1089
1090@logofunc(name='or', aware=True, arity=2)
1091def logo_or(interp, *vals):
1092    """
1093    OR tf1 tf2
1094    (OR tf1 tf2 tf3 ...)
1095
1096    outputs TRUE if any input is TRUE, otherwise FALSE.  An input can
1097    be a list, in which case it is taken as an expression to run; that
1098    expression must produce a TRUE or FALSE value.  List expressions
1099    are evaluated from left to right; as soon as a TRUE value is
1100    found, the remaining inputs are not examined.  Example::
1101
1102        IF OR :X=0 [some.long.computation] [...]
1103
1104    to avoid the long computation if the first condition is met.
1105    """
1106    for val in vals:
1107        if isinstance(val, list):
1108            val = interp.eval(val)
1109        if val:
1110            return True
1111    return False
1112
1113@logofunc(name='not', aware=True)
1114def logo_not(interp, val):
1115    """
1116    NOT tf
1117
1118    outputs TRUE if the input is FALSE, and vice versa.  The input can be
1119    a list, in which case it is taken as an expression to run; that
1120    expression must produce a TRUE or FALSE value.
1121    """
1122    if isinstance(val, list):
1123        val = interp.eval(val)
1124    return not val
1125
1126
1127########################################
1128## Graphics
1129########################################
1130
1131# @@: from logo_turtle.py
1132
1133########################################
1134## Workspace Management
1135########################################
1136
1137##############################
1138## Procedure Definition
1139
1140@logofunc(name='define', aware=True)
1141def logo_define(interp, procname, text):
1142    """
1143    DEFINE procname text
1144
1145    command.  Defines a procedure with name ``procname`` and text
1146    ``text``.  If there is already a procedure with the same name, the
1147    new definition replaces the old one.  The text input must be a
1148    list whose members are lists.  The first member is a list of
1149    inputs; it looks like a TO line but without the word TO, without
1150    the procedure name, and without the colons before input names.  In
1151    other words, the members of this first sublist are words for the
1152    names of required inputs and lists for the names of optional or
1153    rest inputs.  The remaining sublists of the text input make up the
1154    body of the procedure, with one sublist for each instruction line
1155    of the body.  (There is no END line in the text input.)
1156    """
1157    body = []
1158    args = text[0]
1159    for l in text[1:]:
1160        body.extend(l)
1161        body.append('\n')
1162    func = interpreter.UserFunction(procname, args, None, body)
1163    interp.setFunction(procname, func)
1164
1165def text(interp, procname):
1166    """
1167    TEXT procname
1168    
1169    outputs the text of the procedure named ``procname`` in the form
1170    expected by DEFINE: a list of lists, the first of which describes
1171    the inputs to the procedure and the rest of which are the lines of
1172    its body.  The text does not reflect formatting information used
1173    when the procedure was defined, such as continuation lines and
1174    extra spaces.
1175    """
1176    func = interp.get_function(procname)
1177    if not isinstance(func, interpreter.UserFunction):
1178        return []
1179    args = func.vars
1180    body = [args]
1181    lastline = []
1182    for tok in func.body:
1183        if tok == '\n':
1184            body.append(lastline)
1185            lastline = []
1186        else:
1187            lastline.append(tok)
1188    if lastline:
1189        body.append(lastline)
1190    return body
1191
1192
1193# @@: fulltext, copydef
1194
1195##############################
1196## Variable Definition
1197
1198# builtin: make, local, localmake
1199
1200@logofunc(aware=True)
1201def thing(interp, v):
1202    """
1203    THING varname
1204    :quoted.varname
1205
1206    outputs the value of the variable whose name is the input.  If
1207    there is more than one such variable, the innermost local variable
1208    of that name is chosen.  The colon notation is an abbreviation not
1209    for THING but for the combination so that :FOO means THING \"FOO.
1210    """
1211    return interp.get_variable(v)
1212
1213# @@: global
1214
1215##############################
1216## Property Lists
1217
1218# @@: ucbcompat
1219
1220##############################
1221## Predicates
1222
1223@logofunc(aliases=['procedure?'], aware=True)
1224def procedurep(interp, name):
1225    """
1226    PROCEDUREP name
1227    PROCEDURE? name
1228
1229    outputs TRUE if the input is the name of a procedure.
1230    """
1231    return interp.root.functions.has_key(name)
1232
1233@logofunc(aliases=['primitive?'], aware=True)
1234def primitivep(interp, name):
1235    """
1236    PRIMITIVEP name
1237    PRIMITIVE? name
1238
1239    outputs TRUE if the input is the name of a primitive procedure
1240    (one built into Logo).
1241    """
1242    try:
1243        func = interp.get_function(name)
1244    except LogoNameError:
1245        return False
1246    return not isinstance(func, intepreter.UserFunction)
1247
1248@logofunc(aliases=['defined?'], aware=True)
1249def definedp(interp, name):
1250    """
1251    DEFINEDP name
1252    DEFINED? name
1253
1254    outputs TRUE if the input is the name of a user-defined procedure.
1255    """
1256    try:
1257        func = interp.get_function(name)
1258    except LogoNameError:
1259        return False
1260    return isinstance(func, intepreter.UserFunction)
1261
1262@logofunc(aliases=['name?'], aware=True)
1263def namep(interp, name):
1264    """
1265    NAMEP name
1266    NAME? name
1267
1268    outputs TRUE if the input is the name of a variable.
1269    """
1270    try:
1271        interp.get_variable(name)
1272        return True
1273    except LogoNameError:
1274        return False
1275
1276##############################
1277## Queries
1278
1279# @@: contents, buried, traced, stepped
1280
1281@logofunc(aware=True)
1282def procedures(interp):
1283    """
1284    PROCEDURES
1285
1286    outputs a list of the names of all unburied user-defined procedures
1287    in the workspace.  
1288    """
1289    return interp.functions.keys()
1290
1291@logofunc(aware=True)
1292def names(interp):
1293    """
1294    NAMES
1295
1296    outputs a contents list consisting of an empty list (indicating
1297    no procedure names) followed by a list of all unburied variable
1298    names in the workspace.
1299    """
1300    return interp.variable_names()
1301
1302# @@: plists, namelist, pllist, nodes
1303
1304##############################
1305## Inspection
1306
1307# @@: po, poall, pops, pons, popls, pon, popl, pot, pots
1308
1309##############################
1310## Workspace Control
1311
1312@logofunc(aliases=['er'], aware=True)
1313def erase(interp, l):
1314    """
1315    ERASE contentslist
1316    ER contentslist
1317
1318    command.  Erases from the workspace the procedures, variables,
1319    and property lists named in the input.  Primitive procedures may
1320    not be erased unless the variable REDEFP has the value TRUE.
1321    """
1322    if type(l) is str:
1323        l = [l]
1324    for n in l:
1325        interp.erase_name(n)
1326
1327@logofunc(aware=True)
1328def erall(interp):
1329    """
1330    ERALL
1331
1332    command.  Erases all unburied procedures, variables, and property
1333    lists from the workspace.  Abbreviates ERASE CONTENTS.
1334    """
1335    # @@: No buried makes this dangerous
1336    erase(interp, names(interp))
1337    erase(interp, procedures(interp))
1338
1339@logofunc(aware=True)
1340def erps(interp):
1341    """
1342    ERPS
1343
1344    command.  Erases all unburied procedures from the workspace.
1345    Abbreviates ERASE PROCEDURES.
1346    """
1347    erase(interp, procedures(interp))
1348
1349@logofunc(aware=True)
1350def erns(interp):
1351    """
1352    ERNS
1353
1354    command.  Erases all unburied variables from the workspace.
1355    Abbreviates ERASE NAMES.
1356    """
1357    erase(interp, names(interp))
1358
1359# @@: ern, erpl, bury, buryall, buryname, unbury, unburyall
1360# @@: buriedp/buried?, trace, untrace, tracedp/traced?, step, unstep
1361# @@: steppedp/stepped?, edit/ed, editfile, edall, edps, edns, edpls,
1362# @@: edn, edpl, savel
1363
1364@logofunc(aware=True)
1365def load(interp, name):
1366    if name.endswith('.logo'):
1367        _load_logo(interp, name)
1368    elif name.endswith('.py'):
1369        _load_python(interp, name)
1370    elif os.path.exists(name + ".logo"):
1371        _load_logo(interp, name + ".logo")
1372    elif os.path.exists(name + ".py"):
1373        _load_python(interp, name + ".py")
1374    else:
1375        _load_python(interp, name)
1376
1377def _load_logo(interp, name):
1378    interp.import_logo(name)
1379
1380def _load_python(interp, name):
1381    if name.endswith('.py'):
1382        name = name[:-3]
1383    mod = __import__(name)
1384    interp.import_module(mod)
1385
1386@logofunc(name='help', aware=True)
1387def logo_help(interp, name):
1388    """
1389    HELP name
1390
1391    command.  Prints information from the reference manual about the
1392    primitive procedure named by the input.
1393    """
1394    try:
1395        func = interp.get_function(name)
1396    except NameError:
1397        print "I don't know how  to %s" % name
1398        return
1399    doc = func.__doc__
1400    if not doc:
1401        print "No help available for %s" % name
1402        return
1403    import textwrap
1404    doc = textwrap.dedent(doc).strip('\n\r')
1405    print doc
1406
1407
1408# @@: gc
1409
1410########################################
1411## Control Structures
1412########################################
1413
1414@logofunc(name='run', aware=True)
1415def logo_run(interp, l):
1416    """
1417    RUN instructionlist
1418    
1419    command or operation.  Runs the Logo instructions in the input
1420    list; outputs if the list contains an expression that outputs.
1421    """
1422    try:
1423        interp.eval(l)
1424    except LogoOutput, e:
1425        return e.value
1426    return None
1427
1428# @@: runresult
1429
1430@logofunc(name='eval', aware=True)
1431def logo_eval(interp, l):
1432    return interp.eval(l)
1433
1434@logofunc(aware=True)
1435def repeat(interp, n, block):
1436    """
1437    REPEAT num instructionlist
1438
1439    command.  Runs the ``instructionlist`` repeatedly, ``num`` times.
1440    """
1441    lastVal = None
1442    if hasattr(interp, '_repcount'):
1443        lastrepcount = interp._repcount
1444    try:
1445        for i in xrange(n):
1446            interp._repcount = i+1
1447            try:
1448                lastVal = interp.eval(block)
1449            except LogoContinue:
1450                pass
1451    except LogoBreak:
1452        lastVal = None
1453        pass
1454    try:
1455        setattr(interp, '_repcount', lastrepcount)
1456    except NameError:
1457        del interp._repcount
1458    return lastVal
1459
1460@logofunc(aware=True)
1461def forever(interp, block):
1462    if hasattr(interp, '_repcount'):
1463        lastrepcount = interp._repcount
1464    interp._repcount = 0
1465    try:
1466        while 1:
1467            try:
1468                interp._repcount += 1
1469                logo_eval(interp, block)
1470            except LogoContinue:
1471                pass
1472    except LogoBreak:
1473        pass
1474    try:
1475        setattr(interp, '_repcount', lastrepcount)
1476    except NameError:
1477        del interp._repcount
1478
1479@logofunc(aware=True)
1480def repcount(interp):
1481    """
1482    REPCOUNT
1483
1484    outputs the repetition count of the innermost current REPEAT or
1485    FOREVER, starting from 1.  If no REPEAT or FOREVER is active,
1486    outputs -1.
1487    """
1488    try:
1489        return interp._repcount
1490    except AttributeError:
1491        return -1
1492
1493@logofunc(aware=True)
1494def test(interp, val):
1495    interp.set_variable_local('lasttestvalue', val)
1496
1497@logofunc(aliases=['ift'], aware=True)
1498def iftrue(interp, lst):
1499    """
1500    IFTRUE instructionlist
1501    IFT instructionlist
1502
1503    command.  Runs its input if the most recent TEST instruction had
1504    a TRUE input.  The TEST must have been in the same procedure or a
1505    superprocedure.
1506    """
1507    if interp.get_variable('lasttestvalue'):
1508        return interp.eval(lst)
1509
1510@logofunc(aliases=['iff'], aware=True)
1511def iffalse(interp, lst):
1512    """
1513    IFFALSE instructionlist
1514    IFF instructionlist
1515
1516    command.  Runs its input if the most recent TEST instruction had
1517    a FALSE input.  The TEST must have been in the same procedure or a
1518    superprocedure.
1519    """
1520    if not interp.get_variable('lasttestvalue'):
1521        return interp.eval(lst)
1522
1523@logofunc(name='if', aware=True)
1524def logo_if(interp, expr, block, elseBlock=None):
1525    """
1526    IF tf instructionlist
1527    (IF tf instructionlist1 instructionlist2)
1528
1529    command.  If the first input has the value TRUE, then IF runs
1530    the second input.  If the first input has the value FALSE, then
1531    IF does nothing.  (If given a third input, IF acts like IFELSE,
1532    as described below.)  It is an error if the first input is not
1533    either TRUE or FALSE.
1534    """
1535    if expr:
1536        return logo_eval(interp, block)
1537    elif elseBlock is not None:
1538        return logo_eval(interp, elseBlock)
1539
1540@logofunc(name='ifelse', aware=True)
1541def logo_ifelse(interp, expr, trueBlock, falseBlock):
1542    """
1543    IFELSE tf instructionlist1 instructionlist2
1544
1545    command or operation.  If the first input has the value TRUE, then
1546    IFELSE runs the second input.  If the first input has the value FALSE,
1547    then IFELSE runs the third input.  IFELSE outputs a value if the
1548    instructionlist contains an expression that outputs a value.
1549    """
1550    if expr:
1551        return logo_eval(interp, trueBlock)
1552    else:
1553        return logo_eval(interp, falseBlock)
1554
1555@logofunc(name='break')
1556def logo_break():
1557    raise LogoBreak()
1558
1559@logofunc()
1560def stop():
1561    """
1562    STOP
1563
1564    command.  Ends the running of the procedure in which it appears.
1565    Control is returned to the context in which that procedure was
1566    invoked.  The stopped procedure does not output a value.
1567    """
1568    raise LogoOutput(None)
1569
1570@logofunc(aliases=['op', 'return'])
1571def output(value):
1572    """
1573    OUTPUT value
1574    OP value
1575    RETURN value
1576
1577    command.  Ends the running of the procedure in which it appears.
1578    That procedure outputs the value ``value`` to the context in which
1579    it was invoked.  Don't be confused: OUTPUT itself is a command,
1580    but the procedure that invokes OUTPUT is an operation.
1581    """
1582    raise LogoOutput(value)
1583
1584# @@: catch, throw, error, pause
1585
1586@logofunc(name='continue', aliases=['co'])
1587def logo_continue():
1588    raise LogoContinue()
1589
1590def bye():
1591    sys.exit()
1592
1593# @@: goto, tag
1594
1595def ignore(value):
1596    """
1597    IGNORE value
1598
1599    command.  Does nothing.  Used when an expression is evaluated for
1600    a side effect and its actual value is unimportant.
1601    """
1602    pass
1603
1604@logofunc(aware=True)
1605def backtick(interp, lst):
1606    """
1607    BACKTICK list
1608
1609    outputs a list equal to its input but with certain substitutions.
1610    If a member of the input list is the word ``,`` (comma) then the
1611    following member should be an instructionlist that produces an
1612    output when run.  That output value replaces the comma and the
1613    instructionlist.  If a member of the input list is the word ``@``
1614    (atsign) then the following member should be an instructionlist
1615    that outputs a list when run.  The members of that list replace the
1616    @ and the instructionlist.  Example::
1617
1618        show `[foo baz ,[bf [a b c]] garply @[bf [a b c]]]
1619
1620    will print::
1621
1622        [foo baz [b c] garply b c]
1623    """
1624    result = []
1625    remaining = lst
1626    while remaining:
1627        if remaining[0] == ',':
1628            result.append(interp.eval(remaining[1]))
1629            remaining = remaining[2:]
1630        elif remaining[0] == '@':
1631            result.extend(interp.eval(remaining[1]))
1632            remaining = remaining[2:]
1633        else:
1634            result.append(remaining[0])
1635            remaining = remaining[1:]
1636    return result
1637
1638@logofunc(name='for', aware=True)
1639def logo_for(interp, forcontrol, lst):
1640    """
1641    FOR forcontrol instructionlist
1642
1643    command.  The first input must be a list containing three or four
1644    members: (1) a word, which will be used as the name of a local
1645    variable; (2) a word or list that will be evaluated as by RUN to
1646    determine a number, the starting value of the variable; (3) a word
1647    or list that will be evaluated to determine a number, the limit value
1648    of the variable; (4) an optional word or list that will be evaluated
1649    to determine the step size.  If the fourth member is missing, the
1650    step size will be 1 or -1 depending on whether the limit value is
1651    greater than or less than the starting value, respectively.
1652    
1653    The second input is an instructionlist.  The effect of FOR is to run
1654    that instructionlist repeatedly, assigning a new value to the control
1655    variable (the one named by the first member of the forcontrol list)
1656    each time.  First the starting value is assigned to the control
1657    variable.  Then the value is compared to the limit value.  FOR is
1658    complete when the sign of (current - limit) is the same as the sign
1659    of the step size.  (If no explicit step size is provided, the
1660    instructionlist is always run at least once.  An explicit step size
1661    can lead to a zero-trip FOR, e.g., FOR [I 1 0 1] ...)  Otherwise, the
1662    instructionlist is run, then the step is added to the current value
1663    of the control variable and FOR returns to the comparison step::
1664
1665        ? for [i 2 7 1.5] [print :i]
1666        2
1667        3.5
1668        5
1669        6.5
1670        ?
1671    """
1672    var = forcontrol[0]
1673    start = _op_run(forcontrol[1])
1674    end = _op_run(forcontrol[2])
1675    if len(forcontrol) > 3:
1676        step = _op_run(forcontrol[3])
1677    else:
1678        if start > end:
1679            step = -1
1680        else:
1681            step = 1
1682    curr = start
1683    while 1:
1684        if step > 0:
1685            if curr > end:
1686                break
1687        else:
1688            if curr < end:
1689                break
1690        interp.set_variable(var, curr)
1691        interp.eval(lst)
1692        curr += step
1693
1694def _op_run(interp, v):
1695    if isinstance(v, str):
1696        v = [v]
1697    if isinstance(v, list):
1698        v = interp.eval(v)
1699    return v
1700
1701@logofunc(aliases=['do.while'], aware=True)
1702def dowhile(interp, lst, test):
1703    """
1704    DO.WHILE instructionlist tfexpression
1705
1706    command.  Repeatedly evaluates the ``instructionlist`` as long as
1707    the evaluated ``tfexpression`` remains TRUE.  Evaluates the first
1708    input first, so the ``instructionlist`` is always run at least
1709    once.  The ``tfexpression`` must be an expressionlist whose value
1710    when evaluated is TRUE or FALSE.
1711    """
1712    lastVal = None
1713    try:
1714        while 1:
1715            try:
1716                lastVal = interp.eval(lst)
1717            except LogoContinue:
1718                pass
1719            v = interp.eval(test)
1720            if not v:
1721                break
1722    except LogoBreak:
1723        lastVal = None
1724        pass
1725    return lastVal
1726
1727@logofunc(name='while', aware=True)
1728def logoWhile(interp, test, block):
1729    """
1730    WHILE tfexpression instructionlist
1731
1732    command.  Repeatedly evaluates the ``instructionlist`` as long as
1733    the evaluated ``tfexpression`` remains TRUE.  Evaluates the first
1734    input first, so the ``instructionlist`` may never be run at all.
1735    The ``tfexpression`` must be an expressionlist whose value when
1736    evaluated is TRUE or FALSE.
1737    """
1738    lastVal = None
1739    try:
1740        while logo_eval(interp, test):
1741            try:
1742                lastVal = logo_eval(interp, block)
1743            except LogoContinue:
1744                pass
1745    except LogoBreak:
1746        lastVal = None
1747        pass
1748    return lastVal
1749
1750# @@: continue with do.until
1751
1752@logofunc(name='for', aware=True)
1753def logo_for(interp, name, l, block):
1754    for item in l:
1755        interp.set_variable(name, item)
1756        try:
1757            logo_eval(interp, block)
1758        except LogoContinue:
1759            pass
1760
1761@logofunc(aware=True)
1762def dowhile(interp, block, clause):
1763    try:
1764        logo_eval(interp, block)
1765    except LogoContinue:
1766        pass
1767    logoWhile(interp, clause, block)
1768
1769@logofunc(aware=True)
1770def until(interp, clause, block):
1771    while not logo_eval(interp, clause):
1772        try:
1773            logo_eval(block)
1774        except LogoContinue:
1775            pass
1776
1777@logofunc(aware=True)
1778def dountil(interp, block, clause):
1779    try:
1780        logo_eval(interp, block)
1781    except LogoContinue:
1782        pass
1783    until(interp, clause, block)
1784
1785########################################
1786## PyLogo primitives
1787########################################
1788
1789@logofunc(name='assert')
1790def logo_assert(value, message=None):
1791    if message is None:
1792        assert value
1793    else:
1794        assert value, message
1795
1796def assertequal(value1, value2, message=None):
1797    if value1 != value2:
1798        if message:
1799            message += '; '
1800        message = (message or '') + '%r != %r' % (value1, value2)
1801        raise AssertionError, message
1802
1803@logofunc(aware=True, arity=2)
1804def function(interp, name, default=NoDefault):
1805    if default is NoDefault:
1806        return interp.getFunc(name)
1807    else:
1808        try:
1809            return interp.getFunc(name)
1810        except LogoNameError:
1811            return default
1812
1813@logofunc(aware=True, arity=3)
1814def catch(interp, block, *args):
1815    assert not len(args)%2, "You must provide a block and a list of exceptions and block handlers for those exceptions (and odd number of arguments)"
1816    try:
1817        value = logo_eval(interp, block)
1818    except Exception, e:
1819        handler = None
1820        while 1:
1821            if not args:
1822                break
1823            if isinstance(args[0], str):
1824                if e.__class__.__name__.lower() == args[0].lower():
1825                    handler = args[1]
1826                    break
1827            elif isinstance(e, args[0]):
1828                handler = args[1]
1829                break
1830            args = args[2:]
1831        if not handler:
1832            raise
1833        interp.set_variable('exception', e)
1834        value = logo_eval(interp, block)
1835    return value
1836
1837@logofunc(name='getattr')
1838def logo_getattr(obj, attr, *args):
1839    return getattr(obj, attr, *args)
1840
1841@logofunc(name='setattr')
1842def logo_setattr(obj, attr, *args):
1843    return setattr(obj, attr, *args)
1844
1845@logofunc(name='None')
1846def logo_none():
1847    return None
1848
1849@logofunc(name='true')
1850def logo_rue():
1851    return True
1852
1853@logofunc(name='false')
1854def logo_alse():
1855    return False
1856
1857_spawnnum = 0
1858_spawnnumLock = threading.Lock()
1859
1860@logofunc(aware=True)
1861def spawn(interp, block, starter=None):
1862    global _spawnnum
1863    _spawnnumLock.acquire()
1864    try:
1865        mynum = _spawnnum + 1
1866        _spawnnum += 1
1867    finally:
1868        _spawnnumLock.release()
1869    interp = interp.new()
1870    if starter:
1871        interp.eval(block)
1872        block = starter
1873    threadname = 'thread_%i' % mynum
1874    interp.set_variable_local('threadname', threadname)
1875    t = threading.Thread(target=logo_eval, args=(interp, block),
1876                         name=threadname)
1877    t.start()
1878    return threadname
1879
1880def wait(sec):
1881    time.sleep(sec)
1882
1883def timestamp():
1884    return time.time()
1885
1886@logofunc(aware=True)
1887def startsync(interp):
1888    interp.set_variable_local('synctimestamp', time.time())
1889
1890@logofunc(aware=True)
1891def sync(interp, sec):
1892    last = interp.get_variable('synctimestamp')
1893    now = time.time()
1894    t = last+sec-now
1895    if t > 0:
1896        time.sleep(t)
1897    interp.set_variable('synctimestamp', time.time())
1898
1899@logofunc(name='import', aware=True)
1900def logo_import(interp, name):
1901    if isinstance(name, list):
1902        name = '.'.join(name)
1903    try:
1904        interp.import_module(name)
1905    except ImportError:
1906        #try:
1907            interp.import_module('pylogo.%s' % name)
1908        #except ImportError:
1909        #    interp.importModule('src.%s' % name)
1910
1911def newdict(lst=None):
1912    if lst:
1913        if len(lst)%2:
1914            raise LogoError("You must give NEWDICT a list with an even number of items",
1915                            description="Invalid function call")
1916        items = []
1917        while 1:
1918            if not lst:
1919                break
1920            items.append((lst[0], lst[1]))
1921            lst = lst[2:]
1922        return dict(items)
1923    else:
1924        return {}
1925
1926def keys(d):
1927    return d.keys()
1928
1929def values(d):
1930    return d.values()
1931
1932def items(d):
1933    return d.items()
1934
1935@logofunc(aware=True)
1936def call(interp, func, *args):
1937    if isinstance(func, str):
1938        func = interp.get_function(func)
1939    return func(*args)
1940
1941@logofunc(name='new', aware=True)
1942def logo_new(interp, cls):
1943    if getattr(cls, 'logo_aware', False):
1944        inst = cls(interp)
1945    else:
1946        inst = cls()
1947    return inst
1948
1949#def builtins_main(interp):
1950#    logoImport(interp, 'pylogo.logo_turtle')