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
0035
0036
0037
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
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
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
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
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
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
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
0516
0517
0518
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
0573
0574
0575
0576
0577
0578
0579
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
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
0676 return v[:-1]
0677 except EOFError:
0678 return []
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695@logofunc(arity=2)
0696def sum(*args):
0697 """
0698 SUM num1 num2
0699