class W_SyntaxError(W_ScriptError): classdef = ClassDef("SyntaxError", W_ScriptError.classdef) method_allocate = new_exception_allocate(classdef)
class W_NotImplementedError(W_ScriptError): classdef = ClassDef("NotImplementedError", W_ScriptError.classdef) method_allocate = new_exception_allocate(classdef)
class W_StringObject(W_Object): classdef = ClassDef("String", W_Object.classdef) classdef.include_module(Comparable) def __init__(self, space, storage, strategy): W_Object.__init__(self, space) self.str_storage = storage self.strategy = strategy def __deepcopy__(self, memo): obj = super(W_StringObject, self).__deepcopy__(memo) obj.str_storage = copy.deepcopy(self.str_storage, memo) obj.strategy = copy.deepcopy(self.strategy, memo) return obj @staticmethod def newstr_fromstr(space, strvalue): strategy = space.fromcache(ConstantStringStrategy) storage = strategy.erase(strvalue) return W_StringObject(space, storage, strategy) @staticmethod def newstr_fromchars(space, chars): strategy = space.fromcache(MutableStringStrategy) storage = strategy.erase(chars) return W_StringObject(space, storage, strategy) def str_w(self, space): return self.strategy.str_w(self.str_storage) def liststr_w(self, space): return self.strategy.liststr_w(self.str_storage) def length(self): return self.strategy.length(self.str_storage) def copy(self, space): return self.strategy.copy(space, self.str_storage) def replace(self, space, chars): strategy = space.fromcache(MutableStringStrategy) self.str_storage = strategy.erase(chars) self.strategy = strategy def extend(self, space, w_other): self.strategy.to_mutable(space, self) strategy = self.strategy assert isinstance(strategy, MutableStringStrategy) storage = strategy.unerase(self.str_storage) w_other.strategy.extend_into(w_other.str_storage, storage) def clear(self, space): self.strategy.to_mutable(space, self) self.strategy.clear(self) def tr_trans(self, space, source, replacement, squeeze): change_made = False string = space.str_w(self) new_string = [] is_negative_set = len(source) > 1 and source[0] == "^" if is_negative_set: source = source[1:] trans_table = create_trans_table(source, replacement, is_negative_set) if squeeze: last_repl = "" for char in string: repl = trans_table[ord(char)] if last_repl == repl: continue if repl != char: last_repl = repl if not change_made: change_made = True new_string.append(repl) else: for char in string: repl = trans_table[ord(char)] if not change_made and repl != char: change_made = True new_string.append(repl) return new_string if change_made else None @classdef.method("to_str") @classdef.method("to_s") def method_to_s(self, space): return self @classdef.method("+") def method_plus(self, space, w_other): assert isinstance(w_other, W_StringObject) total_size = self.length() + w_other.length() s = space.newstr_fromchars(newlist_hint(total_size)) s.extend(space, self) s.extend(space, w_other) return s @classdef.method("<<") def method_lshift(self, space, w_other): assert isinstance(w_other, W_StringObject) self.extend(space, w_other) return self @classdef.method("size") @classdef.method("length") def method_length(self, space): return space.newint(self.length()) @classdef.method("hash") def method_hash(self, space): return space.newint(self.strategy.hash(self.str_storage)) @classdef.method("[]") def method_subscript(self, space, w_idx, w_count=None): start, end, as_range, nil = space.subscript_access(self.length(), w_idx, w_count=w_count) if nil: return space.w_nil elif as_range: assert start >= 0 assert end >= 0 return self.strategy.getslice(space, self.str_storage, start, end) else: return space.newstr_fromstr( self.strategy.getitem(self.str_storage, start)) @classdef.method("<=>") def method_comparator(self, space, w_other): if isinstance(w_other, W_StringObject): s1 = space.str_w(self) s2 = space.str_w(w_other) if s1 < s2: return space.newint(-1) elif s1 == s2: return space.newint(0) elif s1 > s2: return space.newint(1) else: if space.respond_to( w_other, space.newsymbol("to_str")) and space.respond_to( w_other, space.newsymbol("<=>")): tmp = space.send(w_other, space.newsymbol("<=>"), [self]) if tmp is not space.w_nil: return space.newint(-space.int_w(tmp)) return space.w_nil @classdef.method("freeze") def method_freeze(self, space): pass @classdef.method("dup") def method_dup(self, space): return self.copy(space) @classdef.method("to_sym") @classdef.method("intern") def method_to_sym(self, space): return space.newsymbol(space.str_w(self)) @classdef.method("clear") def method_clear(self, space): self.clear(space) return self @classdef.method("ljust", integer="int", padstr="str") def method_ljust(self, space, integer, padstr=" "): if not padstr: raise space.error(space.w_ArgumentError, "zero width padding") elif integer <= self.length(): return self.copy(space) else: pad_len = integer - self.length() - 1 assert pad_len >= 0 chars = [] chars += space.str_w(self) for i in xrange(pad_len / len(padstr)): chars += padstr chars += padstr[:pad_len % len(padstr) + 1] return space.newstr_fromchars(chars) @classdef.method("split", limit="int") def method_split(self, space, w_sep=None, limit=-1): if w_sep is None: sep = None elif isinstance(w_sep, W_StringObject): sep = space.str_w(w_sep) else: raise NotImplementedError("Regexp separators for String#split") results = space.str_w(self).split(sep, limit - 1) return space.newarray([space.newstr_fromstr(s) for s in results]) @classdef.method("downcase!") def method_downcase(self, space): self.strategy.to_mutable(space, self) changed = self.strategy.downcase(self.str_storage) return self if changed else space.w_nil @classdef.method("to_i", radix="int") def method_to_i(self, space, radix=10): if not 2 <= radix <= 36: raise space.error(space.w_ArgumentError, "invalid radix %d" % radix) s = space.str_w(self) if not s: return space.newint(0) i = 0 neg = s[i] == "-" if neg: i += 1 val = 0 while i < len(s): c = ord(s[i]) if ord("a") <= c <= ord("z"): digit = c - ord("a") + 10 elif ord("A") <= c <= ord("Z"): digit = c - ord("A") + 10 elif ord("0") <= c <= ord("9"): digit = c - ord("0") else: break if digit >= radix: break val = val * radix + digit i += 1 if neg: val = -val return space.newint(val) @classdef.method("tr", source="str", replacement="str") def method_tr(self, space, source, replacement): string = self.copy(space) new_string = self.tr_trans(space, source, replacement, False) return space.newstr_fromchars(new_string) if new_string else string @classdef.method("tr!", source="str", replacement="str") def method_tr_i(self, space, source, replacement): new_string = self.tr_trans(space, source, replacement, False) self.replace(space, new_string) return self if new_string else space.w_nil @classdef.method("tr_s", source="str", replacement="str") def method_tr_s(self, space, source, replacement): string = self.copy(space) new_string = self.tr_trans(space, source, replacement, True) return space.newstr_fromchars(new_string) if new_string else string @classdef.method("tr_s!", source="str", replacement="str") def method_tr_s_i(self, space, source, replacement): new_string = self.tr_trans(space, source, replacement, True) self.replace(space, new_string) return self if new_string else space.w_nil classdef.app_method(""" def empty? self.length == 0 end """)
class W_IntegerObject(W_NumericObject): classdef = ClassDef("Integer", W_NumericObject.classdef) @classdef.method("to_i") def method_to_i(self, space): return self
class W_ThreadObject(W_Object): classdef = ClassDef("Thread", W_Object.classdef)
class W_EncodingObject(W_Object): classdef = ClassDef("Encoding", W_Object.classdef)
class W_BaseObject(object): __metaclass__ = ObjectMetaclass _attrs_ = [] classdef = ClassDef("BasicObject") def __deepcopy__(self, memo): obj = object.__new__(self.__class__) memo[id(self)] = obj return obj @classmethod def setup_class(cls, space, w_cls): pass def getclass(self, space): return space.getclassobject(self.classdef) def is_kind_of(self, space, w_cls): return w_cls.is_ancestor_of(self.getclass(space)) def attach_method(self, space, name, func): w_cls = space.getsingletonclass(self) w_cls.define_method(space, name, func) def is_true(self, space): return True @classdef.method("initialize") def method_initialize(self): return self @classdef.method("__id__") def method___id__(self, space): return space.newint(compute_unique_id(self)) @classdef.method("method_missing") def method_method_missing(self, space, w_name): name = space.symbol_w(w_name) class_name = space.str_w( space.send(self.getclass(space), space.newsymbol("name"))) raise space.error(space.w_NoMethodError, "undefined method `%s' for %s" % (name, class_name)) @classdef.method("==") @classdef.method("equal?") def method_eq(self, space, w_other): return space.newbool(self is w_other) @classdef.method("!") def method_not(self, space): return space.newbool(not space.is_true(self)) @classdef.method("!=") def method_ne(self, space, w_other): return space.newbool(not space.is_true( space.send(self, space.newsymbol("=="), [w_other]))) @classdef.method("__send__", method="str") def method_send(self, space, method, args_w, block): return space.send(self, space.newsymbol(method), args_w[1:], block) @classdef.method("instance_eval", string="str", filename="str") def method_instance_eval(self, space, string=None, filename=None, w_lineno=None, block=None): if string is not None: if filename is None: filename = "instance_eval" if w_lineno is not None: lineno = space.int_w(w_lineno) else: lineno = 1 return space.execute(string, self, space.getclass(self), filename, lineno) else: space.invoke_block( block.copy(w_self=self, w_scope=space.getclass(self)), [])
class W_FileObject(W_IOObject): classdef = ClassDef("File", W_IOObject.classdef) @classmethod def setup_class(cls, space, w_cls): if sys.platform == "win32": w_alt_seperator = space.newstr_fromstr("\\") w_fnm_syscase = space.newint(0x08) else: w_alt_seperator = space.w_nil w_fnm_syscase = space.newint(0) space.set_const(w_cls, "SEPARATOR", space.newstr_fromstr("/")) space.set_const(w_cls, "ALT_SEPARATOR", w_alt_seperator) space.set_const(w_cls, "FNM_SYSCASE", w_fnm_syscase) space.set_const(w_cls, "RDONLY", space.newint(os.O_RDONLY)) space.set_const(w_cls, "WRONLY", space.newint(os.O_WRONLY)) space.set_const(w_cls, "RDWR", space.newint(os.O_RDWR)) space.set_const(w_cls, "APPEND", space.newint(os.O_APPEND)) space.set_const(w_cls, "CREAT", space.newint(os.O_CREAT)) space.set_const(w_cls, "EXCL", space.newint(os.O_EXCL)) space.set_const(w_cls, "TRUNC", space.newint(os.O_TRUNC)) @classdef.singleton_method("allocate") def method_allocate(self, space, args_w): return W_FileObject(space) @classdef.method("initialize", filename="str") def method_initialize(self, space, filename, w_mode=None, w_perm_or_opt=None, w_opt=None): if isinstance(w_perm_or_opt, W_HashObject): assert w_opt is None perm = 0665 w_opt = w_perm_or_opt elif w_opt is not None: perm = space.int_w(w_perm_or_opt) else: perm = 0665 if w_mode is None: mode = os.O_RDONLY elif isinstance(w_mode, W_StringObject): mode_str = space.str_w(w_mode) if "+" in mode_str: mode = os.O_RDWR if "w" in mode_str: mode |= os.O_CREAT | os.O_TRUNC elif "a" in mode_str: mode |= os.O_CREAT | os.O_APPEND elif mode_str == "r": mode = os.O_RDONLY elif mode_str == "w": mode = os.O_WRONLY | os.O_CREAT | os.O_TRUNC elif mode_str == "a": mode = os.O_WRONLY | os.O_CREAT | os.O_TRUNC else: raise space.error(space.w_ArgumentError, "invalid access mode %s" % mode_str) else: mode = space.int_w(w_mode) if w_perm_or_opt is not None or w_opt is not None: raise NotImplementedError( "options hash or permissions for File.new") self.fd = os.open(filename, mode, perm) return self @classdef.singleton_method("dirname", path="path") def method_dirname(self, space, path): if "/" not in path: return space.newstr_fromstr(".") idx = path.rfind("/") while idx > 0 and path[idx - 1] == "/": idx -= 1 if idx == 0: return space.newstr_fromstr("/") assert idx >= 0 return space.newstr_fromstr(path[:idx]) @classdef.singleton_method("expand_path", path="path", dir="path") def method_expand_path(self, space, path, dir=None): if path and path[0] == "~": if len(path) >= 2 and path[1] == "/": path = os.environ["HOME"] + path[1:] elif len(path) < 2: return space.newstr_fromstr(os.environ["HOME"]) else: raise NotImplementedError elif not path or path[0] != "/": if dir is not None: dir = space.str_w( W_FileObject.method_expand_path(self, space, dir)) else: dir = os.getcwd() path = dir + "/" + path items = [] parts = path.split("/") for part in parts: if part == "..": items.pop() elif part and part != ".": items.append(part) if not items: return space.newstr_fromstr("/") return space.newstr_fromstr("/" + "/".join(items)) @classdef.singleton_method("join") def singleton_method_join(self, space, args_w): sep = space.str_w(space.find_const(self, "SEPARATOR")) result = [] for w_arg in args_w: if isinstance(w_arg, W_ArrayObject): string = space.str_w( W_FileObject.singleton_method_join(self, space, space.listview(w_arg))) else: string = space.str_w(w_arg) if string.startswith(sep): while result and result[-1] == sep: result.pop() elif result and not result[-1] == sep: result += sep result += string return space.newstr_fromchars(result) @classdef.singleton_method("exists?", filename="str") @classdef.singleton_method("exist?", filename="str") def method_existp(self, space, filename): return space.newbool(os.path.exists(filename)) @classdef.singleton_method("file?", filename="str") def method_filep(self, space, filename): return space.newbool(os.path.isfile(filename)) @classdef.singleton_method("directory?", filename="str") def method_directoryp(self, space, filename): return space.newbool(os.path.isdir(filename)) @classdef.singleton_method("executable?", filename="str") def method_executablep(self, space, filename): return space.newbool( os.path.isfile(filename) and os.access(filename, os.X_OK))
class W_IOObject(W_Object): classdef = ClassDef("IO", W_Object.classdef) def __init__(self, space): W_Object.__init__(self, space) self.fd = -1 def __del__(self): # Do not close standard file streams if self.fd > 3: os.close(self.fd) def __deepcopy__(self, memo): obj = super(W_IOObject, self).__deepcopy__(memo) obj.fd = self.fd return obj @classmethod def setup_class(cls, space, w_cls): w_stdin = space.send(w_cls, space.newsymbol("new"), [space.newint(0)]) space.globals.set("$stdin", w_stdin) space.set_const(space.w_object, "STDIN", w_stdin) w_stdout = space.send(w_cls, space.newsymbol("new"), [space.newint(1)]) space.globals.set("$stdout", w_stdout) space.globals.set("$>", w_stdout) space.set_const(space.w_object, "STDOUT", w_stdout) w_stderr = space.send(w_cls, space.newsymbol("new"), [space.newint(2)]) space.globals.set("$stderr", w_stderr) space.set_const(space.w_object, "STDERR", w_stderr) @classdef.singleton_method("allocate") def method_allocate(self, space, args_w): return W_IOObject(space) @classdef.method("initialize") def method_initialize(self, space, w_fd_or_io, w_mode_str_or_int=None, w_opts=None): if isinstance(w_fd_or_io, W_IOObject): fd = w_fd_or_io.fd else: fd = space.int_w(w_fd_or_io) if isinstance(w_mode_str_or_int, W_StringObject): mode = space.str_w(w_mode_str_or_int) if ":" in mode: raise NotImplementedError("encoding for IO.new") elif w_mode_str_or_int is None: mode = None else: raise NotImplementedError("int mode for IO.new") if w_opts is not None: raise NotImplementedError("options hash for IO.new") if mode is None: mode = "r" self.fd = fd return self @classdef.method("read") def method_read(self, space, w_length=None, w_str=None): if w_length: length = space.int_w(w_length) if length < 0: raise space.error(space.w_ArgumentError, "negative length %d given" % length) else: length = -1 read_bytes = 0 read_chunks = [] while length < 0 or read_bytes < length: if length > 0: max_read = int(length - read_bytes) else: max_read = 8192 current_read = os.read(self.fd, max_read) if len(current_read) == 0: break read_bytes += len(current_read) read_chunks += current_read # Return nil on EOF if length is given if read_bytes == 0: return space.w_nil w_read_str = space.newstr_fromchars(read_chunks) if w_str is not None: w_str.clear(space) w_str.extend(space, w_read_str) return w_str else: return w_read_str classdef.app_method(""" def << s write(s) return self end """) @classdef.method("write") def method_write(self, space, w_str): string = space.str_w(space.send(w_str, space.newsymbol("to_s"))) bytes_written = os.write(self.fd, string) return space.newint(bytes_written) @classdef.method("flush") def method_flush(self, space): # We have no internal buffers to flush! return self @classdef.method("print") def method_print(self, space, args_w): if not args_w: w_last = space.globals.get("$_") if w_last: args_w.append(w_last) w_sep = space.globals.get("$,") if w_sep: sep = space.str_w(w_sep) else: sep = "" w_end = space.globals.get("$\\") if w_end: end = space.str_w(w_end) else: end = "" strings = [ space.str_w(space.send(w_arg, space.newsymbol("to_s"))) for w_arg in args_w ] os.write(self.fd, sep.join(strings)) os.write(self.fd, end) return space.w_nil @classdef.method("puts") def method_puts(self, space, args_w): for w_arg in args_w: string = space.str_w(space.send(w_arg, space.newsymbol("to_s"))) os.write(self.fd, string) if not string.endswith("\n"): os.write(self.fd, "\n") return space.w_nil