0001from pylogo.common import *
0002
0003class UCBArray(list):
0004 def __init__(v, origin=1):
0005 list.__init__(v)
0006 self.origin = origin
0007 def __getitem__(self, i):
0008 return list.__getitem__(self, i-self.origin)
0009 def __setitem__(self, i, value):
0010 list.__setitem__(self, i-self.origin, value)
0011
0012def array(size, origin=1):
0013 """
0014 ARRAY size
0015 (ARRAY size origin)
0016
0017 outputs an array of ``size`` members (must be a positive integer),
0018 each of which initially is an empty list. Array members can be
0019 selected with ITEM and changed with SETITEM. The first member of
0020 the array is member number 1 unless an ``origin`` input (must be
0021 an integer) is given, in which case the first member of the array
0022 has that number as its index. (Typically 0 is used as the origin
0023 if anything.)
0024 """
0025
0026
0027 return UCBArray([[]]*size)
0028
0029def mdarray(sizelist, origin=1):
0030 """
0031 MDARRAY sizelist
0032 (MDARRAY sizelist origin)
0033
0034 outputs a multi-dimensional array. The first input must be a list
0035 of one or more positive integers. The second input, if present,
0036 must be a single integer that applies to every dimension of the array.
0037 Ex: (MDARRAY [3 5] 0) outputs a two-dimensional array whose members
0038 range from [0 0] to [2 4].
0039 """
0040 if len(sizelist) == 1:
0041 return array(sizelist[0], origin=origin)
0042 else:
0043 result = []
0044 for i in range(sizelist[0]):
0045 result.append(array(sizelist[1:], origin=origin))
0046 return UCBArray(result, origin=origin)
0047
0048def listtoarray(lst, origin=1):
0049 """
0050 LISTTOARRAY list
0051 (LISTTOARRAY list origin)
0052
0053 outputs an array of the same size as the input list, whose members
0054 are the members of the input list.
0055 """
0056 return UCBArray(lst, origin=origin)
0057
0058def arraytolist(array):
0059 """
0060 ARRAYTOLIST array
0061
0062 outputs a list whose members are the members of the input array.
0063 The first member of the output is the first member of the array,
0064 regardless of the array's origin.
0065 """
0066 return list(array)
0067
0068def mditem(indexlst, array):
0069 """
0070 MDITEM indexlist array
0071
0072 outputs the member of the multidimensional ``array`` selected by
0073 the list of numbers ``indexlist``.
0074 """
0075 while indexlst:
0076 array = array[indexlst[0]]
0077 indexlst = indexlst[1:]
0078 return array
0079
0080def quoted(v):
0081 """
0082 QUOTED thing
0083
0084 outputs its input, if a list; outputs its input with a quotation
0085 mark prepended, if a word.
0086 """
0087
0088
0089 if isinstance(v, str):
0090 return '"%s' % v
0091 else:
0092 return v
0093
0094def mdsetitem(indexlst, array, value):
0095 """
0096 MDSETITEM indexlist array value
0097
0098 command. Replaces the member of ``array`` chosen by ``indexlist``
0099 with the new ``value``.
0100 """
0101 if len(indexlst) == 1:
0102 array[indexlst[0]] = value
0103 else:
0104 mdsetitem(indexlst[1:], array[indexlst[0]], value)
0105
0106
0107@logofunc(name='.setitem')
0108def dotsetitem(index, array, value):
0109 """
0110 .SETITEM index array value
0111
0112 command. Changes the ``index``th member of ``array`` to be
0113 ``value``, like SETITEM, but without checking for circularity.
0114 WARNING: Primitives whose names start with a period are DANGEROUS.
0115 Their use by non-experts is not recommended. The use of .SETITEM
0116 can lead to circular arrays, which will get some Logo primitives
0117 into infinite loops; and the loss of memory if a circular
0118 structure is released.
0119 """
0120
0121
0122
0123
0124 setitem(index, array, value)
0125
0126@logofunc(aware=True)
0127def push(interp, stackname, thing):
0128 """
0129 PUSH stackname thing
0130
0131 command. Adds the ``thing`` to the stack that is the value of the
0132 variable whose name is ``stackname``. This variable must have a
0133 list as its value; the initial value should be the empty list.
0134 New members are added at the front of the list.
0135 """
0136 var = interp.get_variable(stackname)
0137 var.append(thing)
0138
0139@logofunc(aware=True)
0140def pop(interp, stackname):
0141 """
0142 POP stackname
0143
0144 outputs the most recently PUSHed member of the stack that is the
0145 value of the variable whose name is ``stackname`` and removes that
0146 member from the stack.
0147 """
0148 var = interp.get_variable(stackname)
0149 return var.pop()
0150
0151@logofunc(aware=True)
0152def queue(interp, queuename, thing):
0153 """
0154 QUEUE queuename thing
0155
0156 command. Adds the ``thing`` to the queue that is the value of the
0157 variable whose name is ``queuename``. This variable must have a
0158 list as its value; the initial value should be the empty list.
0159 New members are added at the back of the list.
0160 """
0161 var = interp.get_variable(queuename)
0162 var.append(thing)
0163
0164@logofunc(aware=True)
0165def dequeue(interp, queuename):
0166 """
0167 DEQUEUE queuename
0168
0169 outputs the least recently QUEUEd member of the queue that is the
0170 value of the variable whose name is ``queuename`` and removes that
0171 member from the queue.
0172 """
0173 var = interp.get_variable(queuename)
0174 return var.pop(0)
0175
0176@logofunc(aliases=['array?'])
0177def arrayp(v):
0178 """
0179 ARRAYP thing
0180 ARRAY? thing
0181
0182 outputs TRUE if the input is an array, FALSE otherwise.
0183 """
0184 return isinstance(v, UCBArray)
0185
0186@logofunc(aliases=['backslashed?'])
0187def backslashedp(c):
0188 """
0189 BACKSLASHEDP char
0190 BACKSLASHED? char
0191 """
0192
0193
0194
0195
0196
0197
0198
0199 return False
0200
0201def rawascii(c):
0202 """
0203 RAWASCII char
0204
0205 outputs the integer (between 0 and 255) that represents the input
0206 character in the ASCII code.
0207 """
0208
0209
0210
0211 return ord(c)
0212
0213_prefix = ''
0214def setprefix(s):
0215 """
0216 SETPREFIX string
0217
0218 command. Sets a prefix that will be used as the implicit beginning
0219 of filenames in OPENREAD, OPENWRITE, OPENAPPEND, OPENUPDATE, LOAD,
0220 and SAVE commands. Logo will put the appropriate separator
0221 character (slash for Unix, backslash for DOS/Windows, colon for
0222 MacOS) between the prefix and the filename entered by the user.
0223 The input to SETPREFIX must be a word, unless it is the empty list,
0224 to indicate that there should be no prefix.
0225 """
0226 global _prefix
0227 if s:
0228 _prefix = s
0229 else:
0230 _prefix = ''
0231
0232def prefix():
0233 """
0234 PREFIX
0235
0236 outputs the current file prefix, or [] if there is no prefix.
0237 See SETPREFIX.
0238 """
0239 return _prefix or LogoList([])
0240
0241_files = {}
0242
0243def openread(filename):
0244 """
0245 OPENREAD filename
0246
0247 command. Opens the named file for reading. The read position is
0248 initially at the beginning of the file.
0249 """
0250 _files[filename] = open(_prefix + filename)
0251
0252def openwrite(filename):
0253 """
0254 OPENWRITE filename
0255
0256 command. Opens the named file for writing. If the file already
0257 existed, the old version is deleted and a new, empty file created.
0258 """
0259 _files[filename] = open(_prefix + filename, 'w')
0260
0261def openappend(filename):
0262 """
0263 OPENAPPEND filename
0264
0265 command. Opens the named file for writing. If the file already
0266 exists, the write position is initially set to the end of the old
0267 file, so that newly written data will be appended to it.
0268 """
0269 _files[filename] = open(_prefix + filename, 'a')
0270
0271def openupdate(filename):
0272 """
0273 OPENUPDATE filename
0274
0275 command. Opens the named file for reading and writing. The read and
0276 write position is initially set to the end of the old file, if any.
0277 Note: each open file has only one position, for both reading and
0278 writing. If a file opened for update is both READER and WRITER at
0279 the same time, then SETREADPOS will also affect WRITEPOS and vice
0280 versa. Also, if you alternate reading and writing the same file,
0281 you must SETREADPOS between a write and a read, and SETWRITEPOS
0282 between a read and a write.
0283 """
0284 _files[filename] = open(_prefix + filename, 'a+')
0285
0286def close(filename):
0287 """
0288 CLOSE filename
0289
0290 command. Closes the named file.
0291 """
0292 _files[filename].close()
0293
0294def allopen():
0295 """
0296 ALLOPEN
0297
0298 outputs a list whose members are the names of all files currently open.
0299 This list does not include the dribble file, if any.
0300 """
0301 return LogoList(_files.keys())
0302
0303def closeall():
0304 """
0305 CLOSEALL
0306
0307 command. Closes all open files. Abbreviates
0308 FOREACH ALLOPEN [CLOSE ?]
0309 """
0310 for key, value in _files.items():
0311 del _files[key]
0312 value.close()
0313
0314@logofunc(aliases=['erf'])
0315def erasefile(filename):
0316 """
0317 ERASEFILE filename
0318 ERF filename
0319
0320 command. Erases (deletes, removes) the named file, which should not
0321 currently be open.
0322 """
0323 os.unlink(_prefix + filename)
0324
0325_in_dribble = False
0326
0327class Dribbler:
0328
0329 def __init__(self, capture, fileobj):
0330 self.capture = capture
0331 self.file = fileobj
0332 for n in ['read', 'readline', 'readlines']:
0333 setattr(self, n, self.cap(n))
0334 for n in ['write']:
0335 setattr(self, n, self.copy(n))
0336
0337 def cap(self, attr):
0338 def func(*args):
0339 v = getattr(self.capture, attr)(*args)
0340 self.file.write(v)
0341 return v
0342 return func
0343
0344 def copy(self, attr):
0345 def func(*args):
0346 self.write.write(''.join(args))
0347 getattr(self.capture, attr)(*args)
0348 return func
0349
0350 def close(self):
0351 self.capture.close()
0352 self.file.close()
0353
0354 def nodribble(self):
0355 self.file.close()
0356 return self.capture
0357
0358def dribble(filename):
0359 """
0360 DRIBBLE filename
0361
0362 command. Creates a new file whose name is the input, like OPENWRITE,
0363 and begins recording in that file everything that is read from the
0364 keyboard or written to the terminal. That is, this writing is in
0365 addition to the writing to WRITER. The intent is to create a
0366 transcript of a Logo session, including things like prompt
0367 characters and interactions.
0368 """
0369 out = open(filename, 'w')
0370 sys.stdout = Dribbler(sys.stdout, out)
0371 sys.stdin = Dribbler(sys.stdin)
0372
0373
0374_plists = {}
0375
0376def _getplist(plistname):
0377 if not _plists.has_key(plistname.lower()):
0378 _plists[plistname.lower()] = {}
0379 return _plists[plistname.lower()]
0380
0381def pprop(plistname, propname, value):
0382 """
0383 PPROP plistname propname value
0384
0385 command. Adds a property to the ``plistname`` property list with
0386 name ``propname`` and value ``value``.
0387 """
0388 _getplist(plistname)[propname.lower()] = value
0389
0390def gprop(plistname, propname):
0391 """
0392 GPROP plistname propname
0393
0394 outputs the value of the ``propname`` property in the
0395 ``plistname`` property list, or the empty list if there is no such
0396 property.
0397 """
0398 try:
0399 return _getplist(plistname)[propname.lower()]
0400 except KeyError:
0401 return LogoList([])
0402
0403def remprop(plistname, propname):
0404 """
0405 REMPROP plistname propname
0406
0407 command. Removes the property named ``propname`` from the
0408 property list named ``plistname``.
0409 """
0410 try:
0411 del _getplist(plistname)[propname.lower()]
0412 except KeyError:
0413 pass
0414
0415def plist(plistname):
0416 """
0417 PLIST plistname
0418
0419 outputs a list whose odd-numbered members are the names, and
0420 whose even-numbered members are the values, of the properties
0421 in the property list named ``plistname``. The output is a copy
0422 of the actual property list; changing properties later will not
0423 magically change a list output earlier by PLIST.
0424 """
0425 v = []
0426 for key, value in _getplist(plistname):
0427 v.extend([key, value])
0428 return LogoList(v)
0429
0430@logofunc(aliases=['plist?'])
0431def plistp(plistname):
0432 """
0433 PLISTP name
0434 PLIST? name
0435
0436 outputs TRUE if the input is the name of a *nonempty* property
0437 list. (In principle every word is the name of a property list; if
0438 you haven't put any properties in it, PLIST of that name outputs
0439 an empty list, rather than giving an error message.)
0440 """
0441 if not _plists.has_key(plistname.lower()):
0442 return False
0443 return not not _getplist(plistname)
0444
0445def erpls():
0446 """
0447 ERPLS
0448
0449 command. Erases all unburied property lists from the workspace.
0450 Abbreviates ERASE PLISTS.
0451 """
0452 global _plists
0453 _plists = {}