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 (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
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
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
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
0999
1000
1001
1002
1003
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
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
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
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
1194
1195
1196
1197
1198
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
1214
1215
1216
1217
1218
1219
1220
1221
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
1278
1279
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
1303
1304
1305
1306
1307
1308
1309
1310
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
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
1360
1361
1362
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
1409
1410
1411
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
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
1585
1586@logofunc(name='continue', aliases=['co'])
1587def logo_continue():
1588 raise LogoContinue()
1589
1590def bye():
1591 sys.exit()
1592
1593
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
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
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
1907 interp.import_module('pylogo.%s' % name)
1908
1909
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
1950