0001import types
0002from cStringIO import StringIO
0003from builtins import _join, logo_soft_repr
0004import interpreter
0005from common import *
0006
0007class Writer(object):
0008
0009    def __init__(self, output):
0010        self._output = output
0011
0012    @logofunc(aliases=['print'], arity=-1)
0013    def pr(self, *args):
0014        trans = []
0015        for arg in args:
0016            if isinstance(arg, list):
0017                trans.append(_join(map(logo_soft_repr, arg)))
0018            else:
0019                trans.append(logo_soft_repr(arg))
0020        self._output.write(' '.join(trans))
0021        self._output.write('\n')
0022
0023    @logofunc(name='type', arity=-1)
0024    def logo_type(self, *args):
0025        trans = []
0026        for arg in args:
0027            if isinstance(arg, list):
0028                trans.append(_join(map(logo_soft_repr, arg)))
0029            else:
0030                trans.append(logo_soft_repr(arg))
0031        self._output.write(' '.join(trans))
0032
0033    def show(self, *args):
0034        self._output.write(' '.join(map(repr(args))))
0035        self._output.write('\n')
0036
0037    def writer(self):
0038        return self
0039
0040    def setwritepos(self, charpos):
0041        """
0042        SETWRITEPOS charpos
0043        
0044        command.  Sets the file pointer of the write stream file so
0045        that the next PRINT, etc., will begin writing at the
0046        ``charpos`` character in the file, counting from 0.  (That is,
0047        SETWRITEPOS 0 will start writing from the beginning of the
0048        file.)  Meaningless if the write stream is the terminal.
0049        """
0050        self.output.seek(charpos)
0051
0052    def writepos(self):
0053        """
0054        WRITEPOS
0055        
0056        outputs the file position of the current write stream file.
0057        """
0058        return self.output.tell()
0059
0060
0061class Reader(object):
0062
0063    def __init__(self, input):
0064        self.input = input
0065
0066    def reader(self):
0067        return self
0068
0069    def setreadpos(self, charpos):
0070        """
0071        SETREADPOS charpos
0072        
0073        command.  Sets the file pointer of the read stream file so
0074        that the next READLIST, etc., will begin reading at the
0075        ``charpos`` character in the file, counting from 0.  (That is,
0076        SETREADPOS 0 will start reading from the beginning of the
0077        file.)  Meaningless if the read stream is the terminal.
0078        """
0079        self.input.seek(charpos)
0080
0081    def readpos(self):
0082        """
0083        READPOS
0084        
0085        outputs the file position of the current read stream file.
0086        """
0087        return self.input.tell()
0088
0089    @logofunc(aliases=["eof?"])
0090    def eofp(self):
0091        """
0092        EOFP
0093        EOF?
0094
0095        predicate, outputs TRUE if there are no more characters to be
0096        read in the read stream file, FALSE otherwise.
0097        """
0098        fn = self.input.name
0099        size = os.stat(fn).st_size
0100        return self.input.tell() >= size
0101
0102class CaptureWriter(Writer):
0103
0104    logo_class = True
0105
0106    def __init__(self):
0107        super(CaptureWriter, self).__init__(StringIO())
0108
0109    def writervalue(self):
0110        return self._output.getvalue()
0111
0112@logofunc(aware=True, name='create')
0113def logo_create(interp, superclass, name, block):
0114    frame = interp.new()
0115    methods = {}
0116    def set_function(name, func):
0117        methods[name] = func
0118    def get_function(name):
0119        if name in methods:
0120            return methods[name]
0121        else:
0122            return frame.__class__.get_function(frame, name)
0123    frame.set_function = set_function
0124    frame.get_function = get_function
0125    frame.eval(block)
0126    frame.vars.update(methods)
0127    if hasattr(superclass, '__logo_create__'):
0128        new = superclass.__logo_create__(name, frame.vars)
0129    elif not isinstance(superclass, (types.ClassType, type)):
0130        new = superclass.__class__()
0131        new.__dict__.update(superclass.__dict__)
0132        for a_name, a_value in frame.vars.items():
0133            if isinstance(a_value, interpreter.UserFunction):
0134                a_value = interpreter.BoundUserFunction(a_value, new)
0135            setattr(new, a_name, a_value)
0136    else:
0137        new = type(superclass)(name, (superclass,), frame.vars)
0138        new.logo_class = True
0139    interp.set_variable(name, new)
0140    interp.set_function(name, new)
0141    return new
0142
0143@logofunc(aware=True)
0144def actor(interp, the_actor):
0145    """
0146    ACTOR object
0147
0148    Adds the actor to the top of the actor list, so that commands will
0149    be passed to the actor first.
0150    """
0151    interp.push_actor(the_actor)
0152
0153@logofunc(aware=True)
0154def actors(interp):
0155    """
0156    ACTORS
0157
0158    outputs all the actors; the first object in the list is the
0159    topmost actor that gets first chance at commands.
0160    """
0161    return interp.actors
0162
0163@logofunc(aware=True, arity=1)
0164def removeactor(interp, the_actor=None):
0165    """
0166    REMOVEACTOR object
0167
0168    Remove the actor from the actor list.  If the actor does not
0169    appear on the list, it will be an error.  If the actor is None or
0170    [] then the most recent actor added with ACTOR will be removed.
0171    """
0172    if the_actor is None or the_actor == []:
0173        interp.pop_actor()
0174    else:
0175        interp.pop_actor(the_actor)
0176
0177def oobuiltins_main(interp):
0178    interp.set_variable('object', object)
0179
0180
0181
0182############################################################
0183## File access from UCBLogo
0184############################################################
0185
0186_filename_prefix = None
0187
0188@logofunc()
0189def setprefix(prefix):
0190    """
0191    SETPREFIX string
0192
0193    command.  Sets a prefix that will be used as the implicit
0194    beginning of filenames in OPENREAD, OPENWRITE, OPENAPPEND,
0195    OPENUPDATE, LOAD, and SAVE commands.  Logo will put the
0196    appropriate separator character (slash for Unix, backslash for
0197    DOS/Windows, colon for MacOS) between the prefix and the filename
0198    entered by the user.  The input to SETPREFIX must be a word,
0199    unless it is the empty list, to indicate that there should be no
0200    prefix.
0201
0202    @@ Note: LOAD and SAVE don't use this
0203    """
0204    global _filename_prefix
0205    # @@: Should this be interpreter-local?
0206    if not prefix:
0207        _filename_prefix = None
0208    else:
0209        _filename_prefix = prefix
0210
0211@logofunc()
0212def prefix():
0213    """
0214    PREFIX
0215
0216    outputs the current file prefix, or None if there is no prefix.
0217    See SETPREFIX.
0218
0219    @@ Note: None instead of [] like in ucblogo
0220    """
0221    return _filename_prefix
0222
0223@logofunc(hide=True)
0224def _filename(filename):
0225    if _filename_prefix is not None:
0226        filename = os.path.join(_filename_prefix, filename)
0227    return filename
0228
0229@logofunc()
0230def openread(filename):
0231    """
0232    OPENREAD filename
0233    
0234    command.  Opens the named file for reading.  The read position is
0235    initially at the beginning of the file.
0236
0237    Use ``ACTOR OPENREAD filename`` to take all input from the given
0238    file.
0239    """
0240    f = open(_filename(filename))
0241    return Reader(f)
0242
0243@logofunc()
0244def openwrite(filename):
0245    """
0246    OPENWRITE filename
0247    
0248    command.  Opens the named file for writing.  If the file already
0249    existed, the old version is deleted and a new, empty file created.
0250
0251    Use ``ACTOR OPENWRITE filename`` to put all output to the given
0252    file.
0253    """
0254    f = open(_filename(filename), 'w')
0255    return Writer(f)
0256
0257@logofunc()
0258def openappend(filename):
0259    """
0260    OPENAPPEND filename
0261
0262    command.  Opens the named file for writing.  If the file already
0263    exists, the write position is initially set to the end of the old
0264    file, so that newly written data will be appended to it.
0265
0266    Use ``ACTOR OPENAPPEND filename`` to put all output to the given
0267    file.
0268    """
0269    f = open(_filename(filename), 'a')
0270    return Writer(f)
0271
0272# @@: not done: OPENUPDATE
0273
0274@logofunc(aware=True)
0275def close(interp, filename):
0276    """
0277    CLOSE filename
0278    
0279    command.  Closes the named file, or file object.
0280    """
0281    if isinstance(filename, basestring):
0282        filename = _filename(filename)
0283        for actor in interp.actors:
0284            if isinstance(actor, Reader):
0285                f = actor.input
0286                if getattr(f, 'name', None) == filename:
0287                    break
0288            elif isinstance(actor, Writer):
0289                f = actor.output
0290                if getattr(f, 'name', None) == filename:
0291                    break
0292        else:
0293            raise ValueError(
0294                "No file with the name %r found in the actor list"
0295                % filename)
0296    else:
0297        f = filename
0298    if isinstance(f, Reader):
0299        f.input.close()
0300    else:
0301        f.output.close()
0302    if f in interp.actors:
0303        interp.actors.remove(f)
0304
0305@logofunc(aware=True)
0306def allopen(interp):
0307    """
0308    ALLOPEN
0309
0310    outputs a list whose members are the file objects currently open.
0311    This list does not include the dribble file, if any.
0312    """
0313    result = []
0314    for actor in actors:
0315        if isinstance(actor, (Reader, Writer)):
0316            result.append(actor)
0317    return result
0318
0319@logofunc(aware=True)
0320def closeall(interp):
0321    """
0322    CLOSEALL
0323
0324    command.  Closes all open files.  Abbreviates
0325    FOREACH ALLOPEN [CLOSE ?]
0326    """
0327    for f in allopen(interp):
0328        close(interp, f)
0329
0330@logofunc(aliases=['erf'])
0331def erasefile(filename):
0332    """
0333    ERASEFILE filename
0334    ERF filename
0335
0336    command.  Erases (deletes, removes) the named file, which should not
0337    currently be open.
0338    """
0339    os.unlink(_filename(filename))
0340
0341# @@: Not implemented: DRIBBLE NODRIBBLE
0342# @@: Not applicable: SETREAD, SETWRITE
0343# @@: Not the same: READER, WRITER
0344
0345@logofunc(aliases=["file?"])
0346def filep(filename):
0347    """
0348    FILEP filename
0349    FILE? filename
0350
0351    predicate, outputs TRUE if a file of the specified name exists
0352    and can be read, FALSE otherwise.
0353    """
0354    return os.path.exists(_filename(filename))