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