""" Java java.io extended standard library APIs implemented in Python. Extends java.io with ObjectStreamField, ObjectStreamConstants, ObjectInput, etc. """ from __future__ import annotations from .._core import Stub from ..java.io import ( Serializable, Externalizable, IOException, ObjectStreamConstants as _BaseObjectStreamConstants, File as _File, PrintWriter, ) from ..java.lang import RuntimeException, String, Exception as _JavaException import io as _pyio import os as _os import sys as _sys import struct as _struct import pickle as _pickle import threading as _threading import traceback as _traceback import types as _py_types __all__ = [ "ObjectStreamField", "ObjectStreamConstants", "ObjectInput", "ObjectOutput", "ObjectInputStream", "ObjectOutputStream", "ObjectStreamClass", "ObjectInputValidation", "SerialCallbackContext", "Externalizable", "Serializable", "FileFilter", "FileNameFilter", "FilePermission", "SerializablePermission", "Console", "PrintStream", "DataInput", "DataOutput", "SequenceInputStream", "StringBufferInputStream", "LineNumberReader", "CharArrayReader", "CharArrayWriter", "FilterReader", "FilterWriter", "PushbackReader", "PushbackInputStream", "StreamTokenizer", "StreamCorruptedException", "WriteAbortedException", ] # ============================================================ # ObjectStreamConstants # ============================================================ class ObjectStreamConstants(Stub): """java.io.ObjectStreamConstants""" STREAM_MAGIC = 0xACED STREAM_VERSION = 5 TC_BASE = 0x70 TC_NULL = 0x70 TC_REFERENCE = 0x71 TC_CLASS = 0x72 TC_OBJECT = 0x73 TC_STRING = 0x74 TC_ARRAY = 0x75 TC_ENUM = 0x7E TC_CLASSDESC = 0x72 TC_PROXYCLASSDESC = 0x7D TC_BLOCKDATA = 0x77 TC_ENDBLOCKDATA = 0x78 TC_RESET = 0x79 TC_EXCEPTION = 0x7A TC_LONGSTRING = 0x7C baseWireHandle = 0x7E0000 PROTOCOL_VERSION_1 = 1 PROTOCOL_VERSION_2 = 2 SC_WRITE_METHOD = 0x01 SC_BLOCK_DATA = 0x08 SC_SERIALIZABLE = 0x02 SC_EXTERNALIZABLE = 0x04 SC_ENUM = 0x10 SUBSTITUTION_PERMISSION = "enableSubstitution" # ============================================================ # ObjectStreamField # ============================================================ class ObjectStreamField(Stub): """java.io.ObjectStreamField""" def __init__(self, name, field_type, unshared=False): super().__init__() self._name = name self._type = field_type self._unshared = unshared def getName(self): return self._name def getType(self): return self._type def getTypeString(self): return self._type.__name__ if hasattr(self._type, '__name__') else str(self._type) def getTypeCode(self): return self.getTypeString()[0].upper() def isUnshared(self): return self._unshared def isPrimitive(self): return self._type in (int, float, bool, bytes, str, bytearray) def toString(self): return f"{self.getTypeString()} {self._name}" # ============================================================ # ObjectStreamClass # ============================================================ class ObjectStreamClass(Stub): """java.io.ObjectStreamClass""" _descriptors = {} def __init__(self, cls=None): super().__init__() self._cls = cls self._name = cls.__name__ if cls else "" self._serial_version_uid = hash(cls) if cls else 0 self._fields = [] self._serializable = hasattr(cls, '__getstate__') or hasattr(cls, '__setstate__') if cls else False if cls: for attr_name in dir(cls): if not attr_name.startswith('_'): attr_type = type(getattr(cls, attr_name, None)) self._fields.append(ObjectStreamField(attr_name, attr_type)) @staticmethod def lookup(cls): if cls is None: return None if cls not in ObjectStreamClass._descriptors: ObjectStreamClass._descriptors[cls] = ObjectStreamClass(cls) return ObjectStreamClass._descriptors[cls] @staticmethod def lookupAny(cls): return ObjectStreamClass.lookup(cls) def getName(self): return self._name def getSerialVersionUID(self): return self._serial_version_uid def getFields(self): return self._fields def getField(self, name): for f in self._fields: if f.getName() == name: return f return None def forClass(self): return self._cls def isSerializable(self): return self._serializable def isEnum(self): return False def toString(self): return f"ObjectStreamClass[{self._name}]" # ============================================================ # ObjectInputValidation # ============================================================ class ObjectInputValidation(Stub): """java.io.ObjectInputValidation""" def validateObject(self): raise NotImplementedError # ============================================================ # SerialCallbackContext # ============================================================ class SerialCallbackContext(Stub): """java.io.SerialCallbackContext""" def __init__(self, obj, desc): super().__init__() self._obj = obj self._desc = desc self._used = False def getObj(self): return self._obj def getDesc(self): return self._desc def setUsed(self): self._used = True def isUsed(self): return self._used # ============================================================ # ObjectInput / ObjectOutput interfaces # ============================================================ class DataInput(Stub): """java.io.DataInput""" def readByte(self): raise NotImplementedError def readShort(self): raise NotImplementedError def readInt(self): raise NotImplementedError def readLong(self): raise NotImplementedError def readFloat(self): raise NotImplementedError def readDouble(self): raise NotImplementedError def readBoolean(self): raise NotImplementedError def readChar(self): raise NotImplementedError def readUnsignedByte(self): raise NotImplementedError def readUnsignedShort(self): raise NotImplementedError def readUTF(self): raise NotImplementedError def readFully(self, b, off=0, length=-1): raise NotImplementedError def skipBytes(self, n): raise NotImplementedError def readLine(self): raise NotImplementedError class DataOutput(Stub): """java.io.DataOutput""" def writeByte(self, v): raise NotImplementedError def writeShort(self, v): raise NotImplementedError def writeInt(self, v): raise NotImplementedError def writeLong(self, v): raise NotImplementedError def writeFloat(self, v): raise NotImplementedError def writeDouble(self, v): raise NotImplementedError def writeBoolean(self, v): raise NotImplementedError def writeChar(self, v): raise NotImplementedError def writeBytes(self, s): raise NotImplementedError def writeChars(self, s): raise NotImplementedError def writeUTF(self, s): raise NotImplementedError def write(self, b, off=0, length=-1): raise NotImplementedError class ObjectInput(DataInput): """java.io.ObjectInput""" def readObject(self): raise NotImplementedError def available(self): return 0 def close(self): pass class ObjectOutput(DataOutput): """java.io.ObjectOutput""" def writeObject(self, obj): raise NotImplementedError def flush(self): pass def close(self): pass # ============================================================ # ObjectInputStream # ============================================================ class ObjectInputStream(ObjectInput): """java.io.ObjectInputStream""" def __init__(self, inp): super().__init__() self._input = inp self._data = inp.read() if hasattr(inp, 'read') else b"" self._pos = 0 self._handles = {} self._next_handle = ObjectStreamConstants.baseWireHandle self._validation_list = [] self._resolved = {} def readObject(self): if self._pos >= len(self._data): raise StreamCorruptedException("No data") try: obj = _pickle.loads(self._data[self._pos:]) return obj except Exception: return None def readByte(self): val = self._data[self._pos] if self._pos < len(self._data) else 0 self._pos += 1 return val def readInt(self): if self._pos + 4 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">i", self._data, self._pos)[0] self._pos += 4 return val def readShort(self): if self._pos + 2 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">h", self._data, self._pos)[0] self._pos += 2 return val def readLong(self): if self._pos + 8 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">q", self._data, self._pos)[0] self._pos += 8 return val def readFloat(self): if self._pos + 4 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">f", self._data, self._pos)[0] self._pos += 4 return val def readDouble(self): if self._pos + 8 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">d", self._data, self._pos)[0] self._pos += 8 return val def readBoolean(self): return self.readByte() != 0 def readChar(self): if self._pos + 2 > len(self._data): raise StreamCorruptedException() val = _struct.unpack_from(">H", self._data, self._pos)[0] self._pos += 2 return chr(val) def readUnsignedByte(self): return self.readByte() & 0xFF def readUnsignedShort(self): return self.readShort() & 0xFFFF def readUTF(self): length = self.readUnsignedShort() if self._pos + length > len(self._data): raise StreamCorruptedException() val = self._data[self._pos:self._pos + length].decode("utf-8") self._pos += length return val def readFully(self, b, off=0, length=-1): if length < 0: length = len(b) - off if self._pos + length > len(self._data): raise StreamCorruptedException() for i in range(length): b[off + i] = self._data[self._pos + i] self._pos += length def skipBytes(self, n): actual = min(n, len(self._data) - self._pos) self._pos += actual return actual def readLine(self): result = [] while self._pos < len(self._data): c = chr(self._data[self._pos]) self._pos += 1 if c == "\n": break if c == "\r": if self._pos < len(self._data) and chr(self._data[self._pos]) == "\n": self._pos += 1 break result.append(c) return "".join(result) def available(self): return len(self._data) - self._pos def close(self): self._input = None self._data = b"" self._pos = 0 def defaultReadObject(self): return self.readObject() def readFields(self): return self.readObject() def registerValidation(self, obj, prio): self._validation_list.append((obj, prio)) def resolveClass(self, desc): return desc.forClass() def resolveObject(self, obj): return obj def enableResolveObject(self, enable): return False def readUnshared(self): return self.readObject() # ============================================================ # ObjectOutputStream # ============================================================ class ObjectOutputStream(ObjectOutput): """java.io.ObjectOutputStream""" def __init__(self, out): super().__init__() self._output = out self._buffer = bytearray() self._handles = {} self._next_handle = ObjectStreamConstants.baseWireHandle self._block_data_mode = False self._extended_block_data_mode = False def writeObject(self, obj): data = _pickle.dumps(obj) self._buffer.extend(data) self._flush_buffer() def writeByte(self, v): self._buffer.extend(_struct.pack(">b", v if isinstance(v, int) else ord(v))) def writeInt(self, v): self._buffer.extend(_struct.pack(">i", int(v))) def writeShort(self, v): self._buffer.extend(_struct.pack(">h", int(v))) def writeLong(self, v): self._buffer.extend(_struct.pack(">q", int(v))) def writeFloat(self, v): self._buffer.extend(_struct.pack(">f", float(v))) def writeDouble(self, v): self._buffer.extend(_struct.pack(">d", float(v))) def writeBoolean(self, v): self._buffer.extend(_struct.pack(">?", bool(v))) def writeChar(self, v): if isinstance(v, str): v = ord(v) self._buffer.extend(_struct.pack(">H", int(v))) def writeBytes(self, s): if isinstance(s, str): self._buffer.extend(s.encode()) elif isinstance(s, bytes): self._buffer.extend(s) elif isinstance(s, bytearray): self._buffer.extend(s) def writeChars(self, s): for c in s: self._buffer.extend(_struct.pack(">H", ord(c))) def writeUTF(self, s): encoded = s.encode("utf-8") self.writeShort(len(encoded)) self._buffer.extend(encoded) def write(self, b, off=0, length=-1): if isinstance(b, int): self._buffer.append(b & 0xFF) elif isinstance(b, bytes): if length < 0: length = len(b) - off self._buffer.extend(b[off:off + length]) elif isinstance(b, bytearray): if length < 0: length = len(b) - off self._buffer.extend(b[off:off + length]) def flush(self): self._flush_buffer() if hasattr(self._output, 'flush'): self._output.flush() def close(self): self.flush() if hasattr(self._output, 'close'): self._output.close() def defaultWriteObject(self): pass def putFields(self): return self def writeFields(self): pass def reset(self): self._handles = {} self._next_handle = ObjectStreamConstants.baseWireHandle self._buffer = bytearray() def useProtocolVersion(self, version): pass def writeUnshared(self, obj): self.writeObject(obj) def annotateClass(self, cls): pass def annotateProxyClass(self, cls): pass def replaceObject(self, obj): return obj def enableReplaceObject(self, enable): return False def writeStreamHeader(self): self._buffer.extend(_struct.pack(">HH", ObjectStreamConstants.STREAM_MAGIC, ObjectStreamConstants.STREAM_VERSION)) def _flush_buffer(self): if self._buffer: data = bytes(self._buffer) if hasattr(self._output, 'write'): self._output.write(data) self._buffer = bytearray() # ============================================================ # StreamCorruptedException / WriteAbortedException # ============================================================ class StreamCorruptedException(IOException): """java.io.StreamCorruptedException""" def __init__(self, message=None): super().__init__(message or "Stream corrupted") class WriteAbortedException(IOException): """java.io.WriteAbortedException""" def __init__(self, message, exception): super().__init__(message) self._exception = exception def getException(self): return self._exception # ============================================================ # FileFilter / FileNameFilter # ============================================================ class FileFilter(Stub): """java.io.FileFilter""" def accept(self, pathname): return True class FileNameFilter(Stub): """java.io.FilenameFilter""" def accept(self, dir, name): return True # ============================================================ # FilePermission / SerializablePermission # ============================================================ class FilePermission(Stub): """java.io.FilePermission""" def __init__(self, path, actions): super().__init__() self._path = path self._actions = actions def getName(self): return self._path def getActions(self): return self._actions def implies(self, permission): return True def equals(self, other): if not isinstance(other, FilePermission): return False return self._path == other._path and self._actions == other._actions def hashCode(self): return hash((self._path, self._actions)) class SerializablePermission(Stub): """java.io.SerializablePermission""" def __init__(self, name, actions=None): super().__init__() self._name = name self._actions = actions or "" def getName(self): return self._name def getActions(self): return self._actions def implies(self, permission): return True # ============================================================ # Console # ============================================================ class Console(Stub): """java.io.Console""" def __init__(self): super().__init__() @staticmethod def console(): return Console() def readLine(self, fmt=None, *args): if fmt: print(fmt % args, end="") return input() def readPassword(self, fmt=None, *args): import getpass if fmt: print(fmt % args, end="") return getpass.getpass("") def format(self, fmt, *args): _sys.stdout.write(fmt % args) _sys.stdout.flush() return self def printf(self, format, *args): return self.format(format, *args) def flush(self): _sys.stdout.flush() def reader(self): return _pyio.BufferedReader(_pyio.FileIO(_sys.stdin.fileno())) def writer(self): return _pyio.TextIOWrapper(_pyio.BufferedWriter( _pyio.FileIO(_sys.stdout.fileno(), "w") )) # ============================================================ # PrintStream # ============================================================ class PrintStream(Stub): """java.io.PrintStream""" def __init__(self, out=None, auto_flush=False, encoding=None): super().__init__() self._out = out or _sys.stdout self._auto_flush = auto_flush self._encoding = encoding or "utf-8" self._error = False def print(self, obj=""): _sys.stdout.write(str(obj)) def println(self, obj=""): print(str(obj)) def printf(self, format, *args): _sys.stdout.write(format % args) if self._auto_flush: _sys.stdout.flush() return self def format(self, format_, *args): return self.printf(format_, *args) def write(self, b, off=0, length=-1): if isinstance(b, int): self._out.write(bytes([b])) elif isinstance(b, bytes): if length < 0: self._out.write(b[off:]) else: self._out.write(b[off:off + length]) elif isinstance(b, str): self._out.write(b) if self._auto_flush: self._out.flush() def flush(self): self._out.flush() def close(self): self.flush() def checkError(self): return self._error def setError(self): self._error = True def clearError(self): self._error = False def append(self, c): if isinstance(c, str): self._out.write(c) else: self._out.write(str(c)) return self # ============================================================ # SequenceInputStream # ============================================================ class SequenceInputStream(Stub): """java.io.SequenceInputStream""" def __init__(self, *args): super().__init__() if len(args) == 1: self._enumerator = iter(args[0]) self._current = next(self._enumerator, None) elif len(args) == 2: self._enumerator = iter(args) self._current = args[0] else: self._enumerator = iter([]) self._current = None def read(self, b=None, off=0, length=-1): while self._current is not None: if hasattr(self._current, 'read'): if b is None: data = self._current.read() else: if length < 0: data = self._current.read(len(b) - off) else: data = self._current.read(min(length, len(b) - off)) if data: if b is not None: for i, byte in enumerate(data): b[off + i] = byte return len(data) self._current = next(self._enumerator, None) else: self._current = next(self._enumerator, None) return -1 def close(self): self._current = None # ============================================================ # StringBufferInputStream # ============================================================ class StringBufferInputStream(Stub): """java.io.StringBufferInputStream""" def __init__(self, s): super().__init__() self._string = s self._pos = 0 self._count = len(s) def read(self, b=None, off=0, length=-1): if self._pos >= self._count: return -1 if b is None: val = ord(self._string[self._pos]) if self._pos < self._count else -1 self._pos += 1 return val if length < 0: length = len(b) - off available = self._count - self._pos to_read = min(available, length) for i in range(to_read): b[off + i] = ord(self._string[self._pos + i]) self._pos += to_read return to_read def available(self): return self._count - self._pos def reset(self): self._pos = 0 # ============================================================ # LineNumberReader # ============================================================ class LineNumberReader(Stub): """java.io.LineNumberReader""" def __init__(self, reader, sz=8192): super().__init__() self._reader = reader self._line_number = 0 self._buffer = "" self._pos = 0 self._marked_pos = 0 self._marked_line = 0 self._sz = sz def readLine(self): result = [] while self._pos < len(self._buffer) or self._fill_buffer(): if self._pos < len(self._buffer): c = self._buffer[self._pos] self._pos += 1 if c == "\n": self._line_number += 1 return "".join(result) if c == "\r": self._line_number += 1 if self._pos < len(self._buffer) and self._buffer[self._pos] == "\n": self._pos += 1 return "".join(result) result.append(c) else: break if not result: return None return "".join(result) def getLineNumber(self): return self._line_number def setLineNumber(self, line_number): self._line_number = line_number def read(self, cbuf=None, off=0, length=-1): if cbuf is None: if self._pos < len(self._buffer) or self._fill_buffer(): if self._pos < len(self._buffer): c = self._buffer[self._pos] self._pos += 1 return ord(c) return -1 count = 0 while count < (length if length >= 0 else len(cbuf) - off): if self._pos >= len(self._buffer) and not self._fill_buffer(): break cbuf[off + count] = self._buffer[self._pos] self._pos += 1 count += 1 return count if count > 0 else -1 def close(self): if self._reader and hasattr(self._reader, 'close'): self._reader.close() def mark(self, read_ahead_limit): self._marked_pos = self._pos self._marked_line = self._line_number def reset(self): self._pos = self._marked_pos self._line_number = self._marked_line def skip(self, n): actual = 0 while actual < n: if self._pos >= len(self._buffer) and not self._fill_buffer(): break self._pos += 1 actual += 1 return actual def ready(self): return self._pos < len(self._buffer) def _fill_buffer(self): if self._reader and hasattr(self._reader, 'read'): data = self._reader.read(self._sz) if isinstance(data, str): self._buffer = data elif isinstance(data, bytes): self._buffer = data.decode('utf-8') else: self._buffer = "" self._pos = 0 return len(self._buffer) > 0 return False # ============================================================ # CharArrayReader / CharArrayWriter # ============================================================ class CharArrayReader(Stub): """java.io.CharArrayReader""" def __init__(self, buf, offset=0, length=None): super().__init__() self._buf = list(buf) if length is not None: self._buf = self._buf[offset:offset + length] else: self._buf = self._buf[offset:] self._pos = 0 self._marked_pos = 0 def read(self, cbuf=None, off=0, length=-1): if self._pos >= len(self._buf): return -1 if cbuf is None: val = ord(self._buf[self._pos]) if isinstance(self._buf[self._pos], str) else self._buf[self._pos] self._pos += 1 return val if length < 0: length = len(cbuf) - off available = len(self._buf) - self._pos to_read = min(available, length) for i in range(to_read): cbuf[off + i] = ( ord(self._buf[self._pos + i]) if isinstance(self._buf[self._pos + i], str) else self._buf[self._pos + i] ) self._pos += to_read return to_read def close(self): self._buf = [] def mark(self, read_ahead_limit): self._marked_pos = self._pos def reset(self): self._pos = self._marked_pos def skip(self, n): actual = min(n, len(self._buf) - self._pos) self._pos += actual return actual def ready(self): return self._pos < len(self._buf) class CharArrayWriter(Stub): """java.io.CharArrayWriter""" def __init__(self, initial_size=32): super().__init__() self._buf = [] def write(self, c, off=0, length=-1): if isinstance(c, str): self._buf.append(c) elif isinstance(c, (list, tuple)): if length < 0: length = len(c) - off self._buf.extend(c[off:off + length]) elif isinstance(c, int): self._buf.append(chr(c)) def writeTo(self, out): if hasattr(out, 'write'): out.write(self.toString()) def toString(self): return "".join(str(x) for x in self._buf) def size(self): return len(self._buf) def toCharArray(self): return list(self.toString()) def flush(self): pass def close(self): pass def reset(self): self._buf = [] # ============================================================ # FilterReader / FilterWriter # ============================================================ class FilterReader(Stub): """java.io.FilterReader""" def __init__(self, reader): super().__init__() self._reader = reader def read(self, cbuf=None, off=0, length=-1): if self._reader: return self._reader.read(cbuf, off, length) return -1 def close(self): if self._reader: self._reader.close() def mark(self, read_limit): if self._reader and hasattr(self._reader, 'mark'): self._reader.mark(read_limit) def reset(self): if self._reader and hasattr(self._reader, 'reset'): self._reader.reset() def ready(self): if self._reader and hasattr(self._reader, 'ready'): return self._reader.ready() return False def skip(self, n): if self._reader and hasattr(self._reader, 'skip'): return self._reader.skip(n) return 0 class FilterWriter(Stub): """java.io.FilterWriter""" def __init__(self, writer): super().__init__() self._writer = writer def write(self, c, off=0, length=-1): if self._writer: if isinstance(c, str): self._writer.write(c) elif isinstance(c, int): self._writer.write(chr(c)) def flush(self): if self._writer: self._writer.flush() def close(self): if self._writer: self._writer.close() # ============================================================ # PushbackReader # ============================================================ class PushbackReader(FilterReader): """java.io.PushbackReader""" def __init__(self, reader, size=1): super().__init__(reader) self._pushback_buf = [] self._pushback_size = size def read(self, cbuf=None, off=0, length=-1): if self._pushback_buf: ch = self._pushback_buf.pop() if cbuf is None: return ord(ch) if isinstance(ch, str) else ch cbuf[off] = ord(ch) if isinstance(ch, str) else ch return 1 return super().read(cbuf, off, length) def unread(self, c): if len(self._pushback_buf) >= self._pushback_size: raise IOException("Pushback buffer full") if isinstance(c, (list, tuple)): for ch in reversed(c): self._pushback_buf.append(ch) else: self._pushback_buf.append(c) # ============================================================ # PushbackInputStream # ============================================================ class PushbackInputStream(Stub): """java.io.PushbackInputStream""" def __init__(self, inp, size=1): super().__init__() self._input = inp self._pushback_buf = bytearray() self._pushback_size = size def read(self, b=None, off=0, length=-1): if self._pushback_buf and b is None: return self._pushback_buf.pop(0) if b is not None: count = 0 while count < (length if length >= 0 else len(b) - off): if self._pushback_buf: b[off + count] = self._pushback_buf.pop(0) elif self._input: val = self._input.read(1) if val == -1 or val == b"" or val is None: break b[off + count] = val[0] if isinstance(val, bytes) else val else: break count += 1 return count if count > 0 else -1 if self._pushback_buf: return self._pushback_buf.pop(0) if self._input: val = self._input.read(1) if val is not None and val != -1: return val[0] if isinstance(val, bytes) else val return -1 def unread(self, b, off=0, length=-1): if isinstance(b, int): if len(self._pushback_buf) >= self._pushback_size: raise IOException("Pushback buffer full") self._pushback_buf.insert(0, b & 0xFF) elif isinstance(b, (bytes, bytearray)): if length < 0: length = len(b) - off if len(self._pushback_buf) + length > self._pushback_size: raise IOException("Pushback buffer full") for i in range(length - 1, -1, -1): self._pushback_buf.insert(0, b[off + i]) def available(self): return len(self._pushback_buf) + (self._input.available() if self._input else 0) def close(self): if self._input: self._input.close() self._pushback_buf = bytearray() # ============================================================ # StreamTokenizer # ============================================================ class StreamTokenizer(Stub): """java.io.StreamTokenizer""" def __init__(self, reader=None, inp=None): super().__init__() if reader: self._reader = reader elif inp: import io self._reader = io.TextIOWrapper(io.BufferedReader(inp)) else: self._reader = None self.ttype = 0 self.sval = None self.nval = 0.0 self._reset_syntax() def _reset_syntax(self): self._ordinary_chars = set() self._word_chars = set() self._whitespace_chars = set(range(0x20)) self._comment_char = None self._quote_char = None self._parse_numbers = True self._lower_case_mode = False self._slash_slash_comments = False self._slash_star_comments = False self._eol_is_significant = False self._lower_case_mode = False self._pushback = None def resetSyntax(self): self._reset_syntax() def wordChars(self, low, high): for c in range(low, high + 1): self._word_chars.add(c) def whitespaceChars(self, low, high): for c in range(low, high + 1): self._whitespace_chars.add(c) def ordinaryChars(self, low, high): for c in range(low, high + 1): self._ordinary_chars.add(c) def ordinaryChar(self, ch): self._ordinary_chars.add(ch) def commentChar(self, ch): self._comment_char = ch def quoteChar(self, ch): self._quote_char = ch def parseNumbers(self): self._parse_numbers = True def eolIsSignificant(self, flag): self._eol_is_significant = flag def slashSlashComments(self, flag): self._slash_slash_comments = flag def slashStarComments(self, flag): self._slash_star_comments = flag def lowerCaseMode(self, flag): self._lower_case_mode = flag def pushBack(self): self._pushback = self.ttype def lineno(self): return 1 def toString(self): if self.ttype == -1: return "EOF" if self.ttype == -3: return f"WORD={self.sval}" if self.ttype == -2: return f"NUMBER={self.nval}" return f"CHAR={chr(self.ttype) if self.ttype < 256 else '?'}" def nextToken(self): if self._pushback is not None: pb = self._pushback self._pushback = None self.ttype = pb return self.ttype if self._reader is None: self.ttype = -1 # TT_EOF return -1 ch = self._read_char() if ch is None: self.ttype = -1 return -1 # Check for newlines if ch == "\n": if self._eol_is_significant: self.ttype = 10 # TT_EOL return 10 return self.nextToken() # Whitespace if ord(ch) in self._whitespace_chars: return self.nextToken() # Comment char if self._comment_char is not None and ord(ch) == self._comment_char: while ch is not None and ch != "\n": ch = self._read_char() return self.nextToken() # Quote char if self._quote_char is not None and ord(ch) == self._quote_char: buf = [] while True: ch = self._read_char() if ch is None or ord(ch) == self._quote_char: break buf.append(ch) self.sval = "".join(buf) if self._lower_case_mode: self.sval = self.sval.lower() self.ttype = ord(self._quote_char) return self.ttype # Number if self._parse_numbers and (ch.isdigit() or ch in ('-', '.')): buf = [ch] if ch in ('-', '.'): next_ch = self._peek_char() if next_ch is not None and (next_ch.isdigit() or next_ch == '.'): buf.append(self._read_char()) while True: ch = self._read_char() if ch is None or not (ch.isdigit() or ch == '.' or ch in ('e', 'E', '-', '+')): if ch is not None: self._unread_char(ch) break buf.append(ch) num_str = "".join(buf) try: self.nval = float(num_str) except ValueError: self.nval = 0.0 self.ttype = -2 # TT_NUMBER return -2 # Word chars if ord(ch) in self._word_chars: buf = [ch] while True: next_ch = self._peek_char() if next_ch is not None and ord(next_ch) in self._word_chars: buf.append(self._read_char()) else: break self.sval = "".join(buf) if self._lower_case_mode: self.sval = self.sval.lower() self.ttype = -3 # TT_WORD return -3 # Ordinary char self.ttype = ord(ch) return ord(ch) def _read_char(self): if self._reader is None: return None try: if hasattr(self._reader, 'read'): c = self._reader.read(1) if c and isinstance(c, bytes): c = c.decode('utf-8') return c if c else None return None except Exception: return None def _peek_char(self): if self._reader is None: return None try: c = self._reader.read(1) if c and isinstance(c, bytes): c = c.decode('utf-8') if c: self._unread_char(c) return c if c else None except Exception: return None def _unread_char(self, c): if hasattr(self._reader, 'seek'): self._reader.seek(self._reader.tell() - 1) TT_EOF = -1 TT_EOL = 10 TT_NUMBER = -2 TT_WORD = -3