diff --git a/src/core/bool.cr b/src/core/bool.cr new file mode 100644 index 0000000..c2ae573 --- /dev/null +++ b/src/core/bool.cr @@ -0,0 +1,62 @@ +# Bool has only two possible values: `true` and `false`. They are constructed using these literals: +# +# ``` +# true # A Bool that is true +# false # A Bool that is false +# ``` +struct Bool + # Bitwise OR. Returns `true` if this bool or *other* is `true`, otherwise returns `false`. + # + # ``` + # false | false # => false + # false | true # => true + # true | false # => true + # true | true # => true + # ``` + def |(other : Bool) + self ? true : other + end + + # Bitwise AND. Returns `true` if this bool and *other* are `true`, otherwise returns `false`. + # + # ``` + # false & false # => false + # false & true # => false + # true & false # => false + # true & true # => true + # ``` + def &(other : Bool) + self ? other : false + end + + # Exclusive OR. Returns `true` if this bool is different from *other*, otherwise returns `false`. + # + # ``` + # false ^ false # => false + # false ^ true # => true + # true ^ false # => true + # true ^ true # => false + # ``` + def ^(other : Bool) + self != other + end + + # Returns a hash value for this boolean: 0 for `false`, 1 for `true`. + def hash + self ? 1 : 0 + end + + # Returns `"true"` for `true` and `"false"` for `false`. + def to_s + self ? "true" : "false" + end + + # Appends `"true"` for `true` and `"false"` for `false` to the given IO. + # def to_s(io) + # io << to_s + # end + + def clone + self + end +end diff --git a/src/core/comparable.cr b/src/core/comparable.cr new file mode 100644 index 0000000..2b8cf94 --- /dev/null +++ b/src/core/comparable.cr @@ -0,0 +1,54 @@ +# The `Comparable` mixin is used by classes whose objects may be ordered. +# +# Including types must provide an `<=>` method, which compares the receiver against +# another object, returning `-1`, `0`, or `+1` depending on whether the receiver is less than, +# equal to, or greater than the other object. +# +# `Comparable` uses `<=>` to implement the conventional comparison operators (`<`, `<=`, `==`, `>=`, and `>`). +module Comparable(T) + # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `-1`. + def <(other : T) + (self <=> other) < 0 + end + + # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `-1` or `0`. + def <=(other : T) + (self <=> other) <= 0 + end + + # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `0`. + # Also returns `true` if this and *other* are the same object. + def ==(other : T) + if self.is_a?(Reference) + # Need to do two different comparisons because the compiler doesn't yet + # restrict something like `other.is_a?(Reference) || other.is_a?(Nil)`. + # See #2461 + return true if other.is_a?(Reference) && self.same?(other) + return true if other.is_a?(Nil) && self.same?(other) + end + + (self <=> other) == 0 + end + + # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `1`. + def >(other : T) + (self <=> other) > 0 + end + + # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `1` or `0`. + def >=(other : T) + (self <=> other) >= 0 + end + + # Comparison operator. Returns `0` if the two objects are equal, + # a negative number if this object is considered less than *other*, + # or a positive number otherwise. + # + # Subclasses define this method to provide class-specific ordering. + # + # ``` + # # Sort in a descending way + # [4, 7, 2].sort { |x, y| y <=> x } # => [7, 4, 2] + # ``` + abstract def <=>(other : T) +end diff --git a/src/core/gc.cr b/src/core/gc.cr new file mode 100644 index 0000000..e1abbad --- /dev/null +++ b/src/core/gc.cr @@ -0,0 +1,13 @@ +module GC + def self.malloc(size : Int) + __crystal_malloc(size.to_u32) + end + + def self.malloc_atomic(size : Int) + __crystal_malloc_atomic(size.to_u32) + end + + def self.realloc(pointer : Void*, size : Int) + __crystal_realloc(pointer, size.to_u32) + end +end diff --git a/src/core/gc/boehm.cr b/src/core/gc/boehm.cr new file mode 100644 index 0000000..ff21ded --- /dev/null +++ b/src/core/gc/boehm.cr @@ -0,0 +1,152 @@ +# @[Link("pthread")] +# {% if flag?(:freebsd) %} +# @[Link("gc-threaded")] +# {% else %} + # @[Link("gc")] +# {% end %} + +lib LibGC + alias Int = LibCR::Int + alias SizeT = LibCR::SizeT + alias Word = LibCR::ULong + + fun init = GC_init + fun malloc = GC_malloc(size : SizeT) : Void* + fun malloc_atomic = GC_malloc_atomic(size : SizeT) : Void* + fun realloc = GC_realloc(ptr : Void*, size : SizeT) : Void* + fun free = GC_free(ptr : Void*) + fun collect_a_little = GC_collect_a_little : Int + fun collect = GC_gcollect + fun add_roots = GC_add_roots(low : Void*, high : Void*) + fun enable = GC_enable + fun disable = GC_disable + fun set_handle_fork = GC_set_handle_fork(value : Int) + + fun base = GC_base(displaced_pointer : Void*) : Void* + fun is_heap_ptr = GC_is_heap_ptr(pointer : Void*) : Int + fun general_register_disappearing_link = GC_general_register_disappearing_link(link : Void**, obj : Void*) : Int + + type Finalizer = Void*, Void* -> + fun register_finalizer = GC_register_finalizer(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**) + fun register_finalizer_ignore_self = GC_register_finalizer_ignore_self(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**) + fun invoke_finalizers = GC_invoke_finalizers : Int + + fun get_heap_usage_safe = GC_get_heap_usage_safe(heap_size : Word*, free_bytes : Word*, unmapped_bytes : Word*, bytes_since_gc : Word*, total_bytes : Word*) + fun set_max_heap_size = GC_set_max_heap_size(Word) + + fun get_start_callback = GC_get_start_callback : Void* + fun set_start_callback = GC_set_start_callback(callback : ->) + + fun set_push_other_roots = GC_set_push_other_roots(proc : ->) + fun get_push_other_roots = GC_get_push_other_roots : -> + + fun push_all_eager = GC_push_all_eager(bottom : Void*, top : Void*) + + $stackbottom = GC_stackbottom : Void* + + fun set_on_collection_event = GC_set_on_collection_event(cb : ->) + + $gc_no = GC_gc_no : LibCR::ULong + $bytes_found = GC_bytes_found : LibCR::Long + # GC_on_collection_event isn't exported. Can't collect totals without it. + # bytes_allocd, heap_size, unmapped_bytes are macros + + fun size = GC_size(addr : Void*) : LibCR::SizeT + + # Boehm GC requires to use GC_pthread_create and GC_pthread_join instead of pthread_create and pthread_join + fun pthread_create = GC_pthread_create(thread : LibCR::PthreadT*, attr : Void*, start : Void* ->, arg : Void*) : LibCR::Int + fun pthread_join = GC_pthread_join(thread : LibCR::PthreadT, value : Void**) : LibCR::Int + fun pthread_detach = GC_pthread_detach(thread : LibCR::PthreadT) : LibCR::Int +end + +# :nodoc: +fun __crystal_malloc(size : UInt32) : Void* + LibGC.malloc(size) +end + +# :nodoc: +fun __crystal_malloc_atomic(size : UInt32) : Void* + LibGC.malloc_atomic(size) +end + +# :nodoc: +fun __crystal_realloc(ptr : Void*, size : UInt32) : Void* + LibGC.realloc(ptr, size) +end + +module GC + def self.init + LibGC.set_handle_fork(1) + LibGC.init + end + + def self.collect + LibGC.collect + end + + def self.enable + LibGC.enable + end + + def self.disable + LibGC.disable + end + + def self.free(pointer : Void*) + LibGC.free(pointer) + end + + def self.add_finalizer(object : Reference) + add_finalizer_impl(object) + end + + def self.add_finalizer(object) + # Nothing + end + + private def self.add_finalizer_impl(object : T) forall T + LibGC.register_finalizer_ignore_self(object.as(Void*), + ->(obj, data) { obj.as(T).finalize }, + nil, nil, nil) + nil + end + + def self.add_root(object : Reference) + roots = @@roots ||= [] of Pointer(Void) + roots << Pointer(Void).new(object.object_id) + end + + def self.register_disappearing_link(pointer : Void**) + base = LibGC.base(pointer.value) + LibGC.general_register_disappearing_link(pointer, base) + end + + def self.is_heap_ptr(pointer : Void*) + LibGC.is_heap_ptr(pointer) != 0 + end + + record Stats, + # collections : LibCR::ULong, + # bytes_found : LibCR::Long, + heap_size : LibCR::ULong, + free_bytes : LibCR::ULong, + unmapped_bytes : LibCR::ULong, + bytes_since_gc : LibCR::ULong, + total_bytes : LibCR::ULong + + def self.stats + LibGC.get_heap_usage_safe(out heap_size, out free_bytes, out unmapped_bytes, out bytes_since_gc, out total_bytes) + # collections = LibGC.gc_no - 1 + # bytes_found = LibGC.bytes_found + + Stats.new( + # collections: collections, + # bytes_found: bytes_found, + heap_size: heap_size, + free_bytes: free_bytes, + unmapped_bytes: unmapped_bytes, + bytes_since_gc: bytes_since_gc, + total_bytes: total_bytes + ) + end +end diff --git a/src/core/indexable.cr b/src/core/indexable.cr new file mode 100644 index 0000000..088ab3b --- /dev/null +++ b/src/core/indexable.cr @@ -0,0 +1,544 @@ +# A container that allows accessing elements via a numeric index. +# +# Indexing starts at `0`. A negative index is assumed to be +# relative to the end of the container: `-1` indicates the last element, +# `-2` is the next to last element, and so on. +# +# Types including this module are typically `Array`-like types. +module Indexable(T) + include Iterable(T) + # TODO: Implement this + # include Enumerable(T) + + # Returns the number of elements in this container. + abstract def size + + # Returns the element at the given *index*, without doing any bounds check. + # + # `Indexable` makes sure to invoke this method with *index* in `0...size`, + # so converting negative indices to positive ones is not needed here. + # + # Clients never invoke this method directly. Instead, they access + # elements with `#[](index)` and `#[]?(index)`. + # + # This method should only be directly invoked if you are absolutely + # sure the index is in bounds, to avoid a bounds check for a small boost + # of performance. + abstract def unsafe_at(index : Int) + + # Returns the element at the given *index*, if in bounds, + # otherwise executes the given block and returns its value. + # + # ``` + # a = [:foo, :bar] + # a.at(0) { :baz } # => :foo + # a.at(2) { :baz } # => :baz + # ``` + def at(index : Int) + index = check_index_out_of_bounds(index) do + return yield + end + unsafe_at(index) + end + + # Returns the element at the given *index*, if in bounds, + # otherwise raises `IndexError`. + # + # ``` + # a = [:foo, :bar] + # a.at(0) # => :foo + # a.at(2) # raises IndexError + # ``` + # TODO: Implement this + # @[AlwaysInline] + # def at(index : Int) + # at(index) { raise IndexError.new } + # end + + # Returns the element at the given *index*. + # + # Negative indices can be used to start counting from the end of the array. + # Raises `IndexError` if trying to access an element outside the array's range. + # + # ``` + # ary = ['a', 'b', 'c'] + # ary[0] # => 'a' + # ary[2] # => 'c' + # ary[-1] # => 'c' + # ary[-2] # => 'b' + # + # ary[3] # raises IndexError + # ary[-4] # raises IndexError + # ``` + # TODO: Implement this + # @[AlwaysInline] + # def [](index : Int) + # at(index) + # end + + # Returns the element at the given *index*. + # + # Negative indices can be used to start counting from the end of the array. + # Returns `nil` if trying to access an element outside the array's range. + # + # ``` + # ary = ['a', 'b', 'c'] + # ary[0]? # => 'a' + # ary[2]? # => 'c' + # ary[-1]? # => 'c' + # ary[-2]? # => 'b' + # + # ary[3]? # nil + # ary[-4]? # nil + # ``` + # TODO: Implement this + # @[AlwaysInline] + # def []?(index : Int) + # at(index) { nil } + # end + + # By using binary search, returns the first element + # for which the passed block returns `true`. + # + # If the block returns `false`, the finding element exists + # behind. If the block returns `true`, the finding element + # is itself or exists infront. + # + # Binary search needs sorted array, so `self` has to be sorted. + # + # Returns `nil` if the block didn't return `true` for any element. + # + # ``` + # [2, 5, 7, 10].bsearch { |x| x >= 4 } # => 5 + # [2, 5, 7, 10].bsearch { |x| x > 10 } # => nil + # ``` + def bsearch + bsearch_index { |value| yield value }.try { |index| unsafe_at(index) } + end + + # By using binary search, returns the index of the first element + # for which the passed block returns `true`. + # + # If the block returns `false`, the finding element exists + # behind. If the block returns `true`, the finding element + # is itself or exists infront. + # + # Binary search needs sorted array, so `self` has to be sorted. + # + # Returns `nil` if the block didn't return `true` for any element. + # + # ``` + # [2, 5, 7, 10].bsearch_index { |x, i| x >= 4 } # => 1 + # [2, 5, 7, 10].bsearch_index { |x, i| x > 10 } # => nil + # ``` + def bsearch_index + (0...size).bsearch { |index| yield unsafe_at(index), index } + end + + # Calls the given block once for each element in `self`, passing that + # element as a parameter. + # + # ``` + # a = ["a", "b", "c"] + # a.each { |x| print x, " -- " } + # ``` + # + # produces: + # + # ```text + # a -- b -- c -- + # ``` + def each + each_index do |i| + yield unsafe_at(i) + end + end + + # Returns an `Iterator` for the elements of `self`. + # + # ``` + # a = ["a", "b", "c"] + # iter = a.each + # iter.next # => "a" + # iter.next # => "b" + # ``` + # + # The returned iterator keeps a reference to `self`: if the array + # changes, the returned values of the iterator change as well. + # TODO: Implement this + # def each + # ItemIterator(self, T).new(self) + # end + + # Calls the given block once for each index in `self`, passing that + # index as a parameter. + # + # ``` + # a = ["a", "b", "c"] + # a.each_index { |x| print x, " -- " } + # ``` + # + # produces: + # + # ```text + # 0 -- 1 -- 2 -- + # ``` + def each_index : Nil + i = 0 + while i < size + yield i + i += 1 + end + end + + # Returns an `Iterator` for each index in `self`. + # + # ``` + # a = ["a", "b", "c"] + # iter = a.each_index + # iter.next # => 0 + # iter.next # => 1 + # ``` + # + # The returned iterator keeps a reference to `self`. If the array + # changes, the returned values of the iterator will change as well. + def each_index + IndexIterator.new(self) + end + + # Returns `true` if `self` is empty, `false` otherwise. + # + # ``` + # ([] of Int32).empty? # => true + # ([1]).empty? # => false + # ``` + def empty? + size == 0 + end + + # Optimized version of `equals?` used when `other` is also an `Indexable`. + def equals?(other : Indexable) + return false if size != other.size + each_with_index do |item, i| + return false unless yield(item, other.unsafe_at(i)) + end + true + end + + # Determines if `self` equals *other* according to a comparison + # done by the given block. + # + # If `self`'s size is the same as *other*'s size, this method yields + # elements from `self` and *other* in tandem: if the block returns true + # for all of them, this method returns `true`. Otherwise it returns `false`. + # + # ``` + # a = [1, 2, 3] + # b = ["a", "ab", "abc"] + # a.equals?(b) { |x, y| x == y.size } # => true + # a.equals?(b) { |x, y| x == y } # => false + # ``` + def equals?(other) + return false if size != other.size + each_with_index do |item, i| + return false unless yield(item, other[i]) + end + true + end + + # Returns the first element of `self` if it's not empty, or raises `IndexError`. + # + # ``` + # ([1, 2, 3]).first # => 1 + # ([] of Int32).first # raises IndexError + # ``` + # TODO: Implement this! + # def first + # first { raise IndexError.new } + # end + + # Returns the first element of `self` if it's not empty, or the given block's value. + # + # ``` + # ([1, 2, 3]).first { 4 } # => 1 + # ([] of Int32).first { 4 } # => 4 + # ``` + # TODO: Implement this! + # def first + # size == 0 ? yield : unsafe_at(0) + # end + + # Returns the first element of `self` if it's not empty, or `nil`. + # + # ``` + # ([1, 2, 3]).first? # => 1 + # ([] of Int32).first? # => nil + # ``` + # TODO: Implement this! + # def first? + # first { nil } + # end + + # Returns a hash code based on `self`'s size and elements. + # + # See also: `Object#hash`. + def hash + reduce(31 * size) do |memo, elem| + 31 * memo + elem.hash + end + end + + # Returns the index of the first appearance of *value* in `self` + # starting from the given *offset*, or `nil` if the value is not in `self`. + # + # ``` + # [1, 2, 3, 1, 2, 3].index(2, offset: 2) # => 4 + # ``` + def index(object, offset : Int = 0) + index(offset) { |e| e == object } + end + + # Returns the index of the first object in `self` for which the block + # returns `true`, starting from the given *offset*, or `nil` if no match + # is found. + # + # ``` + # [1, 2, 3, 1, 2, 3].index(offset: 2) { |x| x < 2 } # => 3 + # ``` + def index(offset : Int = 0) + offset += size if offset < 0 + return nil if offset < 0 + + offset.upto(size - 1) do |i| + if yield unsafe_at(i) + return i + end + end + nil + end + + # Returns the last element of `self` if it's not empty, or raises `IndexError`. + # + # ``` + # ([1, 2, 3]).last # => 3 + # ([] of Int32).last # raises IndexError + # ``` + def last + last { raise IndexError.new } + end + + # Returns the last element of `self` if it's not empty, or the given block's value. + # + # ``` + # ([1, 2, 3]).last { 4 } # => 3 + # ([] of Int32).last { 4 } # => 4 + # ``` + def last + size == 0 ? yield : unsafe_at(size - 1) + end + + # Returns the last element of `self` if it's not empty, or `nil`. + # + # ``` + # ([1, 2, 3]).last? # => 3 + # ([] of Int32).last? # => nil + # ``` + def last? + last { nil } + end + + # Same as `#each`, but works in reverse. + def reverse_each(&block) : Nil + (size - 1).downto(0) do |i| + yield unsafe_at(i) + end + end + + # Returns an `Iterator` over the elements of `self` in reverse order. + # TODO: Implement this + # def reverse_each + # ReverseItemIterator(self, T).new(self) + # end + + # Returns the index of the last appearance of *value* in `self`, or + # `nil` if the value is not in `self`. + # + # If *offset* is given, it defines the position to _end_ the search + # (elements beyond this point are ignored). + # + # ``` + # [1, 2, 3, 2, 3].rindex(2) # => 3 + # [1, 2, 3, 2, 3].rindex(2, offset: 2) # => 1 + # ``` + def rindex(value, offset = size - 1) + rindex(offset) { |elem| elem == value } + end + + # Returns the index of the first object in `self` for which the block + # returns `true`, starting from the last object, or `nil` if no match + # is found. + # + # If *offset* is given, the search starts from that index towards the + # first elements in `self`. + # + # ``` + # [1, 2, 3, 2, 3].rindex { |x| x < 3 } # => 3 + # [1, 2, 3, 2, 3].rindex(offset: 2) { |x| x < 3 } # => 1 + # ``` + def rindex(offset = size - 1) + offset += size if offset < 0 + return nil if offset >= size + + offset.downto(0) do |i| + if yield unsafe_at(i) + return i + end + end + nil + end + + # Returns a random element from `self`, using the given *random* number generator. + # Raises `IndexError` if `self` is empty. + # + # ``` + # a = [1, 2, 3] + # a.sample # => 2 + # a.sample # => 1 + # a.sample(Random.new(1)) # => 3 + # ``` + # TODO: Implement this + # def sample(random = Random::DEFAULT) + # raise IndexError.new if size == 0 + # unsafe_at(random.rand(size)) + # end + + # Returns a `Tuple` populated with the elements at the given indexes. + # Raises `IndexError` if any index is invalid. + # + # ``` + # ["a", "b", "c", "d"].values_at(0, 2) # => {"a", "c"} + # ``` + # TODO: Implement this + # def values_at(*indexes : Int) + # indexes.map { |index| self[index] } + # end + + def zip(other : Indexable) + each_with_index do |elem, i| + yield elem, other[i] + end + end + # TODO: Implement this + # def zip(other : Indexable(U)) forall U + # pairs = Array({T, U}).new(size) + # zip(other) { |x, y| pairs << {x, y} } + # pairs + # end + # TODO: Implement this + # def zip?(other : Indexable) + # each_with_index do |elem, i| + # yield elem, other[i]? + # end + # end + # TODO: Implement this + # def zip?(other : Indexable(U)) forall U + # pairs = Array({T, U?}).new(size) + # zip?(other) { |x, y| pairs << {x, y} } + # pairs + # end + # TODO: Implement this + # private def check_index_out_of_bounds(index) + # check_index_out_of_bounds(index) { raise IndexError.new } + # end + + private def check_index_out_of_bounds(index) + index += size if index < 0 + if 0 <= index < size + index + else + yield + end + end + + # :nodoc: + # TODO: Implement this + # def self.range_to_index_and_count(range, collection_size) + # start_index = range.begin + # start_index += collection_size if start_index < 0 + # raise IndexError.new if start_index < 0 + + # end_index = range.end + # end_index += collection_size if end_index < 0 + # end_index -= 1 if range.excludes_end? + # count = end_index - start_index + 1 + # count = 0 if count < 0 + + # {start_index, count} + # end + # TODO: Implement this + # private class ItemIterator(A, T) + # include Iterator(T) + + # def initialize(@array : A, @index = 0) + # end + + # def next + # if @index >= @array.size + # stop + # else + # value = @array[@index] + # @index += 1 + # value + # end + # end + + # def rewind + # @index = 0 + # self + # end + # end + # TODO: Implement this + # private class ReverseItemIterator(A, T) + # include Iterator(T) + + # def initialize(@array : A, @index : Int32 = array.size - 1) + # end + + # def next + # if @index < 0 + # stop + # else + # value = @array[@index] + # @index -= 1 + # value + # end + # end + + # def rewind + # @index = @array.size - 1 + # self + # end + # end + # TODO: Implement this + # private class IndexIterator(A) + # include Iterator(Int32) + + # def initialize(@array : A, @index = 0) + # end + + # def next + # if @index >= @array.size + # stop + # else + # value = @index + # @index += 1 + # value + # end + # end + + # def rewind + # @index = 0 + # self + # end + # end +end diff --git a/src/core/int.cr b/src/core/int.cr index 09399ed..c1cc58c 100644 --- a/src/core/int.cr +++ b/src/core/int.cr @@ -1,17 +1,103 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. +# Int is the base type of all integer types. # -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - +# There are four signed integer types: `Int8`, `Int16`, `Int32` and `Int64`, +# being able to represent numbers of 8, 16, 32 and 64 bits respectively. +# There are four unsigned integer types: `UInt8`, `UInt16`, `UInt32` and `UInt64`. +# +# An integer literal is an optional `+` or `-` sign, followed by +# a sequence of digits and underscores, optionally followed by a suffix. +# If no suffix is present, the literal's type is the lowest between `Int32`, `Int64` and `UInt64` +# in which the number fits: +# +# ``` +# 1 # Int32 +# +# 1_i8 # Int8 +# 1_i16 # Int16 +# 1_i32 # Int32 +# 1_i64 # Int64 +# +# 1_u8 # UInt8 +# 1_u16 # UInt16 +# 1_u32 # UInt32 +# 1_u64 # UInt64 +# +# +10 # Int32 +# -20 # Int32 +# +# 2147483648 # Int64 +# 9223372036854775808 # UInt64 +# ``` +# +# The underscore `_` before the suffix is optional. +# +# Underscores can be used to make some numbers more readable: +# +# ``` +# 1_000_000 # better than 1000000 +# ``` +# +# Binary numbers start with `0b`: +# +# ``` +# 0b1101 # == 13 +# ``` +# +# Octal numbers start with `0o`: +# +# ``` +# 0o123 # == 83 +# ``` +# +# Hexadecimal numbers start with `0x`: +# +# ``` +# 0xFE012D # == 16646445 +# 0xfe012d # == 16646445 +# ``` struct Int alias Signed = Int8 | Int16 | Int32 | Int64 alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 alias Primitive = Signed | Unsigned + # Returns a `Char` that has the unicode codepoint of `self`. + # + # Raises `ArgumentError` if this integer's value doesn't fit a char's range (`0..0x10ffff`). + # + # ``` + # 97.chr # => 'a' + # ``` + # def chr + # unless 0 <= self <= Char::MAX_CODEPOINT + # raise ArgumentError.new("#{self} out of char range") + # end + # unsafe_chr + # end + + def ~ + self ^ -1 + end + + # Divides `self` by *other* using floored division. + # + # In floored division, given two integers x and y: + # * q = x / y is rounded toward negative infinity + # * r = x % y has the sign of the second argument + # * x == q*y + r + # + # For example: + # + # ```text + # x y x / y x % y + # 5 3 1 2 + # -5 3 -2 1 + # 5 -3 -2 -1 + # -5 -3 1 -2 + # ``` + # + # Raises if *other* is zero, or if *other* is -1 and + # `self` is signed and is the minimum value for that + # integer type. def /(other : Int) check_div_argument other @@ -21,21 +107,62 @@ struct Int div end + # Divides `self` by *other* using truncated division. + # + # In truncated division, given two integers x and y: + # * `q = x.tdiv(y)` is rounded toward zero + # * `r = x.remainder(y)` has the sign of the first argument + # * `x == q*y + r` + # + # For example: + # + # ```text + # x y x / y x % y + # 5 3 1 2 + # -5 3 -1 -2 + # 5 -3 -1 2 + # -5 -3 1 -2 + # ``` + # + # Raises if *other* is `0`, or if *other* is `-1` and + # `self` is signed and is the minimum value for that + # integer type. + def tdiv(other : Int) + check_div_argument other + + unsafe_div other + end + private def check_div_argument(other) if other == 0 - self # raise DivisionByZero.new + self + # TODO: Implement this! + # raise DivisionByZero.new end {% begin %} if self < 0 && self == {{@type}}::MIN && other == -1 - self # raise ArgumentError.new "overflow: {{@type}}::MIN / -1" + self + # TODO: Implement this! + # raise ArgumentError.new "Overflow: {{@type}}::MIN / -1" end {% end %} end + # def fdiv(other) + # to_f / other + # end + + # Returns `self` modulo *other*. + # + # This uses floored division. + # + # See `Int#/` for more details. def %(other : Int) if other == 0 - self # raise DivisionByZero.new + self + # TODO: Implement this! + # raise DivisionByZero.new elsif (self ^ other) >= 0 self.unsafe_mod(other) else @@ -44,6 +171,35 @@ struct Int end end + # Returns `self` remainder *other*. + # + # This uses truncated division. + # + # See `Int#div` for more details. + def remainder(other : Int) + if other == 0 + self + # TODO: Implement this! + raise DivisionByZero.new + else + unsafe_mod other + end + end + + # Returns the result of shifting this number's bits *count* positions to the right. + # Also known as arithmetic right shift. + # + # * If *count* is greater than the number of bits of this integer, returns 0 + # * If *count* is negative, a left shift is performed + # + # ``` + # 8000 >> 1 # => 4000 + # 8000 >> 2 # => 2000 + # 8000 >> 32 # => 0 + # 8000 >> -1 # => 16000 + # + # -8000 >> 1 # => -4000 + # ``` def >>(count : Int) if count < 0 self << count.abs @@ -54,6 +210,17 @@ struct Int end end + # Returns the result of shifting this number's bits *count* positions to the left. + # + # * If *count* is greater than the number of bits of this integer, returns 0 + # * If *count* is negative, a right shift is performed + # + # ``` + # 8000 << 1 # => 16000 + # 8000 << 2 # => 32000 + # 8000 << 32 # => 0 + # 8000 << -1 # => 4000 + # ``` def <<(count : Int) if count < 0 self >> count.abs @@ -68,10 +235,107 @@ struct Int self >= 0 ? self : -self end + def ceil + self + end + + def floor + self + end + + def round + self + end + + def trunc + self + end + + # Returns the value of raising `self` to the power of *exponent*. + # + # Raises `ArgumentError` if *exponent* is negative: if this is needed, + # either use a float base or a float exponent. + # + # ``` + # 2 ** 3 # => 8 + # 2 ** 0 # => 1 + # 2 ** -1 # ArgumentError + # ``` + # def **(exponent : Int) : self + # if exponent < 0 + # raise ArgumentError.new "Cannot raise an integer to a negative integer power, use floats for that" + # end + + # result = self.class.new(1) + # k = self + # while exponent > 0 + # result *= k if exponent & 0b1 != 0 + # k *= k + # exponent = exponent.unsafe_shr(1) + # end + # result + # end + + # Returns the value of raising `self` to the power of *exponent*. + # + # ``` + # 2 ** 3.0 # => 8.0 + # 2 ** 0.0 # => 1.0 + # 2 ** -1.0 # => 0.5 + # ``` + # def **(exponent : Float) : Float64 + # to_f ** exponent + # end + def ===(char : Char) self === char.ord end + # Returns this number's *bit*th bit, starting with the least-significant. + # + # ``` + # 11.bit(0) # => 1 + # 11.bit(1) # => 1 + # 11.bit(2) # => 0 + # 11.bit(3) # => 1 + # 11.bit(4) # => 0 + # ``` + def bit(bit) + self >> bit & 1 + end + + def gcd(other : Int) + self == 0 ? other.abs : (other % self).gcd(self) + end + + def lcm(other : Int) + (self * other).abs / gcd(other) + end + + def divisible_by?(num) + self % num == 0 + end + + def even? + divisible_by? 2 + end + + def odd? + !even? + end + + def hash + self + end + + def succ + self + 1 + end + + def pred + self - 1 + end + def times(&block : self ->) : Nil i = self ^ self while i < self @@ -79,6 +343,224 @@ struct Int i += 1 end end + + # def times + # TimesIterator(typeof(self)).new(self) + # end + + def upto(to, &block : self ->) : Nil + x = self + while x <= to + yield x + x += 1 + end + end + + # def upto(to) + # UptoIterator(typeof(self), typeof(to)).new(self, to) + # end + + def downto(to, &block : self ->) : Nil + x = self + while x >= to + yield x + x -= 1 + end + end + + # def downto(to) + # DowntoIterator(typeof(self), typeof(to)).new(self, to) + # end + + def to(to, &block : self ->) : Nil + if self < to + upto(to) { |i| yield i } + elsif self > to + downto(to) { |i| yield i } + else + yield self + end + end + + def to(to) + self <= to ? upto(to) : downto(to) + end + + def modulo(other) + self % other + end + + private DIGITS_DOWNCASE = "0123456789abcdefghijklmnopqrstuvwxyz" + private DIGITS_UPCASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + private DIGITS_BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + + # def to_s + # to_s(10) + # end + + # def to_s(io : IO) + # to_s(10, io) + # end + + # def to_s(base : Int, upcase : Bool = false) + # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 + # raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 + + # case self + # when 0 + # return "0" + # when 1 + # return "1" + # end + + # internal_to_s(base, upcase) do |ptr, count| + # String.new(ptr, count, count) + # end + # end + + # def to_s(base : Int, io : IO, upcase : Bool = false) + # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 + # raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 + + # case self + # when 0 + # io << '0' + # return + # when 1 + # io << '1' + # return + # end + + # internal_to_s(base, upcase) do |ptr, count| + # io.write_utf8 Slice.new(ptr, count) + # end + # end + + # private def internal_to_s(base, upcase = false) + # chars = uninitialized UInt8[65] + # ptr_end = chars.to_unsafe + 64 + # ptr = ptr_end + # num = self + + # neg = num < 0 + + # digits = (base == 62 ? DIGITS_BASE62 : (upcase ? DIGITS_UPCASE : DIGITS_DOWNCASE)).to_unsafe + + # while num != 0 + # ptr -= 1 + # ptr.value = digits[num.remainder(base).abs] + # num = num.tdiv(base) + # end + + # if neg + # ptr -= 1 + # ptr.value = '-'.ord.to_u8 + # end + + # count = (ptr_end - ptr).to_i32 + # yield ptr, count + # end + + # Writes this integer to the given *io* in the given *format*. + # + # See also: `IO#write_bytes`. + # def to_io(io : IO, format : IO::ByteFormat) + # format.encode(self, io) + # end + + # Reads an integer from the given *io* in the given *format*. + # + # See also: `IO#read_bytes`. + # def self.from_io(io : IO, format : IO::ByteFormat) : self + # format.decode(self, io) + # end + + # Counts `1`-bits in the binary representation of this integer. + # + # ``` + # 5.popcount # => 2 + # -15.popcount # => 29 + # ``` + # abstract def popcount + + # private class TimesIterator(T) + # include Iterator(T) + + # @n : T + # @index : Int32 + + # def initialize(@n : T, @index = 0) + # end + + # def next + # if @index < @n + # value = @index + # @index += 1 + # value + # else + # stop + # end + # end + + # def rewind + # @index = 0 + # self + # end + # end + + # private class UptoIterator(T, N) + # include Iterator(T) + + # @from : T + # @to : N + # @current : T + + # def initialize(@from : T, @to : N) + # @current = @from + # end + + # def next + # if @current > @to + # stop + # else + # value = @current + # @current += 1 + # value + # end + # end + + # def rewind + # @current = @from + # self + # end + # end + + # private class DowntoIterator(T, N) + # include Iterator(T) + + # @from : T + # @to : N + # @current : T + + # def initialize(@from : T, @to : N) + # @current = @from + # end + + # def next + # if @current < @to + # stop + # else + # value = @current + # @current -= 1 + # value + # end + # end + + # def rewind + # @current = @from + # self + # end + # end end struct Int8 diff --git a/src/core/int.cr.sample b/src/core/int.cr.sample deleted file mode 100644 index b22285c..0000000 --- a/src/core/int.cr.sample +++ /dev/null @@ -1,734 +0,0 @@ -# Taken from Crystal version 0.21.0 - -# Int is the base type of all integer types. -# -# There are four signed integer types: `Int8`, `Int16`, `Int32` and `Int64`, -# being able to represent numbers of 8, 16, 32 and 64 bits respectively. -# There are four unsigned integer types: `UInt8`, `UInt16`, `UInt32` and `UInt64`. -# -# An integer literal is an optional `+` or `-` sign, followed by -# a sequence of digits and underscores, optionally followed by a suffix. -# If no suffix is present, the literal's type is the lowest between `Int32`, `Int64` and `UInt64` -# in which the number fits: -# -# ``` -# 1 # Int32 -# -# 1_i8 # Int8 -# 1_i16 # Int16 -# 1_i32 # Int32 -# 1_i64 # Int64 -# -# 1_u8 # UInt8 -# 1_u16 # UInt16 -# 1_u32 # UInt32 -# 1_u64 # UInt64 -# -# +10 # Int32 -# -20 # Int32 -# -# 2147483648 # Int64 -# 9223372036854775808 # UInt64 -# ``` -# -# The underscore `_` before the suffix is optional. -# -# Underscores can be used to make some numbers more readable: -# -# ``` -# 1_000_000 # better than 1000000 -# ``` -# -# Binary numbers start with `0b`: -# -# ``` -# 0b1101 # == 13 -# ``` -# -# Octal numbers start with `0o`: -# -# ``` -# 0o123 # == 83 -# ``` -# -# Hexadecimal numbers start with `0x`: -# -# ``` -# 0xFE012D # == 16646445 -# 0xfe012d # == 16646445 -# ``` -struct Int - alias Signed = Int8 | Int16 | Int32 | Int64 - alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 - alias Primitive = Signed | Unsigned - - # Returns a `Char` that has the unicode codepoint of `self`. - # - # Raises `ArgumentError` if this integer's value doesn't fit a char's range (`0..0x10ffff`). - # - # ``` - # 97.chr # => 'a' - # ``` - def chr - unless 0 <= self <= Char::MAX_CODEPOINT - raise ArgumentError.new("#{self} out of char range") - end - unsafe_chr - end - - def ~ - self ^ -1 - end - - # Divides `self` by *other* using floored division. - # - # In floored division, given two integers x and y: - # * q = x / y is rounded toward negative infinity - # * r = x % y has the sign of the second argument - # * x == q*y + r - # - # For example: - # - # ```text - # x y x / y x % y - # 5 3 1 2 - # -5 3 -2 1 - # 5 -3 -2 -1 - # -5 -3 1 -2 - # ``` - # - # Raises if *other* is zero, or if *other* is -1 and - # `self` is signed and is the minimum value for that - # integer type. - def /(other : Int) - check_div_argument other - - div = unsafe_div other - mod = unsafe_mod other - div -= 1 if other > 0 ? mod < 0 : mod > 0 - div - end - - # Divides `self` by *other* using truncated division. - # - # In truncated division, given two integers x and y: - # * `q = x.tdiv(y)` is rounded toward zero - # * `r = x.remainder(y)` has the sign of the first argument - # * `x == q*y + r` - # - # For example: - # - # ```text - # x y x / y x % y - # 5 3 1 2 - # -5 3 -1 -2 - # 5 -3 -1 2 - # -5 -3 1 -2 - # ``` - # - # Raises if *other* is `0`, or if *other* is `-1` and - # `self` is signed and is the minimum value for that - # integer type. - def tdiv(other : Int) - check_div_argument other - - unsafe_div other - end - - private def check_div_argument(other) - if other == 0 - raise DivisionByZero.new - end - - {% begin %} - if self < 0 && self == {{@type}}::MIN && other == -1 - raise ArgumentError.new "Overflow: {{@type}}::MIN / -1" - end - {% end %} - end - - def fdiv(other) - to_f / other - end - - # Returns `self` modulo *other*. - # - # This uses floored division. - # - # See `Int#/` for more details. - def %(other : Int) - if other == 0 - raise DivisionByZero.new - elsif (self ^ other) >= 0 - self.unsafe_mod(other) - else - me = self.unsafe_mod(other) - me == 0 ? me : me + other - end - end - - # Returns `self` remainder *other*. - # - # This uses truncated division. - # - # See `Int#div` for more details. - def remainder(other : Int) - if other == 0 - raise DivisionByZero.new - else - unsafe_mod other - end - end - - # Returns the result of shifting this number's bits *count* positions to the right. - # Also known as arithmetic right shift. - # - # * If *count* is greater than the number of bits of this integer, returns 0 - # * If *count* is negative, a left shift is performed - # - # ``` - # 8000 >> 1 # => 4000 - # 8000 >> 2 # => 2000 - # 8000 >> 32 # => 0 - # 8000 >> -1 # => 16000 - # - # -8000 >> 1 # => -4000 - # ``` - def >>(count : Int) - if count < 0 - self << count.abs - elsif count < sizeof(self) * 8 - self.unsafe_shr(count) - else - self.class.zero - end - end - - # Returns the result of shifting this number's bits *count* positions to the left. - # - # * If *count* is greater than the number of bits of this integer, returns 0 - # * If *count* is negative, a right shift is performed - # - # ``` - # 8000 << 1 # => 16000 - # 8000 << 2 # => 32000 - # 8000 << 32 # => 0 - # 8000 << -1 # => 4000 - # ``` - def <<(count : Int) - if count < 0 - self >> count.abs - elsif count < sizeof(self) * 8 - self.unsafe_shl(count) - else - self.class.zero - end - end - - def abs - self >= 0 ? self : -self - end - - def ceil - self - end - - def floor - self - end - - def round - self - end - - def trunc - self - end - - # Returns the value of raising `self` to the power of *exponent*. - # - # Raises `ArgumentError` if *exponent* is negative: if this is needed, - # either use a float base or a float exponent. - # - # ``` - # 2 ** 3 # => 8 - # 2 ** 0 # => 1 - # 2 ** -1 # ArgumentError - # ``` - def **(exponent : Int) : self - if exponent < 0 - raise ArgumentError.new "Cannot raise an integer to a negative integer power, use floats for that" - end - - result = self.class.new(1) - k = self - while exponent > 0 - result *= k if exponent & 0b1 != 0 - k *= k - exponent = exponent.unsafe_shr(1) - end - result - end - - # Returns the value of raising `self` to the power of *exponent*. - # - # ``` - # 2 ** 3.0 # => 8.0 - # 2 ** 0.0 # => 1.0 - # 2 ** -1.0 # => 0.5 - # ``` - def **(exponent : Float) : Float64 - to_f ** exponent - end - - def ===(char : Char) - self === char.ord - end - - # Returns this number's *bit*th bit, starting with the least-significant. - # - # ``` - # 11.bit(0) # => 1 - # 11.bit(1) # => 1 - # 11.bit(2) # => 0 - # 11.bit(3) # => 1 - # 11.bit(4) # => 0 - # ``` - def bit(bit) - self >> bit & 1 - end - - def gcd(other : Int) - self == 0 ? other.abs : (other % self).gcd(self) - end - - def lcm(other : Int) - (self * other).abs / gcd(other) - end - - def divisible_by?(num) - self % num == 0 - end - - def even? - divisible_by? 2 - end - - def odd? - !even? - end - - def hash - self - end - - def succ - self + 1 - end - - def pred - self - 1 - end - - def times(&block : self ->) : Nil - i = self ^ self - while i < self - yield i - i += 1 - end - end - - def times - TimesIterator(typeof(self)).new(self) - end - - def upto(to, &block : self ->) : Nil - x = self - while x <= to - yield x - x += 1 - end - end - - def upto(to) - UptoIterator(typeof(self), typeof(to)).new(self, to) - end - - def downto(to, &block : self ->) : Nil - x = self - while x >= to - yield x - x -= 1 - end - end - - def downto(to) - DowntoIterator(typeof(self), typeof(to)).new(self, to) - end - - def to(to, &block : self ->) : Nil - if self < to - upto(to) { |i| yield i } - elsif self > to - downto(to) { |i| yield i } - else - yield self - end - end - - def to(to) - self <= to ? upto(to) : downto(to) - end - - def modulo(other) - self % other - end - - private DIGITS_DOWNCASE = "0123456789abcdefghijklmnopqrstuvwxyz" - private DIGITS_UPCASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - private DIGITS_BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - - def to_s - to_s(10) - end - - def to_s(io : IO) - to_s(10, io) - end - - def to_s(base : Int, upcase : Bool = false) - raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 - raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 - - case self - when 0 - return "0" - when 1 - return "1" - end - - internal_to_s(base, upcase) do |ptr, count| - String.new(ptr, count, count) - end - end - - def to_s(base : Int, io : IO, upcase : Bool = false) - raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 - raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 - - case self - when 0 - io << '0' - return - when 1 - io << '1' - return - end - - internal_to_s(base, upcase) do |ptr, count| - io.write_utf8 Slice.new(ptr, count) - end - end - - private def internal_to_s(base, upcase = false) - chars = uninitialized UInt8[65] - ptr_end = chars.to_unsafe + 64 - ptr = ptr_end - num = self - - neg = num < 0 - - digits = (base == 62 ? DIGITS_BASE62 : (upcase ? DIGITS_UPCASE : DIGITS_DOWNCASE)).to_unsafe - - while num != 0 - ptr -= 1 - ptr.value = digits[num.remainder(base).abs] - num = num.tdiv(base) - end - - if neg - ptr -= 1 - ptr.value = '-'.ord.to_u8 - end - - count = (ptr_end - ptr).to_i32 - yield ptr, count - end - - # Writes this integer to the given *io* in the given *format*. - # - # See also: `IO#write_bytes`. - def to_io(io : IO, format : IO::ByteFormat) - format.encode(self, io) - end - - # Reads an integer from the given *io* in the given *format*. - # - # See also: `IO#read_bytes`. - def self.from_io(io : IO, format : IO::ByteFormat) - format.decode(self, io) - end - - # Counts `1`-bits in the binary representation of this integer. - # - # ``` - # 5.popcount # => 2 - # -15.popcount # => 29 - # ``` - abstract def popcount - - private class TimesIterator(T) - include Iterator(T) - - @n : T - @index : Int32 - - def initialize(@n : T, @index = 0) - end - - def next - if @index < @n - value = @index - @index += 1 - value - else - stop - end - end - - def rewind - @index = 0 - self - end - end - - private class UptoIterator(T, N) - include Iterator(T) - - @from : T - @to : N - @current : T - - def initialize(@from : T, @to : N) - @current = @from - end - - def next - if @current > @to - stop - else - value = @current - @current += 1 - value - end - end - - def rewind - @current = @from - self - end - end - - private class DowntoIterator(T, N) - include Iterator(T) - - @from : T - @to : N - @current : T - - def initialize(@from : T, @to : N) - @current = @from - end - - def next - if @current < @to - stop - else - value = @current - @current -= 1 - value - end - end - - def rewind - @current = @from - self - end - end -end - -struct Int8 - MIN = -128_i8 - MAX = 127_i8 - - # Returns an `Int8` by invoking `to_i8` on *value*. - def self.new(value) - value.to_i8 - end - - def - - 0_i8 - self - end - - def popcount - Intrinsics.popcount8(self) - end - - def clone - self - end -end - -struct Int16 - MIN = -32768_i16 - MAX = 32767_i16 - - # Returns an `Int16` by invoking `to_i16` on *value*. - def self.new(value) - value.to_i16 - end - - def - - 0_i16 - self - end - - def popcount - Intrinsics.popcount16(self) - end - - def clone - self - end -end - -struct Int32 - MIN = -2147483648_i32 - MAX = 2147483647_i32 - - # Returns an `Int32` by invoking `to_i32` on *value*. - def self.new(value) - value.to_i32 - end - - def - - 0 - self - end - - def popcount - Intrinsics.popcount32(self) - end - - def clone - self - end -end - -struct Int64 - MIN = -9223372036854775808_i64 - MAX = 9223372036854775807_i64 - - # Returns an `Int64` by invoking `to_i64` on *value*. - def self.new(value) - value.to_i64 - end - - def - - 0_i64 - self - end - - def popcount - Intrinsics.popcount64(self) - end - - def clone - self - end -end - -struct UInt8 - MIN = 0_u8 - MAX = 255_u8 - - # Returns an `UInt8` by invoking `to_u8` on *value*. - def self.new(value) - value.to_u8 - end - - def abs - self - end - - def popcount - Intrinsics.popcount8(self) - end - - def clone - self - end -end - -struct UInt16 - MIN = 0_u16 - MAX = 65535_u16 - - # Returns an `UInt16` by invoking `to_u16` on *value*. - def self.new(value) - value.to_u16 - end - - def abs - self - end - - def popcount - Intrinsics.popcount16(self) - end - - def clone - self - end -end - -struct UInt32 - MIN = 0_u32 - MAX = 4294967295_u32 - - # Returns an `UInt32` by invoking `to_u32` on *value*. - def self.new(value) - value.to_u32 - end - - def abs - self - end - - def popcount - Intrinsics.popcount32(self) - end - - def clone - self - end -end - -struct UInt64 - MIN = 0_u64 - MAX = 18446744073709551615_u64 - - # Returns an `UInt64` by invoking `to_u64` on *value*. - def self.new(value) - value.to_u64 - end - - def abs - self - end - - def popcount - Intrinsics.popcount64(self) - end - - def clone - self - end -end diff --git a/src/core/iterable.cr b/src/core/iterable.cr new file mode 100644 index 0000000..765eb0a --- /dev/null +++ b/src/core/iterable.cr @@ -0,0 +1,48 @@ +# The `Iterable` mixin provides convenience methods to collection classes +# that provide an `each` method that returns an `Iterator` over the collection. +module Iterable(T) + # Must return an `Iterator` over the elements in this collection. + abstract def each + + # Same as `each.cycle`. + def cycle + each.cycle + end + + # Same as `each.cycle(n)`. + def cycle(n) + each.cycle(n) + end + + # Returns an Iterator that enumerates over the items, chunking them together + # based on the return value of the block. + # + # ``` + # (0..7).chunk(&./(3)).to_a # => [{0, [0, 1, 2]}, {1, [3, 4, 5]}, {2, [6, 7]}] + # ``` + # + # See also: `Iterator#chunks`. + def chunk(reuse = false, &block : T -> U) forall U + each.chunk reuse, &block + end + + # Same as `each.slice(count, reuse)`. + def each_slice(count : Int, reuse = false) + each.slice(count, reuse) + end + + # Same as `each.cons(count)`. + def each_cons(count : Int, reuse = false) + each.cons(count, reuse) + end + + # Same as `each.with_index(offset)`. + def each_with_index(offset = 0) + each.with_index(offset) + end + + # Same as `each.with_object(obj)`. + def each_with_object(obj) + each.with_object(obj) + end +end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr b/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr new file mode 100644 index 0000000..1fd2bd6 --- /dev/null +++ b/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr @@ -0,0 +1,10 @@ +lib LibCR + alias Int8T = SChar + alias Int16T = Short + alias Int32T = Int + alias Int64T = Long + alias UInt8T = Char + alias UInt16T = UShort + alias UInt32T = UInt + alias UInt64T = ULong +end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr b/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr new file mode 100644 index 0000000..8b9d046 --- /dev/null +++ b/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr @@ -0,0 +1,61 @@ +require "../stddef" +require "../stdint" + +lib LibCR + alias BlkcntT = Long + alias BlksizeT = Long + alias ClockT = Long + alias ClockidT = Int + alias DevT = ULong + alias GidT = UInt + alias IdT = UInt + alias InoT = ULong + alias ModeT = UInt + alias NlinkT = ULong + alias OffT = Long + alias PidT = Int + + union PthreadAttrTU + __i : StaticArray(Int, 14) + __vi : StaticArray(Int, 14) + __s : StaticArray(ULong, 7) + end + + struct PthreadAttrT + __u : PthreadAttrTU + end + + union PthreadCondTU + __i : StaticArray(Int, 12) + __vi : StaticArray(Int, 12) + __p : StaticArray(Void*, 6) + end + + struct PthreadCondT + __u : PthreadCondTU + end + + struct PthreadCondattrT + __attr : UInt + end + + union PthreadMutexTU + __i : StaticArray(Int, 10) + __vi : StaticArray(Int, 10) + __p : StaticArray(Void*, 5) + end + + struct PthreadMutexT + __u : PthreadMutexTU + end + + struct PthreadMutexattrT + __attr : UInt + end + + type PthreadT = Void* + alias SSizeT = Long + alias SusecondsT = Long + alias TimeT = Long + alias UidT = UInt +end diff --git a/src/core/macros.cr b/src/core/macros.cr new file mode 100644 index 0000000..4d8c5a0 --- /dev/null +++ b/src/core/macros.cr @@ -0,0 +1,130 @@ +# Defines a **`Struct`** with the given name and properties. +# +# The generated struct has a constructor with the given properties +# in the same order as declared. The struct only provides getters, +# not setters, making it immutable by default. +# +# The properties can be type declarations or assignments. +# +# You can pass a block to this macro, that will be inserted inside +# the struct definition. +# +# ``` +# record Point, x : Int32, y : Int32 +# +# Point.new 1, 2 # => # +# ``` +# +# An example with the block version: +# +# ``` +# record Person, first_name : String, last_name : String do +# def full_name +# "#{first_name} #{last_name}" +# end +# end +# +# person = Person.new "John", "Doe" +# person.full_name # => "John Doe" +# ``` +# +# An example with type declarations and default values: +# +# ``` +# record Point, x : Int32 = 0, y : Int32 = 0 +# +# Point.new # => # +# Point.new y: 2 # => # +# ``` +# +# An example with assignments (in this case the compiler must be able to +# infer the types from the default values): +# +# ``` +# record Point, x = 0, y = 0 +# +# Point.new # => # +# Point.new y: 2 # => # +# ``` +macro record(name, *properties) + struct {{name.id}} + {% for property in properties %} + {% if property.is_a?(Assign) %} + getter {{property.target.id}} + {% elsif property.is_a?(TypeDeclaration) %} + getter {{property.var}} : {{property.type}} + {% else %} + getter :{{property.id}} + {% end %} + {% end %} + + def initialize({{ + *properties.map do |field| + "@#{field.id}".id + end + }}) + end + + {{yield}} + + def clone + {{name.id}}.new({{ + *properties.map do |property| + if property.is_a?(Assign) + "@#{property.target.id}.clone".id + elsif property.is_a?(TypeDeclaration) + "@#{property.var.id}.clone".id + else + "@#{property.id}.clone".id + end + end + }}) + end + end +end + +# Prints a series of expressions together with their values. +# Useful for print style debugging. +# +# ``` +# a = 1 +# pp a # => "a # => 1" +# +# pp [1, 2, 3].map(&.to_s) # => "[1, 2, 3].map(&.to_s) # => ["1", "2", "3"]" +# ``` +# macro pp(*exps) +# {% if exps.size == 0 %} +# # Nothing +# {% elsif exps.size == 1 %} +# {% exp = exps.first %} +# %prefix = "#{{{ exp.stringify }}} # => " +# ::print %prefix +# %object = {{exp}} +# PrettyPrint.format(%object, STDOUT, width: 80 - %prefix.size, indent: %prefix.size) +# ::puts +# %object +# {% else %} +# %names = { {{*exps.map(&.stringify)}} } +# %max_size = %names.max_of &.size +# { +# {% for exp, i in exps %} +# begin +# %prefix = "#{%names[{{i}}].ljust(%max_size)} # => " +# ::print %prefix +# %object = {{exp}} +# PrettyPrint.format(%object, STDOUT, width: 80 - %prefix.size, indent: %prefix.size) +# ::puts +# %object +# end, +# {% end %} +# } +# {% end %} +# end + +# macro assert_responds_to(var, method) +# if {{var}}.responds_to?(:{{method}}) +# {{var}} +# else +# raise "Expected {{var}} to respond to :{{method}}, not #{ {{var}} }" +# end +# end diff --git a/src/core/named_tuple.cr b/src/core/named_tuple.cr new file mode 100644 index 0000000..cd7468b --- /dev/null +++ b/src/core/named_tuple.cr @@ -0,0 +1,490 @@ +# A named tuple is a fixed-size, immutable, stack-allocated mapping +# of a fixed set of keys to values. +# +# You can think of a `NamedTuple` as an immutable `Hash` whose keys (which +# are of type `Symbol`), and the types for each key, are known at compile time. +# +# A named tuple can be created with a named tuple literal: +# +# ``` +# language = {name: "Crystal", year: 2011} # NamedTuple(name: String, year: Int32) +# +# language[:name] # => "Crystal" +# language[:year] # => 2011 +# language[:other] # compile time error +# ``` +# +# The compiler knows what types are in each key, so when indexing a named tuple +# with a symbol literal the compiler will return the value for that key and +# with the expected type, like in the above snippet. Indexing with a symbol +# literal for which there's no key will give a compile-time error. +# +# Indexing with a symbol that is only known at runtime will return +# a value whose type is the union of all the types in the named tuple, +# and might raise `KeyError`. +struct NamedTuple + # Creates a named tuple that will contain the given arguments. + # + # This method is useful in macros and generic code because with it you can + # creates empty named tuples, something that you can't do with a tuple literal. + # + # ``` + # NamedTuple.new(name: "Crystal", year: 2011) #=> {name: "Crystal", year: 2011} + # NamedTuple.new # => {} + # {} # syntax error + # ``` + def self.new(**options : **T) + options + end + + # Creates a named tuple from the given hash, with elements casted to the given types. + # Here the Int32 | String union is cast to Int32. + # + # ``` + # num_or_str = 42.as(Int32 | String) + # NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # => {name: "number", val: 42} + # + # num_or_str = "a string".as(Int32 | String) + # NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # raises TypeCastError (cast from String to Int32 failed) + # ``` + # See also: `#from`. + def self.from(hash : Hash) : self + {% begin %} + NamedTuple.new(**{{T}}).from(hash) + {% end %} + end + + # Expects to be called on a named tuple whose values are types, creates a tuple from the given hash, + # with types casted appropriately. The hash keys must be either symbols or strings. + # + # This allows you to easily pass a hash as individual named arguments to a method. + # + # ``` + # require "json" + # + # def speak_about(thing : String, n : Int64) + # "I see #{n} #{thing}s" + # end + # + # data = JSON.parse(%({"thing": "world", "n": 2})).as_h + # speak_about(**{thing: String, n: Int64}.from(data)) # => "I see 2 worlds" + # ``` + def from(hash : Hash) + if size != hash.size + raise ArgumentError.new("Expected a hash with #{size} keys but one with #{hash.size} keys was given.") + end + + {% begin %} + NamedTuple.new( + {% for key, value in T %} + {{key.stringify}}: self[{{key.symbolize}}].cast(hash.fetch({{key.symbolize}}) { hash["{{key}}"] }), + {% end %} + ) + {% end %} + end + + # Returns the value for the given *key*, if there's such key, otherwise raises `KeyError`. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # + # key = :name + # tuple[key] # => "Crystal" + # + # key = "year" + # tuple[key] # => 2011 + # + # key = :other + # tuple[key] # raises KeyError + # ``` + def [](key : Symbol | String) + fetch(key) { raise KeyError.new "Missing named tuple key: #{key.inspect}" } + end + + # Returns the value for the given *key*, if there's such key, otherwise returns `nil`. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # + # key = :name + # tuple[key]? # => "Crystal" + # + # key = "year" + # tuple[key] # => 2011 + # + # key = :other + # tuple[key]? # => nil + # ``` + def []?(key : Symbol | String) + fetch(key, nil) + end + + # Returns the value for the given *key*, if there's such key, otherwise returns *default_value*. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.fetch(:name, "Unknown") # => "Crystal" + # tuple.fetch("year", 0) # => 2011 + # tuple.fetch(:other, 0) # => 0 + # ``` + def fetch(key : Symbol | String, default_value) + fetch(key) { default_value } + end + + # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.fetch(:name) { "Unknown" } # => "Crystal" + # tuple.fetch(:other) { 0 } # => 0 + # ``` + def fetch(key : Symbol, &block) + {% for key in T %} + return self[{{key.symbolize}}] if {{key.symbolize}} == key + {% end %} + yield + end + + # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.fetch("name") { "Unknown" } # => "Crystal" + # tuple.fetch("other") { 0 } # => 0 + # ``` + def fetch(key : String, &block) + {% for key in T %} + return self[{{key.symbolize}}] if {{key.stringify}} == key + {% end %} + yield + end + + # Returns a hash value based on this name tuple's size, keys and values. + # + # See also: `Object#hash`. + def hash + hash = 31 * size + {% for key in T.keys.sort %} + hash = 31 * hash + {{key.symbolize}}.hash + hash = 31 * hash + self[{{key.symbolize}}].hash + {% end %} + hash + end + + # Same as `to_s`. + def inspect + to_s + end + + # Returns a `Tuple` of symbols with the keys in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.keys # => {:name, :year} + # ``` + def keys + {% begin %} + Tuple.new( + {% for key in T %} + {{key.symbolize}}, + {% end %} + ) + {% end %} + end + + # Returns a `Tuple` with the values in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.values # => {"Crystal", 2011} + # ``` + def values + {% begin %} + Tuple.new( + {% for key in T %} + self[{{key.symbolize}}], + {% end %} + ) + {% end %} + end + + # Returns `true` if this named tuple has the given *key*, `false` otherwise. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.has_key?(:name) # => true + # tuple.has_key?(:other) # => false + # ``` + def has_key?(key : Symbol) : Bool + {% for key in T %} + return true if {{key.symbolize}} == key + {% end %} + false + end + + # ditto + def has_key?(key : String) : Bool + {% for key in T %} + return true if {{key.stringify}} == key + {% end %} + false + end + + # Appends a string representation of this named tuple to the given `IO`. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.to_s # => %({name: "Crystal", year: 2011}) + # ``` + def to_s(io) + io << "{" + {% for key, value, i in T %} + {% if i > 0 %} + io << ", " + {% end %} + key = {{key.stringify}} + if Symbol.needs_quotes?(key) + key.inspect(io) + else + io << key + end + io << ": " + self[{{key.symbolize}}].inspect(io) + {% end %} + io << "}" + end + + def pretty_print(pp) + pp.surround("{", "}", left_break: nil, right_break: nil) do + {% for key, value, i in T %} + {% if i > 0 %} + pp.comma + {% end %} + pp.group do + key = {{key.stringify}} + if Symbol.needs_quotes?(key) + pp.text key.inspect + else + pp.text key + end + pp.text ": " + pp.nest do + pp.breakable "" + self[{{key.symbolize}}].pretty_print(pp) + end + end + {% end %} + end + end + + # Yields each key and value in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.each do |key, value| + # puts "#{key} = #{value}" + # end + # ``` + # + # Output: + # + # ```text + # name = Crystal + # year = 2011 + # ``` + def each : Nil + {% for key in T %} + yield {{key.symbolize}}, self[{{key.symbolize}}] + {% end %} + end + + # Yields each key in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.each_key do |key| + # puts key + # end + # ``` + # + # Output: + # + # ```text + # name + # year + # ``` + def each_key : Nil + {% for key in T %} + yield {{key.symbolize}} + {% end %} + end + + # Yields each value in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.each_value do |value| + # puts value + # end + # ``` + # + # Output: + # + # ```text + # Crystal + # 2011 + # ``` + def each_value : Nil + {% for key in T %} + yield self[{{key.symbolize}}] + {% end %} + end + + # Yields each key and value, together with an index starting at *offset*, in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.each_with_index do |key, value, i| + # puts "#{i + 1}) #{key} = #{value}" + # end + # ``` + # + # Output: + # + # ```text + # 1) name = Crystal + # 2) year = 2011 + # ``` + def each_with_index(offset = 0) + i = offset + each do |key, value| + yield key, value, i + i += 1 + end + end + + # Returns an `Array` populated with the results of each iteration in the given block, + # which is given each key and value in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.map { |k, v| "#{k}: #{v}" } # => ["name: Crystal", "year: 2011"] + # ``` + def map + array = Array(typeof(yield first_key_internal, first_value_internal)).new(size) + each do |k, v| + array.push yield k, v + end + array + end + + # Returns a new `Array` of tuples populated with each key-value pair. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.to_a # => [{:name, "Crystal"}, {:year, 2011}] + # ``` + def to_a + ary = Array({typeof(first_key_internal), typeof(first_value_internal)}).new(size) + each do |key, value| + ary << {key.as(typeof(first_key_internal)), value.as(typeof(first_value_internal))} + end + ary + end + + # Returns a `Hash` with the keys and values in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.to_h # => {:name => "Crystal", :year => 2011} + # ``` + def to_h + {% if T.size == 0 %} + {% raise "Can't convert an empty NamedTuple to a Hash" %} + {% else %} + { + {% for key in T %} + {{key.symbolize}} => self[{{key.symbolize}}], + {% end %} + } + {% end %} + end + + # Returns the number of elements in this named tuple. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.size # => 2 + # ``` + def size + {{T.size}} + end + + # Returns `true` if this named tuple is empty. + # + # ``` + # tuple = {name: "Crystal", year: 2011} + # tuple.empty? # => false + # ``` + def empty? + size == 0 + end + + # Returns `true` if this tuple has the same keys as *other*, and values + # for each key are the same in `self` and *other*. + # + # ``` + # tuple1 = {name: "Crystal", year: 2011} + # tuple2 = {year: 2011, name: "Crystal"} + # tuple3 = {name: "Crystal", year: 2012} + # tuple4 = {name: "Crystal", year: 2011.0} + # + # tuple1 == tuple2 # => true + # tuple1 == tuple3 # => false + # tuple1 == tuple4 # => true + # ``` + def ==(other : self) + {% for key in T %} + return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}] + {% end %} + true + end + + # ditto + def ==(other : NamedTuple) + compare_with_other_named_tuple(other) + end + + private def compare_with_other_named_tuple(other : U) forall U + {% if T.keys.sort == U.keys.sort %} + {% for key in T %} + return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}] + {% end %} + + true + {% else %} + false + {% end %} + end + + # Returns a named tuple with the same keys but with cloned values, using the `clone` method. + def clone + {% begin %} + { + {% for key in T %} + {{key.stringify}}: self[{{key.symbolize}}].clone, + {% end %} + } + {% end %} + end + + private def first_key_internal + i = 0 + keys[i] + end + + private def first_value_internal + i = 0 + values[i] + end +end diff --git a/src/core/nil.cr b/src/core/nil.cr new file mode 100644 index 0000000..d552a7f --- /dev/null +++ b/src/core/nil.cr @@ -0,0 +1,116 @@ +# The `Nil` type has only one possible value: `nil`. +# +# `nil` is commonly used to represent the absence of a value. +# For example, `String#index` returns the position of the character or `nil` if it's not +# in the string: +# +# ``` +# str = "Hello world" +# str.index 'e' # => 1 +# str.index 'a' # => nil +# ``` +# +# In the above example, trying to invoke a method on the returned value will +# give a compile time error unless both `Int32` and `Nil` define that method: +# +# ``` +# str = "Hello world" +# idx = str.index 'e' +# idx + 1 # Error: undefined method '+' for Nil +# ``` +# +# The language and the standard library provide short, readable, easy ways to deal with `nil`, +# such as `Object#try` and `Object#not_nil!`: +# +# ``` +# str = "Hello world" +# +# # The index of 'e' in str or 0 if not found +# idx1 = str.index('e') || 0 +# +# idx2 = str.index('a') +# if idx2 +# # Compiles: idx2 can't be nil here +# idx2 + 1 +# end +# +# # Tell the compiler that we are sure the returned +# # value is not nil: raises a runtime exception +# # if our assumption doesn't hold. +# idx3 = str.index('o').not_nil! +# ``` +struct Nil + # Returns `0_u64`. Even though `Nil` is not a `Reference` type, it is usually + # mixed with them to form nilable types so it's useful to have an + # object id for `nil`. + def object_id + 0_u64 + end + + # :nodoc: + def crystal_type_id + 0 + end + + # Returns `true`: `Nil` has only one singleton value: `nil`. + def ==(other : Nil) + true + end + + # Returns `true`: `Nil` has only one singleton value: `nil`. + def same?(other : Nil) + true + end + + # Returns `false`. + # TODO: Implement this! + # def same?(other : Reference) + # false + # end + + # Returns `0`. + def hash + 0 + end + + # Returns an empty string. + def to_s + "" + end + + # Doesn't write anything to the given `IO`. + # TODO: Implement this! + # def to_s(io : IO) + # # Nothing to do + # end + + # Returns `"nil"`. + def inspect + "nil" + end + + # Writes `"nil"` to the given `IO`. + # TODO: Implement this! + # def inspect(io) + # io << "nil" + # end + + # Doesn't yield to the block. + # + # See also: `Object#try`. + def try(&block) + self + end + + # Raises an exception. + # + # See also: `Object#not_nil!`. + # TODO: Implement this! + # def not_nil! + # raise "Nil assertion failed" + # end + + def clone + self + end +end diff --git a/src/core/number.cr b/src/core/number.cr index a434814..2ff35e3 100644 --- a/src/core/number.cr +++ b/src/core/number.cr @@ -1,7 +1,8 @@ # The top-level number type. struct Number + # TODO: Implement this! # include Comparable(Number) - + # TODO: Implement this! # alias Primitive = Int::Primitive | Float::Primitive alias Primitive = Int::Primitive diff --git a/src/core/number.cr.sample b/src/core/number.cr.sample deleted file mode 100644 index 7644b07..0000000 --- a/src/core/number.cr.sample +++ /dev/null @@ -1,280 +0,0 @@ -# Taken from Crystal version 0.21.0 - -# The top-level number type. -struct Number - include Comparable(Number) - - alias Primitive = Int::Primitive | Float::Primitive - - def self.zero : self - new(0) - end - - # Returns self. - def + - self - end - - # Creates an `Array` of `self` with the given values, which will be casted - # to this type with the `new` method (defined in each `Number` type). - # - # ``` - # floats = Float64[1, 2, 3, 4] - # floats.class # => Array(Float64) - # - # ints = Int64[1, 2, 3] - # ints.class # => Array(Int64) - # ``` - macro [](*nums) - Array({{@type}}).build({{nums.size}}) do |%buffer| - {% for num, i in nums %} - %buffer[{{i}}] = {{@type}}.new({{num}}) - {% end %} - {{nums.size}} - end - end - - # Creates a `Slice` of `self` with the given values, which will be casted - # to this type with the `new` method (defined in each `Number` type). - # - # The slice is allocated on the heap. - # - # ``` - # floats = Float64.slice(1, 2, 3, 4) - # floats.class # => Slice(Float64) - # - # ints = Int64.slice(1, 2, 3) - # ints.class # => Slice(Int64) - # ``` - macro slice(*nums, read_only = false) - %slice = Slice({{@type}}).new({{nums.size}}, read_only: {{read_only}}) - {% for num, i in nums %} - %slice.to_unsafe[{{i}}] = {{@type}}.new({{num}}) - {% end %} - %slice - end - - # Creates a `StaticArray` of `self` with the given values, which will be casted - # to this type with the `new` method (defined in each `Number` type). - # - # ``` - # floats = Float64.static_array(1, 2, 3, 4) - # floats.class # => StaticArray(Float64, 4) - # - # ints = Int64.static_array(1, 2, 3) - # ints.class # => StaticArray(Int64, 3) - # ``` - macro static_array(*nums) - %array = uninitialized StaticArray({{@type}}, {{nums.size}}) - {% for num, i in nums %} - %array.to_unsafe[{{i}}] = {{@type}}.new({{num}}) - {% end %} - %array - end - - # Invokes the given block with the sequence of numbers starting at `self`, - # incremented by *by* on each call, and with an optional *to*. - # - # ``` - # 3.step(to: 10, by: 2) do |n| - # puts n - # end - # ``` - # - # Output: - # - # ```text - # 3 - # 5 - # 7 - # 9 - # ``` - def step(*, to = nil, by = 1) - x = self + (by - by) - - if to - if by > 0 - while x <= to - yield x - x += by - end - elsif by < 0 - while x >= to - yield x - x += by - end - end - else - while true - yield x - x += by - end - end - - self - end - - def step(*, to = nil, by = 1) - StepIterator.new(self + (by - by), to, by) - end - - # Returns the absolute value of this number. - # - # ``` - # 123.abs # => 123 - # -123.abs # => 123 - # ``` - def abs - self < 0 ? -self : self - end - - # Returns the square of `self` (`self * self`). - # - # ``` - # 4.abs2 # => 16 - # 1.5.abs2 # => 2.25 - # ``` - def abs2 - self * self - end - - # Returns the sign of this number as an `Int32`. - # * `-1` if this number is negative - # * `0` if this number is zero - # * `1` if this number is positive - # - # ``` - # 123.sign # => 1 - # 0.sign # => 0 - # -42.sign # => -1 - # ``` - def sign - self < 0 ? -1 : (self == 0 ? 0 : 1) - end - - # Returns a `Tuple` of two elements containing the quotient - # and modulus obtained by dividing `self` by *number*. - # - # ``` - # 11.divmod(3) # => {3, 2} - # 11.divmod(-3) # => {-4, -1} - # ``` - def divmod(number) - {(self / number).floor, self % number} - end - - # Implements the comparison operator. - # - # See also: `Object#<=>`. - def <=>(other) - self > other ? 1 : (self < other ? -1 : 0) - end - - # Keeps *digits* significants digits of this number in the given *base*. - # - # ``` - # 1234.567.significant(1) # => 1000 - # 1234.567.significant(2) # => 1200 - # 1234.567.significant(3) # => 1230 - # 1234.567.significant(4) # => 1235 - # 1234.567.significant(5) # => 1234.6 - # 1234.567.significant(6) # => 1234.57 - # 1234.567.significant(7) # => 1234.567 - # 1234.567.significant(8) # => 1234.567 - # - # 15.159.significant(1, base = 2) # => 16 - # ``` - def significant(digits, base = 10) - if digits < 0 - raise ArgumentError.new "digits should be non-negative" - end - - x = self.to_f - - if x == 0 - return x - end - - y = if base == 10 - 10 ** ((Math.log10(self.abs) - digits + 1).floor) - elsif base == 2 - 2 ** ((Math.log2(self.abs) - digits + 1).floor) - else - base ** (((Math.log2(self.abs)) / (Math.log2(base)) - digits + 1).floor) - end - - self.class.new((x / y).round * y) - end - - # Rounds this number to a given precision in decimal *digits*. - # - # ``` - # -1763.116.round(2) # => -1763.12 - # ``` - def round(digits, base = 10) - x = self.to_f - y = base ** digits - self.class.new((x * y).round / y) - end - - # Clamps a value within *range*. - # - # ``` - # 5.clamp(10..100) # => 10 - # 50.clamp(10..100) # => 50 - # 500.clamp(10..100) # => 100 - # ``` - def clamp(range : Range) - raise ArgumentError.new("Can't clamp an exclusive range") if range.exclusive? - clamp range.begin, range.end - end - - # Clamps a value between *min* and *max*. - # - # ``` - # 5.clamp(10, 100) # => 10 - # 50.clamp(10, 100) # => 50 - # 500.clamp(10, 100) # => 100 - # ``` - def clamp(min, max) - return max if self > max - return min if self < min - self - end - - private class StepIterator(T, L, B) - include Iterator(T) - - @n : T - @to : L - @by : B - @original : T - - def initialize(@n : T, @to : L, @by : B) - @original = @n - end - - def next - if to = @to - if @by > 0 - return stop if @n > to - elsif @by < 0 - return stop if @n < to - end - - value = @n - @n += @by - value - else - value = @n - @n += @by - value - end - end - - def rewind - @n = @original - self - end - end -end diff --git a/src/core/object.cr b/src/core/object.cr new file mode 100644 index 0000000..dc5818b --- /dev/null +++ b/src/core/object.cr @@ -0,0 +1,1192 @@ +# `Object` is the base type of all Crystal objects. +class Object + # Returns `true` if this object is equal to *other*. + # + # Subclasses override this method to provide class-specific meaning. + # abstract def ==(other) + + # Returns `true` if this object is not equal to *other*. + # + # By default this method is implemented as `!(self == other)` + # so there's no need to override this unless there's a more efficient + # way to do it. + def !=(other) + !(self == other) + end + + # Shortcut to `!(self =~ other)`. + def !~(other) + !(self =~ other) + end + + # Case equality. + # + # The `===` method is used in a `case ... when ... end` expression. + # + # For example, this code: + # + # ``` + # case value + # when x + # # something when x + # when y + # # something when y + # end + # ``` + # + # Is equivalent to this code: + # + # ``` + # if x === value + # # something when x + # elsif y === value + # # something when y + # end + # ``` + # + # Object simply implements `===` by invoking `==`, but subclasses + # (notably `Regex`) can override it to provide meaningful case-equality semantics. + def ===(other) + self == other + end + + # Pattern match. + # + # Overridden by descendants (notably `Regex` and `String`) to provide meaningful + # pattern-match semantics. + def =~(other) + nil + end + + # Generates an `Int` hash value for this object. + # + # This method must have the property that `a == b` implies `a.hash == b.hash`. + # + # The hash value is used along with `==` by the `Hash` class to determine if two objects + # reference the same hash key. + # TODO: It must be implemented by Float32 + # abstract def hash + + # Returns a string representation of this object. + # + # Descendants must usually **not** override this method. Instead, + # they must override `to_s(io)`, which must append to the given + # IO object. + # def to_s + # String.build do |io| + # to_s io + # end + # end + + # Appends a `String` representation of this object + # to the given `IO` object. + # + # An object must never append itself to the io argument, + # as this will in turn call `to_s(io)` on it. + # TODO: It must be implemented by Reference + # abstract def to_s(io : IO) + + # Returns a `String` representation of this object. + # + # Similar to `to_s`, but usually returns more information about + # this object. + # + # Classes must usually **not** override this method. Instead, + # they must override `inspect(io)`, which must append to the + # given `IO` object. + # def inspect + # String.build do |io| + # inspect io + # end + # end + + # Appends a string representation of this object + # to the given `IO` object. + # + # Similar to `to_s(io)`, but usually appends more information + # about this object. + # def inspect(io : IO) + # to_s io + # end + + # Pretty prints `self` into the given printer. + # + # By default appends a text that is the result of invoking + # `#inspect` on `self`. Subclasses should override + # for custom pretty printing. + # def pretty_print(pp : PrettyPrint) : Nil + # pp.text(inspect) + # end + + # Returns a pretty printed version of `self`. + # def pretty_inspect(width = 79, newline = "\n", indent = 0) : String + # String.build do |io| + # PrettyPrint.format(self, io, width, newline, indent) + # end + # end + + # Yields `self` to the block, and then returns `self`. + # + # The primary purpose of this method is to "tap into" a method chain, + # in order to perform operations on intermediate results within the chain. + # + # ``` + # (1..10).tap { |x| puts "original: #{x.inspect}" } + # .to_a.tap { |x| puts "array: #{x.inspect}" } + # .select { |x| x % 2 == 0 }.tap { |x| puts "evens: #{x.inspect}" } + # .map { |x| x*x }.tap { |x| puts "squares: #{x.inspect}" } + # ``` + def tap + yield self + self + end + + # Yields `self`. `Nil` overrides this method and doesn't yield. + # + # This method is useful for dealing with nilable types, to safely + # perform operations only when the value is not `nil`. + # + # ``` + # # First program argument in downcase, or nil + # ARGV[0]?.try &.downcase + # ``` + def try + yield self + end + + # Returns `self`. `Nil` overrides this method and raises an exception. + def not_nil! + self + end + + # Return `self`. + # + # ``` + # str = "hello" + # str.itself.object_id == str.object_id # => true + # ``` + def itself + self + end + + # Returns a shallow copy of this object. + # + # As a convention, `clone` is the method used to create a deep copy of + # an object, but this logic isn't defined generically for every type + # because cycles could be involved, and the clone logic might not need + # to clone everything. + # + # Many types in the standard library, like `Array`, `Hash`, `Set` and + # `Deque`, and all primitive types, define `dup` and `clone`. + # TODO: It must be implemented by Int8 + # abstract def dup + + # Unsafely reinterprets the bytes of an object as being of another `type`. + # + # This method is useful to treat a type that is represented as a chunk of + # bytes as another type where those bytes convey useful information. As an + # example, you can check the individual bytes of an `Int32`: + # + # ``` + # 0x01020304.unsafe_as(StaticArray(UInt8, 4)) # => StaticArray[4, 3, 2, 1] + # ``` + # + # Or treat the bytes of a `Float64` as an `Int64`: + # + # ``` + # 1.234_f64.unsafe_as(Int64) # => 4608236261112822104 + # ``` + # + # This method is **unsafe** because it behaves unpredictably when the given + # `type` doesn't have the same bytesize as the receiver, or when the given + # `type` representation doesn't semantically match the underlying bytes. + # + # Also note that because `unsafe_as` is a regular method, unlike the pseudo-method + # `as`, you can't specify some types in the type grammar using a short notation, so + # specifying a static array must always be done as `StaticArray(T, N)`, a tuple + # as `Tuple(...)` and so on, never as `UInt8[4]` or `{Int32, Int32}`. + def unsafe_as(type : T.class) forall T + x = self + pointerof(x).as(T*).value + end + + {% for prefixes in { {"", "", "@"}, {"class_", "self.", "@@"} } %} + {% + macro_prefix = prefixes[0].id + method_prefix = prefixes[1].id + var_prefix = prefixes[2].id + %} + + # Defines getter methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}getter name + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}name + # {{var_prefix}}name + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}getter :name, "age" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type. + # + # ``` + # class Person + # {{macro_prefix}}getter name : String + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String + # + # def {{method_prefix}}name : String + # {{var_prefix}}name + # end + # end + # ``` + # + # The type declaration can also include an initial value: + # + # ``` + # class Person + # {{macro_prefix}}getter name : String = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String = "John Doe" + # + # def {{method_prefix}}name : String + # {{var_prefix}}name + # end + # end + # ``` + # + # An assignment can be passed too, but in this case the type of the + # variable must be easily inferrable from the initial value: + # + # ``` + # class Person + # {{macro_prefix}}getter name = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name = "John Doe" + # + # def {{method_prefix}}name : String + # {{var_prefix}}name + # end + # end + # ``` + # + # If a block is given to the macro, a getter is generated + # with a variable that is lazily initialized with + # the block's contents: + # + # ``` + # class Person + # {{macro_prefix}}getter(birth_date) { Time.now } + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}birth_date + # {{var_prefix}}birth_date ||= Time.now + # end + # end + # ``` + macro {{macro_prefix}}getter(*names, &block) + \{% if block %} + \{% if names.size != 1 %} + \{{ raise "Only one argument can be passed to `getter` with a block" }} + \{% end %} + + \{% name = names[0] %} + + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name.var.id}} : \{{name.type}}? + + def {{method_prefix}}\{{name.var.id}} + {{var_prefix}}\{{name.var.id}} ||= \{{yield}} + end + \{% else %} + def {{method_prefix}}\{{name.id}} + {{var_prefix}}\{{name.id}} ||= \{{yield}} + end + \{% end %} + \{% else %} + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.var.id}} : \{{name.type}} + {{var_prefix}}\{{name.var.id}} + end + \{% elsif name.is_a?(Assign) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.target.id}} + {{var_prefix}}\{{name.target.id}} + end + \{% else %} + def {{method_prefix}}\{{name.id}} + {{var_prefix}}\{{name.id}} + end + \{% end %} + \{% end %} + \{% end %} + end + + # Defines raise-on-nil and nilable getter methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}getter! name + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}name? + # {{var_prefix}}name + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name.not_nil! + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}getter! :name, "age" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type, as nilable. + # + # ``` + # class Person + # {{macro_prefix}}getter! name : String + # end + # ``` + # + # is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String? + # + # def {{method_prefix}}name? + # {{var_prefix}}name + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name.not_nil! + # end + # end + # ``` + macro {{macro_prefix}}getter!(*names) + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}}? + \{% name = name.var %} + \{% end %} + + def {{method_prefix}}\{{name.id}}? + {{var_prefix}}\{{name.id}} + end + + def {{method_prefix}}\{{name.id}} + {{var_prefix}}\{{name.id}}.not_nil! + end + \{% end %} + end + + # Defines query getter methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}getter? happy + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}happy? + # {{var_prefix}}happy + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}getter? :happy, "famous" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type. + # + # ``` + # class Person + # {{macro_prefix}}getter? happy : Bool + # end + # ``` + # + # is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy : Bool + # + # def {{method_prefix}}happy? : Bool + # {{var_prefix}}happy + # end + # end + # ``` + # + # The type declaration can also include an initial value: + # + # ``` + # class Person + # {{macro_prefix}}getter? happy : Bool = true + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy : Bool = true + # + # def {{method_prefix}}happy? : Bool + # {{var_prefix}}happy + # end + # end + # ``` + # + # An assignment can be passed too, but in this case the type of the + # variable must be easily inferrable from the initial value: + # + # ``` + # class Person + # {{macro_prefix}}getter? happy = true + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy = true + # + # def {{method_prefix}}happy? + # {{var_prefix}}happy + # end + # end + # ``` + macro {{macro_prefix}}getter?(*names) + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.var.id}}? : \{{name.type}} + {{var_prefix}}\{{name.var.id}} + end + \{% elsif name.is_a?(Assign) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.target.id}}? + {{var_prefix}}\{{name.target.id}} + end + \{% else %} + def {{method_prefix}}\{{name.id}}? + {{var_prefix}}\{{name.id}} + end + \{% end %} + \{% end %} + end + + # Defines setter methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}setter name + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}setter :name, "age" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type. + # + # ``` + # class Person + # {{macro_prefix}}setter name : String + # end + # ``` + # + # is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String + # + # def {{method_prefix}}name=({{var_prefix}}name : String) + # end + # end + # ``` + # + # The type declaration can also include an initial value: + # + # ``` + # class Person + # {{macro_prefix}}setter name : String = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String = "John Doe" + # + # def {{method_prefix}}name=({{var_prefix}}name : String) + # end + # end + # ``` + # + # An assignment can be passed too, but in this case the type of the + # variable must be easily inferrable from the initial value: + # + # ``` + # class Person + # {{macro_prefix}}setter name = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name = "John Doe" + # + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # end + # ``` + macro {{macro_prefix}}setter(*names) + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) + end + \{% elsif name.is_a?(Assign) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) + end + \{% else %} + def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) + end + \{% end %} + \{% end %} + end + + # Defines property methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}property name + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}property :name, "age" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type. + # + # ``` + # class Person + # {{macro_prefix}}property name : String + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String + # + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name + # end + # end + # ``` + # + # The type declaration can also include an initial value: + # + # ``` + # class Person + # {{macro_prefix}}property name : String = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String = "John Doe" + # + # def {{method_prefix}}name=({{var_prefix}}name : String) + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name + # end + # end + # ``` + # + # An assignment can be passed too, but in this case the type of the + # variable must be easily inferrable from the initial value: + # + # ``` + # class Person + # {{macro_prefix}}property name = "John Doe" + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name = "John Doe" + # + # def {{method_prefix}}name=({{var_prefix}}name : String) + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name + # end + # end + # ``` + # + # If a block is given to the macro, a property is generated + # with a variable that is lazily initialized with + # the block's contents: + # + # ``` + # class Person + # {{macro_prefix}}property(birth_date) { Time.now } + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}birth_date + # {{var_prefix}}birth_date ||= Time.now + # end + # + # def {{method_prefix}}birth_date=({{var_prefix}}birth_date) + # end + # end + # ``` + macro {{macro_prefix}}property(*names, &block) + \{% if block %} + \{% if names.size != 1 %} + \{{ raise "Only one argument can be passed to `property` with a block" }} + \{% end %} + + \{% name = names[0] %} + + {{macro_prefix}}setter \{{name}} + + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name.var.id}} : \{{name.type}}? + + def {{method_prefix}}\{{name.var.id}} + {{var_prefix}}\{{name.var.id}} ||= \{{yield}} + end + \{% else %} + def {{method_prefix}}\{{name.id}} + {{var_prefix}}\{{name.id}} ||= \{{yield}} + end + \{% end %} + \{% else %} + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.var.id}} : \{{name.type}} + {{var_prefix}}\{{name.var.id}} + end + + def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) + end + \{% elsif name.is_a?(Assign) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.target.id}} + {{var_prefix}}\{{name.target.id}} + end + + def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) + end + \{% else %} + def {{method_prefix}}\{{name.id}} + {{var_prefix}}\{{name.id}} + end + + def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) + end + \{% end %} + \{% end %} + \{% end %} + end + + # Defines raise-on-nil property methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}property! name + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # + # def {{method_prefix}}name? + # {{var_prefix}}name + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name.not_nil! + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}property! :name, "age" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type, as nilable. + # + # ``` + # class Person + # {{macro_prefix}}property! name : String + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}name : String? + # + # def {{method_prefix}}name=({{var_prefix}}name) + # end + # + # def {{method_prefix}}name? + # {{var_prefix}}name + # end + # + # def {{method_prefix}}name + # {{var_prefix}}name.not_nil! + # end + # end + # ``` + macro {{macro_prefix}}property!(*names) + {{macro_prefix}}getter! \{{*names}} + + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) + end + \{% else %} + def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) + end + \{% end %} + \{% end %} + end + + # Defines query property methods for each of the given arguments. + # + # Writing: + # + # ``` + # class Person + # {{macro_prefix}}property? happy + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # def {{method_prefix}}happy=({{var_prefix}}happy) + # end + # + # def {{method_prefix}}happy? + # {{var_prefix}}happy + # end + # end + # ``` + # + # The arguments can be string literals, symbol literals or plain names: + # + # ``` + # class Person + # {{macro_prefix}}property? :happy, "famous" + # end + # ``` + # + # If a type declaration is given, a variable with that name + # is declared with that type. + # + # ``` + # class Person + # {{macro_prefix}}property? happy : Bool + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy : Bool + # + # def {{method_prefix}}happy=({{var_prefix}}happy) + # end + # + # def {{method_prefix}}happy? + # {{var_prefix}}happy + # end + # + # def {{method_prefix}}happy + # {{var_prefix}}happy.not_nil! + # end + # end + # ``` + # + # The type declaration can also include an initial value: + # + # ``` + # class Person + # {{macro_prefix}}property? happy : Bool = true + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy : Bool = true + # + # def {{method_prefix}}happy=({{var_prefix}}happy : Bool) + # end + # + # def {{method_prefix}}happy? : Bool + # {{var_prefix}}happy + # end + # end + # ``` + # + # An assignment can be passed too, but in this case the type of the + # variable must be easily inferrable from the initial value: + # + # ``` + # class Person + # {{macro_prefix}}property? happy = true + # end + # ``` + # + # Is the same as writing: + # + # ``` + # class Person + # {{var_prefix}}happy = true + # + # def {{method_prefix}}happy=({{var_prefix}}happy) + # end + # + # def {{method_prefix}}happy? + # {{var_prefix}}happy + # end + # end + # ``` + macro {{macro_prefix}}property?(*names) + \{% for name in names %} + \{% if name.is_a?(TypeDeclaration) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.var.id}}? : \{{name.type}} + {{var_prefix}}\{{name.var.id}} + end + + def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) + end + \{% elsif name.is_a?(Assign) %} + {{var_prefix}}\{{name}} + + def {{method_prefix}}\{{name.target.id}}? + {{var_prefix}}\{{name.target.id}} + end + + def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) + end + \{% else %} + def {{method_prefix}}\{{name.id}}? + {{var_prefix}}\{{name.id}} + end + + def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) + end + \{% end %} + \{% end %} + end + {% end %} + + # Delegate *methods* to *to*. + # + # Note that due to current language limitations this is only useful + # when no captured blocks are involved. + # + # ``` + # class StringWrapper + # def initialize(@string : String) + # end + # + # delegate downcase, to: @string + # delegate gsub, to: @string + # delegate empty?, capitalize, to: @string + # end + # + # wrapper = StringWrapper.new "HELLO" + # wrapper.downcase # => "hello" + # wrapper.gsub(/E/, "A") # => "HALLO" + # wrapper.empty? # => false + # wrapper.capitalize # => "Hello" + # ``` + macro delegate(*methods, to object) + {% for method in methods %} + def {{method.id}}(*args, **options) + {{object.id}}.{{method.id}}(*args, **options) + end + + def {{method.id}}(*args, **options) + {{object.id}}.{{method.id}}(*args, **options) do |*yield_args| + yield *yield_args + end + end + {% end %} + end + + # Defines a `hash` method computed from the given fields. + # + # ``` + # class Person + # def initialize(@name, @age) + # end + # + # # Define a hash method based on @name and @age + # def_hash @name, @age + # end + # ``` + macro def_hash(*fields) + def hash + {% if fields.size == 1 %} + {{fields[0]}}.hash + {% else %} + hash = 0 + {% for field in fields %} + hash = 31 * hash + {{field}}.hash + {% end %} + hash + {% end %} + end + end + + # Defines an `==` method by comparing the given fields. + # + # The generated `==` method has a `self` restriction. + # + # ``` + # class Person + # def initialize(@name, @age) + # end + # + # # Define a `==` method that compares @name and @age + # def_equals @name, @age + # end + # ``` + macro def_equals(*fields) + def ==(other : self) + {% for field in fields %} + return false unless {{field.id}} == other.{{field.id}} + {% end %} + true + end + end + + # Defines `hash` and `==` method from the given fields. + # + # The generated `==` method has a `self` restriction. + # + # ``` + # class Person + # def initialize(@name, @age) + # end + # + # # Define a hash method based on @name and @age + # # Define a `==` method that compares @name and @age + # def_equals_and_hash @name, @age + # end + # ``` + macro def_equals_and_hash(*fields) + def_equals {{*fields}} + def_hash {{*fields}} + end + + # Forwards missing methods to *delegate*. + # + # ``` + # class StringWrapper + # def initialize(@string : String) + # end + # + # forward_missing_to @string + # end + # + # wrapper = StringWrapper.new "HELLO" + # wrapper.downcase # => "hello" + # wrapper.gsub(/E/, "A") # => "HALLO" + # ``` + macro forward_missing_to(delegate) + macro method_missing(call) + {{delegate}}.\{{call}} + end + end + + # Defines a `clone` method that returns a copy of this object with all + # instance variables cloned (`clone` is in turn invoked on them). + macro def_clone + # Returns a copy of `self` with all instance variables cloned. + def clone + clone = \{{@type}}.allocate + clone.initialize_copy(self) + clone + end + + protected def initialize_copy(other) + \{% for ivar in @type.instance_vars %} + @\{{ivar.id}} = other.@\{{ivar.id}}.clone + \{% end %} + end + end + + protected def self.set_crystal_type_id(ptr) + ptr.as(LibCR::SizeT*).value = LibCR::SizeT.new(crystal_instance_type_id) + # ptr.as(LibC::SizeT*).value = LibC::SizeT.new(crystal_instance_type_id) + ptr + end +end diff --git a/src/core/pointer.cr b/src/core/pointer.cr index 72e4598..ddb12b7 100644 --- a/src/core/pointer.cr +++ b/src/core/pointer.cr @@ -1,26 +1,528 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. +# struct Pointer(T) + # def self.null + # new 0_u64 + # end + + # def +(other : Int) + # self + other.to_i64 + # end + + # def [](offset : Int) + # (self + offset).value + # end + + # def []=(offset : Int, value : T) + # (self + offset).value = value + # end +# end +# require "c/string" +require "./lib_cr/x86_64-linux-musl/c/string" +require "./lib_cr/x86_64-linux-musl/c/stdlib" + +# A typed pointer to some memory. +# +# This is the only unsafe type in Crystal. If you are using a pointer, you are writing +# unsafe code because a pointer doesn't know where it's pointing to nor how much memory +# starting from it is valid. However, pointers make it possible to interface with C and +# to implement efficient data structures. For example, both `Array` and `Hash` are +# implemented using pointers. +# +# You can obtain pointers in four ways: `#new`, `#malloc`, `pointerof` and by calling a C +# function that returns a pointer. +# +# `pointerof(x)`, where *x* is a variable or an instance variable, returns a pointer to +# that variable: +# +# ``` +# x = 1 +# ptr = pointerof(x) +# ptr.value = 2 +# x # => 2 +# ``` +# +# Note that a pointer is *falsey* if it's null (if it's address is zero). +# +# When calling a C function that expects a pointer you can also pass `nil` instead of using +# `Pointer.null` to construct a null pointer. +# +# For a safe alternative, see `Slice`, which is a pointer with a size and with bounds checking. struct Pointer(T) - def self.null - new 0_u64 + # Unsafe wrapper around a `Pointer` that allows to write values to + # it while advancing the location and keeping track of how many elements + # were written. + # + # See also: `Pointer#appender`. + struct Appender(T) + def initialize(@pointer : Pointer(T)) + @start = @pointer + end + + def <<(value : T) + @pointer.value = value + @pointer += 1 + end + + def size + @pointer - @start + end + + def pointer + @pointer + end end + include Comparable(self) + + # Returns `true` if this pointer's address is zero. + # + # ``` + # a = 1 + # pointerof(a).null? # => false + # + # b = Pointer(Int32).new(0) + # b.null? # => true + # ``` + def null? + address == 0 + end + + # Returns a new pointer whose address is this pointer's address incremented by `other * sizeof(T)`. + # + # ``` + # ptr = Pointer(Int32).new(1234) + # ptr.address # => 1234 + # + # # An Int32 occupies four bytes + # ptr2 = ptr + 1 + # ptr2.address # => 1238 + # ``` def +(other : Int) self + other.to_i64 end - def [](offset : Int) + # Returns a new pointer whose address is this pointer's address decremented by `other * sizeof(T)`. + # + # ``` + # ptr = Pointer(Int32).new(1234) + # ptr.address # => 1234 + # + # # An Int32 occupies four bytes + # ptr2 = ptr - 1 + # ptr2.address # => 1230 + # ``` + def -(other : Int) + self + (-other) + end + + # Returns -1, 0 or 1 if this pointer's address is less, equal or greater than *other*'s address, + # respectively. + # + # See also: `Object#<=>`. + # NOTE: Is `#<=>` in Comparable, not in Object? + # def <=>(other : self) + # address <=> other.address + # end + + # Gets the value pointed at this pointer's address plus `offset * sizeof(T)`. + # + # ``` + # ptr = Pointer.malloc(4) { |i| i + 10 } + # ptr[0] # => 10 + # ptr[1] # => 11 + # ptr[2] # => 12 + # ptr[3] # => 13 + # ``` + def [](offset) (self + offset).value end - def []=(offset : Int, value : T) + # Sets the value pointed at this pointer's address plus `offset * sizeof(T)`. + # + # ``` + # ptr = Pointer(Int32).malloc(4) # [0, 0, 0, 0] + # ptr[1] = 42 + # + # ptr2 = ptr + 1 + # ptr2.value # => 42 + # ``` + def []=(offset, value : T) (self + offset).value = value end + + # Copies *count* elements from *source* into `self`. + # If *source* and `self` overlap, behaviour is undefined. + # Use `#move_from` if they overlap (slower but always works). + # + # ``` + # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] + # + # # ptr2 -> [11, 12, 13, 14] + # # ^---^ <- copy this + # # ptr1 -> [1, 2, 3, 4] + # # ^---^ <- here + # ptr1.copy_from(ptr2, 2) + # ptr1[0] # => 11 + # ptr1[1] # => 12 + # ptr1[2] # => 3 + # ptr1[3] # => 4 + # ``` + # def copy_from(source : Pointer(T), count : Int) + # source.copy_to(self, count) + # end + + # :nodoc: + # def copy_from(source : Pointer(NoReturn), count : Int) + # raise ArgumentError.new("Negative count") if count < 0 + + # We need this overload for cases when we have a pointer to unreachable + # data, like when doing Tuple.new.to_a + # self + # end + + # Copies *count* elements from `self` into *target*. + # If `self` and *target* overlap, behaviour is undefined. + # Use `#move_to` if they overlap (slower but always works). + # + # ``` + # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] + # + # # ptr1 -> [1, 2, 3, 4] + # # ^---^ <- copy this + # # ptr2 -> [11, 12, 13, 14] + # # ^---^ <- here + # ptr1.copy_to(ptr2, 2) + # ptr2[0] # => 1 + # ptr2[1] # => 2 + # ptr2[2] # => 13 + # ptr2[3] # => 14 + # ``` + # def copy_to(target : Pointer, count : Int) + # target.copy_from_impl(self, count) + # end + + # Copies *count* elements from *source* into `self`. + # *source* and `self` may overlap; the copy is always done in a non-destructive manner. + # + # ``` + # ptr1 = Pointer.malloc(4) { |i| i + 1 } # ptr1 -> [1, 2, 3, 4] + # ptr2 = ptr1 + 1 # ^--------- ptr2 + # + # # [1, 2, 3, 4] + # # ^-----^ <- copy this + # # ^------^ <- here + # ptr2.move_from(ptr1, 3) + # + # ptr1[0] # => 1 + # ptr1[1] # => 1 + # ptr1[2] # => 2 + # ptr1[3] # => 3 + # ``` + # def move_from(source : Pointer(T), count : Int) + # source.move_to(self, count) + # end + + # :nodoc: + # def move_from(source : Pointer(NoReturn), count : Int) + # raise ArgumentError.new("Negative count") if count < 0 + + # We need this overload for cases when we have a pointer to unreachable + # data, like when doing Tuple.new.to_a + # self + # end + + # Copies *count* elements from `self` into *source*. + # *source* and `self` may overlap; the copy is always done in a non-destructive manner. + # + # ``` + # ptr1 = Pointer.malloc(4) { |i| i + 1 } # ptr1 -> [1, 2, 3, 4] + # ptr2 = ptr1 + 1 # ^--------- ptr2 + # + # # [1, 2, 3, 4] + # # ^-----^ <- copy this + # # ^------^ <- here + # ptr1.move_to(ptr2, 3) + # + # ptr1[0] # => 1 + # ptr1[1] # => 1 + # ptr1[2] # => 2 + # ptr1[3] # => 3 + # ``` + # def move_to(target : Pointer, count : Int) + # target.move_from_impl(self, count) + # end + + # We use separate method in which we make sure that `source` + # is never a union of pointers. This is guaranteed because both + # copy_from/move_from/copy_to/move_to reverse self and caller, + # and so if either self or the arguments are unions a dispatch + # will happen and unions will disappear. + # protected def copy_from_impl(source : Pointer(T), count : Int) + # raise ArgumentError.new("Negative count") if count < 0 + + # if self.class == source.class + # Intrinsics.memcpy(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false) + # else + # while (count -= 1) >= 0 + # self[count] = source[count] + # end + # end + # self + # end + + # protected def move_from_impl(source : Pointer(T), count : Int) + # raise ArgumentError.new("Negative count") if count < 0 + + # if self.class == source.class + # Intrinsics.memmove(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false) + # else + # if source.address < address + # copy_from source, count + # else + # count.times do |i| + # self[i] = source[i] + # end + # end + # end + # self + # end + + # Compares *count* elements from this pointer and *other*, byte by byte. + # + # Returns 0 if both pointers point to the same sequence of *count* bytes. Otherwise + # returns the difference between the first two differing bytes (treated as UInt8). + # + # ``` + # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] + # + # ptr1.memcmp(ptr2, 4) # => -10 + # ptr2.memcmp(ptr1, 4) # => 10 + # ptr1.memcmp(ptr1, 4) # => 0 + # ``` + # def memcmp(other : Pointer(T), count : Int) + # # LibC.memcmp(self.as(Void*), (other.as(Void*)), (count * sizeof(T))) + # LibCR.memcmp(self.as(Void*), (other.as(Void*)), (count * sizeof(T))) + # end + + # Swaps the contents pointed at the offsets *i* and *j*. + # + # ``` + # ptr = Pointer.malloc(4) { |i| i + 1 } + # ptr[2] # => 3 + # ptr[3] # => 4 + # ptr.swap(2, 3) + # ptr[2] # => 4 + # ptr[3] # => 3 + # ``` + # def swap(i, j) + # self[i], self[j] = self[j], self[i] + # end + + # Returns the address of this pointer. + # + # ``` + # ptr = Pointer(Int32).new(1234) + # ptr.hash # => 1234 + # ``` + # TODO: This is in object.cr + # def_hash address + + # Appends a string representation of this pointer to the given `IO`, + # including its type and address in hexadecimal. + # + # ``` + # ptr1 = Pointer(Int32).new(1234) + # ptr1.to_s # => "Pointer(Int32)@0x4d2" + # + # ptr2 = Pointer(Int32).new(0) + # ptr2.to_s # => "Pointer(Int32).null" + # ``` + # def to_s(io : IO) + # io << "Pointer(" + # io << T.to_s + # io << ")" + # if address == 0 + # io << ".null" + # else + # io << "@0x" + # address.to_s(16, io) + # end + # end + + # Tries to change the size of the allocation pointed to by this pointer to *size*, + # and returns that pointer. + # + # Since the space after the end of the block may be in use, realloc may find it + # necessary to copy the block to a new address where more free space is available. + # The value of realloc is the new address of the block. + # If the block needs to be moved, realloc copies the old contents. + # + # Remember to always assign the value of realloc. + # + # ``` + # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr = ptr.realloc(8) + # ptr # [1, 2, 3, 4, 0, 0, 0, 0] + # ``` + # def realloc(size : Int) + # realloc(size.to_u64) + # end + + # Shuffles *count* consecutive values pointed by this pointer. + # + # ``` + # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr.shuffle!(4) + # ptr # [3, 4, 1, 2] + # ``` + # def shuffle!(count : Int, random = Random::DEFAULT) + # (count - 1).downto(1) do |i| + # j = random.rand(i + 1) + # swap(i, j) + # end + # self + # end + + # Sets *count* consecutive values pointed by this pointer to the + # values returned by the block. + # + # ``` + # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] + # ptr.map!(4) { |value| value * 2 } + # ptr # [2, 4, 6, 8] + # ``` + # def map!(count : Int) + # count.times do |i| + # self[i] = yield self[i] + # end + # end + + # Like `map!`, but yield 2 arugments, the element and it's index + # def map_with_index!(count : Int, &block) + # count.times do |i| + # self[i] = yield self[i], i + # end + # self + # end + + # Returns a pointer whose memory address is zero. This doesn't allocate memory. + # + # When calling a C function you can also pass `nil` instead of constructing a + # null pointer with this method. + # + # ``` + # ptr = Pointer(Int32).null + # ptr.address # => 0 + # ``` + def self.null + new 0_u64 + end + + # Returns a pointer that points to the given memory address. This doesn't allocate memory. + # + # ``` + # ptr = Pointer(Int32).new(5678) + # ptr.address # => 5678 + # ``` + def self.new(address : Int) + new address.to_u64 + end + + # Allocates `size * sizeof(T)` bytes from the system's heap initialized + # to zero and returns a pointer to the first byte from that memory. + # The memory is allocated by the `GC`, so when there are + # no pointers to this memory, it will be automatically freed. + # + # ``` + # # Allocate memory for an Int32: 4 bytes + # ptr = Pointer(Int32).malloc + # ptr.value # => 0 + # + # # Allocate memory for 10 Int32: 40 bytes + # ptr = Pointer(Int32).malloc(10) + # ptr[0] # => 0 + # # ... + # ptr[9] # => 0 + # ``` + # def self.malloc(size : Int = 1) + # if size < 0 + # raise ArgumentError.new("Negative Pointer#malloc size") + # end + # malloc(size.to_u64) + # end + + # Allocates `size * sizeof(T)` bytes from the system's heap initialized + # to *value* and returns a pointer to the first byte from that memory. + # The memory is allocated by the `GC`, so when there are + # no pointers to this memory, it will be automatically freed. + # + # ``` + # # An Int32 occupies 4 bytes, so here we are requesting 8 bytes + # # initialized to the number 42 + # ptr = Pointer.malloc(2, 42) + # ptr[0] # => 42 + # ptr[1] # => 42 + # ``` + # def self.malloc(size : Int, value : T) + # ptr = Pointer(T).malloc(size) + # size.times { |i| ptr[i] = value } + # ptr + # end + + # Allocates `size * sizeof(T)` bytes from the system's heap initialized + # to the value returned by the block (which is invoked once with each index in the range `0...size`) + # and returns a pointer to the first byte from that memory. + # The memory is allocated by the `GC`, so when there are + # no pointers to this memory, it will be automatically freed. + # + # ``` + # # An Int32 occupies 4 bytes, so here we are requesting 16 bytes. + # # i is an index in the range 0 .. 3 + # ptr = Pointer.malloc(4) { |i| i + 10 } + # ptr[0] # => 10 + # ptr[1] # => 11 + # ptr[2] # => 12 + # ptr[3] # => 13 + # ``` + # def self.malloc(size : Int, &block : Int32 -> T) + # ptr = Pointer(T).malloc(size) + # size.times { |i| ptr[i] = yield i } + # ptr + # end + + # Returns a `Pointer::Appender` for this pointer. + def appender + Pointer::Appender.new(self) + end + + # Returns a `Slice` that points to this pointer and is bounded by the given *size*. + # + # ``` + # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15] + # slice = ptr.to_slice(4) # => Slice[10, 11, 12, 13] + # slice.class # => Slice(Int32) + # ``` + # def to_slice(size) + # Slice.new(self, size) + # end + + # Clears (sets to "zero" bytes) a number of values pointed by this pointer. + # + # ``` + # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15] + # ptr.clear(3) + # ptr.to_slice(6) # => Slice[0, 0, 0, 13, 14, 15] + # ``` + # def clear(count = 1) + # ptr = self.as(Pointer(Void)) + # TODO: Use LibC(R).memset ? + # Intrinsics.memset(self.as(Void*), 0_u8, (count * sizeof(T)).to_u32, 0_u32, false) + # end + + def clone + self + end end diff --git a/src/core/prelude.cr b/src/core/prelude.cr index fc9608a..03973d6 100644 --- a/src/core/prelude.cr +++ b/src/core/prelude.cr @@ -10,10 +10,26 @@ # Order-dependent list require "./lib_cr" require "./lib_cr/**" +require "./macros" +require "./object" +require "./comparable" +# require "exception" +require "./iterable" +# require "./iterator" +require "./indexable" require "./string" # Alpha-sorted list +require "./bool" require "./box" +require "./gc" +# require "./gc/null" +# require "./gc/boehm" require "./int" +require "./named_tuple" +require "./nil" require "./number" require "./pointer" +require "./reference" +require "./static_array" +require "./tuple" diff --git a/src/core/reference.cr b/src/core/reference.cr new file mode 100644 index 0000000..0340e7e --- /dev/null +++ b/src/core/reference.cr @@ -0,0 +1,133 @@ +# `Reference` is the base class of classes you define in your program. +# It is set as a class' superclass when you don't specify one: +# +# ``` +# class MyClass # < Reference +# end +# ``` +# +# A reference type is passed by reference: when you pass it to methods, +# return it from methods or assign it to variables, a pointer is actually passed. +# +# Invoking `new` on a `Reference` allocates a new instance on the heap. +# The instance's memory is automatically freed (garbage-collected) when +# the instance is no longer referred by any other entity in the program. +class Reference + # Returns `true` if this reference is the same as *other*. Invokes `same?`. + def ==(other : self) + same?(other) + end + + # Returns `false` (other can only be a `Value` here). + def ==(other) + false + end + + # Returns `true` if this reference is the same as *other*. This is only + # `true` if this reference's `object_id` is the same as *other*'s. + def same?(other : Reference) + object_id == other.object_id + end + + # Returns `false`: a reference is never `nil`. + def same?(other : Nil) + false + end + + # Returns a shallow copy of this object. + # + # This allocates a new object and copies the contents of + # `self` into it. + def dup + {% if @type.abstract? %} + # This shouldn't happen, as the type is abstract, + # but we need to avoid the allocate invocation below + raise "Can't dup {{@type}}" + {% else %} + dup = self.class.allocate + dup.as(Void*).copy_from(self.as(Void*), instance_sizeof(self)) + dup + {% end %} + end + + # Returns this reference's `object_id` as the hash value. + def hash + object_id + end + + # def inspect(io : IO) : Nil + # io << "#<" << {{@type.name.id.stringify}} << ":0x" + # object_id.to_s(16, io) + + # executed = exec_recursive(:inspect) do + # {% for ivar, i in @type.instance_vars %} + # {% if i > 0 %} + # io << "," + # {% end %} + # io << " @{{ivar.id}}=" + # @{{ivar.id}}.inspect io + # {% end %} + # end + # unless executed + # io << " ..." + # end + # io << ">" + # nil + # end + + # def pretty_print(pp) : Nil + # {% if @type.overrides?(Reference, "inspect") %} + # pp.text inspect + # {% else %} + # prefix = "#<#{{{@type.name.id.stringify}}}:0x#{object_id.to_s(16)}" + # executed = exec_recursive(:pretty_print) do + # pp.surround(prefix, ">", left_break: nil, right_break: nil) do + # {% for ivar, i in @type.instance_vars.map(&.name).sort %} + # {% if i == 0 %} + # pp.breakable + # {% else %} + # pp.comma + # {% end %} + # pp.group do + # pp.text "@{{ivar.id}}=" + # pp.nest do + # pp.breakable "" + # @{{ivar.id}}.pretty_print(pp) + # end + # end + # {% end %} + # end + # end + # unless executed + # pp.text "#{prefix} ...>" + # end + # {% end %} + # end + + # def to_s(io : IO) : Nil + # io << "#<" << self.class.name << ":0x" + # object_id.to_s(16, io) + # io << ">" + # nil + # end + + # :nodoc: + # module ExecRecursive + # def self.hash + # @@exec_recursive ||= {} of {UInt64, Symbol} => Bool + # end + # end + + # private def exec_recursive(method) + # hash = ExecRecursive.hash + # key = {object_id, method} + # if hash[key]? + # false + # else + # hash[key] = true + # value = yield + # hash.delete(key) + # true + # end + # end +end diff --git a/src/core/static_array.cr b/src/core/static_array.cr new file mode 100644 index 0000000..25ba47e --- /dev/null +++ b/src/core/static_array.cr @@ -0,0 +1,247 @@ +# A fixed-size, stack allocated array. +struct StaticArray(T, N) + # include Indexable(T) + + # Create a new `StaticArray` with the given *args*. The type of the + # static array will be the union of the type of the given *args*, + # and its size will be the number of elements in *args*. + # + # ``` + # ary = StaticArray[1, 'a'] + # ary[0] # => 1 + # ary[1] # => 'a' + # ary.class # => StaticArray(Char | Int32, 2) + # ``` + # + # See also: `Number.static_array`. + macro [](*args) + %array = uninitialized StaticArray(typeof({{*args}}), {{args.size}}) + {% for arg, i in args %} + %array.to_unsafe[{{i}}] = {{arg}} + {% end %} + %array + end + + # Creates a new static array and invokes the + # block once for each index of the array, assigning the + # block's value in that index. + # + # ``` + # StaticArray(Int32, 3).new { |i| i * 2 } # => StaticArray[0, 2, 4] + # ``` + def self.new(&block : Int32 -> T) + array = uninitialized self + N.times do |i| + array.to_unsafe[i] = yield i + end + array + end + + # Creates a new static array filled with the given value. + # + # ``` + # StaticArray(Int32, 3).new(42) # => StaticArray[42, 42, 42] + # ``` + def self.new(value : T) + new { value } + end + + # Disallow creating an uninitialized StaticArray with new. + # If this is desired, one can use `array = uninitialized ...` + # which makes it clear that it's unsafe. + private def initialize + end + + # Equality. Returns `true` if each element in `self` is equal to each + # corresponding element in *other*. + # + # ``` + # array = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] + # array2 = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] + # array3 = StaticArray(Int32, 3).new 1 # => StaticArray[1, 1, 1] + # array == array2 # => true + # array == array3 # => false + # ``` + def ==(other : StaticArray) + return false unless size == other.size + each_with_index do |e, i| + return false unless e == other[i] + end + true + end + + # Equality with another object. Always returns `false`. + # + # ``` + # array = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] + # array == nil # => false + # ``` + def ==(other) + false + end + + @[AlwaysInline] + def unsafe_at(index : Int) + to_unsafe[index] + end + + # Sets the given value at the given *index*. + # + # Negative indices can be used to start counting from the end of the array. + # Raises `IndexError` if trying to set an element outside the array's range. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] + # array[2] = 2 # => 2 + # array # => StaticArray[1, 2, 2] + # array[4] = 4 # raises IndexError + # ``` + @[AlwaysInline] + def []=(index : Int, value : T) + index = check_index_out_of_bounds index + to_unsafe[index] = value + end + + # Yields the current element at the given index and updates the value + # at the given *index* with the block's value. + # Raises `IndexError` if trying to set an element outside the array's range. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] + # array.update(1) { |x| x * 2 } # => 4 + # array # => StaticArray[1, 4, 3] + # array.update(5) { |x| x * 2 } # raises IndexError + # ``` + def update(index : Int) + index = check_index_out_of_bounds index + to_unsafe[index] = yield to_unsafe[index] + end + + # Returns the size of `self` + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } + # array.size # => 3 + # ``` + def size + N + end + + # Fills the array by substituting all elements with the given value. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } + # array.[]= 2 # => nil + # array # => StaticArray[2, 2, 2] + # ``` + def []=(value : T) + size.times do |i| + to_unsafe[i] = value + end + end + + # Modifies `self` by randomizing the order of elements in the array + # using the given *random* number generator. Returns `self`. + # + # ``` + # a = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] + # a.shuffle!(Random.new(42)) # => StaticArray[3, 2, 1] + # a # => StaticArray[3, 2, 1] + # ``` + def shuffle!(random = Random::DEFAULT) + to_slice.shuffle!(random) + self + end + + # Invokes the given block for each element of `self`, replacing the element + # with the value returned by the block. Returns `self`. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } + # array.map! { |x| x*x } # => StaticArray[1, 4, 9] + # ``` + def map! + to_unsafe.map!(size) { |e| yield e } + self + end + + # Like `map`, but the block gets passed both the element and its index. + def map_with_index!(&block : (T, Int32) -> T) + to_unsafe.map_with_index!(size) { |e, i| yield e, i } + self + end + + # Reverses the elements of this array in-place, then returns `self`. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } + # array.reverse! # => StaticArray[3, 2, 1] + # ``` + def reverse! + to_slice.reverse! + self + end + + # Returns a slice that points to the elements of this static array. + # Changes made to the returned slice also affect this static array. + # + # ``` + # array = StaticArray(Int32, 3).new(2) + # slice = array.to_slice # => Slice[2, 2, 2] + # slice[0] = 3 + # array # => StaticArray[3, 2, 2] + # ``` + def to_slice + Slice.new(to_unsafe, size) + end + + # Returns a pointer to this static array's data. + # + # ``` + # ary = StaticArray(Int32, 3).new(42) + # ary.to_unsafe[0] # => 42 + # ``` + def to_unsafe : Pointer(T) + pointerof(@buffer) + end + + # Appends a string representation of this static array to the given `IO`. + # + # ``` + # array = StaticArray(Int32, 3).new { |i| i + 1 } + # array.to_s # => "StaticArray[1, 2, 3]" + # ``` + def to_s(io : IO) + io << "StaticArray[" + join ", ", io, &.inspect(io) + io << "]" + end + + def pretty_print(pp) + # Don't pass `self` here because we'll pass `self` by + # value and for big static arrays that seems to make + # LLVM really slow. + # TODO: investigate why, maybe report a bug to LLVM? + pp.list("StaticArray[", to_slice, "]") + end + + # Returns a new `StaticArray` where each element is cloned from elements in `self`. + def clone + array = uninitialized self + N.times do |i| + array.to_unsafe[i] = to_unsafe[i].clone + end + array + end + + # :nodoc: + def index(object, offset : Int = 0) + # Optimize for the case of looking for a byte in a byte slice + if T.is_a?(UInt8.class) && + (object.is_a?(UInt8) || (object.is_a?(Int) && 0 <= object < 256)) + return to_slice.fast_index(object, offset) + end + + super + end +end diff --git a/src/core/string.cr b/src/core/string.cr index a9bc892..c0b5107 100644 --- a/src/core/string.cr +++ b/src/core/string.cr @@ -1,43 +1,4137 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. +# require "c/stdlib" +# require "c/stdio" +# require "c/string" +require "./lib_cr/x86_64-linux-musl/c/string" + +# A `String` represents an immutable sequence of UTF-8 characters. +# +# A `String` is typically created with a string literal, enclosing UTF-8 characters +# in double quotes: +# +# ``` +# "hello world" +# ``` +# +# A backslash can be used to denote some characters inside the string: +# +# ``` +# "\"" # double quote +# "\\" # backslash +# "\e" # escape +# "\f" # form feed +# "\n" # newline +# "\r" # carriage return +# "\t" # tab +# "\v" # vertical tab +# ``` +# +# You can use a backslash followed by an *u* and four hexadecimal characters to denote a unicode codepoint written: +# +# ``` +# "\u0041" # == "A" +# ``` +# +# Or you can use curly braces and specify up to six hexadecimal numbers (0 to 10FFFF): +# +# ``` +# "\u{41}" # == "A" +# ``` +# +# A string can span multiple lines: +# +# ``` +# "hello +# world" # same as "hello\n world" +# ``` +# +# Note that in the above example trailing and leading spaces, as well as newlines, +# end up in the resulting string. To avoid this, you can split a string into multiple lines +# by joining multiple literals with a backslash: +# +# ``` +# "hello " \ +# "world, " \ +# "no newlines" # same as "hello world, no newlines" +# ``` +# +# Alternatively, a backslash followed by a newline can be inserted inside the string literal: +# +# ``` +# "hello \ +# world, \ +# no newlines" # same as "hello world, no newlines" +# ``` +# +# In this case, leading whitespace is not included in the resulting string. +# +# If you need to write a string that has many double quotes, parentheses, or similar +# characters, you can use alternative literals: +# +# ``` +# # Supports double quotes and nested parentheses +# %(hello ("world")) # same as "hello (\"world\")" +# +# # Supports double quotes and nested brackets +# %[hello ["world"]] # same as "hello [\"world\"]" +# +# # Supports double quotes and nested curlies +# %{hello {"world"}} # same as "hello {\"world\"}" +# +# # Supports double quotes and nested angles +# %> # same as "hello <\"world\">" +# ``` +# +# To create a `String` with embedded expressions, you can use string interpolation: +# +# ``` +# a = 1 +# b = 2 +# "sum = #{a + b}" # "sum = 3" +# ``` +# +# This ends up invoking `Object#to_s(IO)` on each expression enclosed by `#{...}`. +# +# If you need to dynamically build a string, use `String#build` or `IO::Memory`. +# +# ### Non UTF-8 valid strings +# +# String might end up being conformed of bytes which are an invalid +# byte sequence according to UTF-8. This can happen if the string is created +# via one of the constructors that accept bytes, or when getting a string +# from `String.build` or `IO::Memory`. No exception will be raised, but +# invalid byte sequences, when asked as chars, will use the unicode replacement +# char (value 0xFFFD). For example: +# +# ``` +# # here 255 is not a valid byte value in the UTF-8 encoding +# string = String.new(Bytes[255, 97]) +# string.valid_encoding? # => false +# +# # The first char here is the unicode replacement char +# string.chars # => ['�', 'a'] +# ``` +# +# One can also create strings with specific byte value in them by +# using octal and hexadecimal escape sequences: +# +# ``` +# # Octal escape sequences +# "\101" # # => "A" +# "\12" # # => "\n" +# "\1" # string with one character with code point 1 +# "\377" # string with one byte with value 255 +# +# # Hexadecimal escape sequences +# "\x45" # # => "A" +# "\xFF" # string with one byte with value 255 +# ``` # -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. +# The reason for allowing strings that don't have a valid UTF-8 sequence +# is that the world is full of content that isn't properly encoded, +# and having a program raise an exception or stop because of this +# is not good. It's better if programs are more resilient, but +# show a replacement character when there's an error in incoming data. +class String + # :nodoc: + # TODO: Implement this! + # TYPE_ID = "".crystal_type_id + + # :nodoc: + HEADER_SIZE = sizeof({Int32, Int32, Int32}) + # TODO: Implement this! + # include Comparable(self) + # TODO: Implement this! + # macro inherited + # {{ raise "Cannot inherit from String" }} + # end + + # Creates a `String` from the given *slice*. `Bytes` will be copied from the slice. + # + # This method is always safe to call, and the resulting string will have + # the contents and size of the slice. + # + # ``` + # slice = Slice.new(4) { |i| ('a'.ord + i).to_u8 } + # String.new(slice) # => "abcd" + # ``` + # TODO: Implement this! (Bytes is alias for Slice) + # def self.new(slice : Bytes) + # new(slice.pointer(slice.size), slice.size) + # end + + # Creates a new `String` from the given *bytes*, which are encoded in the given *encoding*. + # + # The *invalid* argument can be: + # * `nil`: an exception is raised on invalid byte sequences + # * `:skip`: invalid byte sequences are ignored + # + # ``` + # slice = Slice.new(2, 0_u8) + # slice[0] = 186_u8 + # slice[1] = 195_u8 + # String.new(slice, "GB2312") # => "好" + # ``` + # TODO: Implement this! + # def self.new(bytes : Bytes, encoding : String, invalid : Symbol? = nil) : String + # String.build do |str| + # String.encode(bytes, encoding, "UTF-8", str, invalid) + # end + # end + + # Creates a `String` from a pointer. `Bytes` will be copied from the pointer. + # + # This method is **unsafe**: the pointer must point to data that eventually + # contains a zero byte that indicates the ends of the string. Otherwise, + # the result of this method is undefined and might cause a segmentation fault. + # + # This method is typically used in C bindings, where you get a `char*` from a + # library and the library guarantees that this pointer eventually has an + # ending zero byte. + # + # ``` + # ptr = Pointer.malloc(5) { |i| i == 4 ? 0_u8 : ('a'.ord + i).to_u8 } + # String.new(ptr) # => "abcd" + # ``` + # TODO: Implement this! + # def self.new(chars : UInt8*) + # new(chars, LibC.strlen(chars)) + # new(chars, LibCR.strlen(chars)) + # end + + # Creates a new `String` from a pointer, indicating its bytesize count + # and, optionally, the UTF-8 codepoints count (size). `Bytes` will be + # copied from the pointer. + # + # If the given size is zero, the amount of UTF-8 codepoints will be + # lazily computed when needed. + # + # ``` + # ptr = Pointer.malloc(4) { |i| ('a'.ord + i).to_u8 } + # String.new(ptr, 2) # => "ab" + # ``` + # TODO: Implement this! + # def self.new(chars : UInt8*, bytesize, size = 0) + # # Avoid allocating memory for the empty string + # return "" if bytesize == 0 + + # new(bytesize) do |buffer| + # buffer.copy_from(chars, bytesize) + # {bytesize, size} + # end + # end + + # Creates a new `String` by allocating a buffer (`Pointer(UInt8)`) with the given capacity, then + # yielding that buffer. The block must return a tuple with the bytesize and size + # (UTF-8 codepoints count) of the String. If the returned size is zero, the UTF-8 codepoints + # count will be lazily computed. + # + # The bytesize returned by the block must be less than or equal to the + # capacity given to this String, otherwise `ArgumentError` is raised. + # + # If you need to build a `String` where the maximum capacity is unknown, use `String#build`. + # + # ``` + # str = String.new(4) do |buffer| + # buffer[0] = 'a'.ord.to_u8 + # buffer[1] = 'b'.ord.to_u8 + # {2, 2} + # end + # str # => "ab" + # ``` + # TODO: Implement this! + # def self.new(capacity : Int) + # check_capacity_in_bounds(capacity) + + # str = GC.malloc_atomic(capacity.to_u32 + HEADER_SIZE + 1).as(UInt8*) + # buffer = str.as(String).to_unsafe + # bytesize, size = yield buffer + + # unless 0 <= bytesize <= capacity + # raise ArgumentError.new("Bytesize out of capacity bounds") + # end + + # buffer[bytesize] = 0_u8 + + # # Try to reclaim some memory if capacity is bigger than what was requested + # if bytesize < capacity + # str = str.realloc(bytesize.to_u32 + HEADER_SIZE + 1) + # end + + # str_header = str.as({Int32, Int32, Int32}*) + # str_header.value = {TYPE_ID, bytesize.to_i, size.to_i} + # str.as(String) + # end + + # Builds a `String` by creating a `String::Builder` with the given initial capacity, yielding + # it to the block and finally getting a `String` out of it. The `String::Builder` automatically + # resizes as needed. + # + # ``` + # str = String.build do |str| + # str << "hello " + # str << 1 + # end + # str # => "hello 1" + # ``` + # TODO: Implement this! + # def self.build(capacity = 64) : self + # String::Builder.build(capacity) do |builder| + # yield builder + # end + # end + + + + # Returns the number of bytes in this string. + # + # ``` + # "hello".bytesize # => 5 + # "你好".bytesize # => 6 + # ``` + def bytesize + @bytesize + end + + # Returns the result of interpreting leading characters in this string as an + # integer base *base* (between 2 and 36). + # + # If there is not a valid number at the start of this string, + # or if the resulting integer doesn't fit an `Int32`, an `ArgumentError` is raised. + # + # Options: + # * **whitespace**: if `true`, leading and trailing whitespaces are allowed + # * **underscore**: if `true`, underscores in numbers are allowed + # * **prefix**: if `true`, the prefixes `"0x"`, `"0"` and `"0b"` override the base + # * **strict**: if `true`, extraneous characters past the end of the number are disallowed + # + # ``` + # "12345".to_i # => 12345 + # "0a".to_i # raises ArgumentError + # "hello".to_i # raises ArgumentError + # "0a".to_i(16) # => 10 + # "1100101".to_i(2) # => 101 + # "1100101".to_i(8) # => 294977 + # "1100101".to_i(10) # => 1100101 + # "1100101".to_i(base: 16) # => 17826049 + # + # "12_345".to_i # raises ArgumentError + # "12_345".to_i(underscore: true) # => 12345 + # + # " 12345 ".to_i # => 12345 + # " 12345 ".to_i(whitespace: false) # raises ArgumentError + # + # "0x123abc".to_i # raises ArgumentError + # "0x123abc".to_i(prefix: true) # => 1194684 + # + # "99 red balloons".to_i # raises ArgumentError + # "99 red balloons".to_i(strict: false) # => 99 + # ``` + # TODO: Implement this! + # def to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) + # to_i32(base, whitespace, underscore, prefix, strict) + # end + + # Same as `#to_i`, but returns `nil` if there is not a valid number at the start + # of this string, or if the resulting integer doesn't fit an `Int32`. + # + # ``` + # "12345".to_i? # => 12345 + # "99 red balloons".to_i? # => nil + # "0a".to_i?(strict: false) # => 0 + # "hello".to_i? # => nil + # ``` + # TODO: Implement this! + # def to_i?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) + # to_i32?(base, whitespace, underscore, prefix, strict) + # end + + # Same as `#to_i`, but returns the block's value if there is not a valid number at the start + # of this string, or if the resulting integer doesn't fit an `Int32`. + # + # ``` + # "12345".to_i { 0 } # => 12345 + # "hello".to_i { 0 } # => 0 + # ``` + # TODO: Implement this! + # def to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # to_i32(base, whitespace, underscore, prefix, strict) { yield } + # end + + # Same as `#to_i` but returns an `Int8`. + # TODO: Implement this! + # def to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8 + # to_i8(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int8: #{self}") } + # end + + # Same as `#to_i` but returns an `Int8` or `nil`. + # TODO: Implement this! + # def to_i8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8? + # to_i8(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `Int8` or the block's value. + # TODO: Implement this! + # def to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ i8, 127, 128 + # end + + # Same as `#to_i` but returns an `UInt8`. + # TODO: Implement this! + # def to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8 + # to_u8(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt8: #{self}") } + # end + + # Same as `#to_i` but returns an `UInt8` or `nil`. + # TODO: Implement this! + # def to_u8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8? + # to_u8(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `UInt8` or the block's value. + # TODO: Implement this! + # def to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ u8, 255 + # end + + # Same as `#to_i` but returns an `Int16`. + # TODO: Implement this! + # def to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16 + # to_i16(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int16: #{self}") } + # end + + # Same as `#to_i` but returns an `Int16` or `nil`. + # TODO: Implement this! + # def to_i16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16? + # to_i16(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `Int16` or the block's value. + # TODO: Implement this! + # def to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ i16, 32767, 32768 + # end + + # Same as `#to_i` but returns an `UInt16`. + # TODO: Implement this! + # def to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16 + # to_u16(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt16: #{self}") } + # end + + # Same as `#to_i` but returns an `UInt16` or `nil`. + # TODO: Implement this! + # def to_u16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16? + # to_u16(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `UInt16` or the block's value. + # TODO: Implement this! + # def to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ u16, 65535 + # end + + # Same as `#to_i`. + # TODO: Implement this! + # def to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32 + # to_i32(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int32: #{self}") } + # end + + # Same as `#to_i`. + # TODO: Implement this! + # def to_i32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32? + # to_i32(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i`. + # TODO: Implement this! + # def to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ i32, 2147483647, 2147483648 + # end + + # Same as `#to_i` but returns an `UInt32`. + # TODO: Implement this! + # def to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32 + # to_u32(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt32: #{self}") } + # end + + # Same as `#to_i` but returns an `UInt32` or `nil`. + # TODO: Implement this! + # def to_u32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32? + # to_u32(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `UInt32` or the block's value. + # TODO: Implement this! + # def to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ u32, 4294967295 + # end + + # Same as `#to_i` but returns an `Int64`. + # TODO: Implement this! + # def to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64 + # to_i64(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int64: #{self}") } + # end + + # Same as `#to_i` but returns an `Int64` or `nil`. + # TODO: Implement this! + # def to_i64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64? + # to_i64(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `Int64` or the block's value. + # TODO: Implement this! + # def to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ i64, 9223372036854775807, 9223372036854775808 + # end + + # Same as `#to_i` but returns an `UInt64`. + # TODO: Implement this! + # def to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64 + # to_u64(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt64: #{self}") } + # end + + # Same as `#to_i` but returns an `UInt64` or `nil`. + # TODO: Implement this! + # def to_u64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64? + # to_u64(base, whitespace, underscore, prefix, strict) { nil } + # end + + # Same as `#to_i` but returns an `UInt64` or the block's value. + # TODO: Implement this! + # def to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) + # gen_to_ u64 + # end + + # :nodoc: + # TODO: Implement this! + # CHAR_TO_DIGIT = begin + # table = StaticArray(Int8, 256).new(-1_i8) + # 10_i8.times do |i| + # table.to_unsafe[48 + i] = i + # end + # 26_i8.times do |i| + # table.to_unsafe[65 + i] = i + 10 + # table.to_unsafe[97 + i] = i + 10 + # end + # table + # end + + # :nodoc: + # TODO: Implement this! + # CHAR_TO_DIGIT62 = begin + # table = CHAR_TO_DIGIT.clone + # 26_i8.times do |i| + # table.to_unsafe[65 + i] = i + 36 + # end + # table + # end + + # :nodoc: + # TODO: Implement this! + # record ToU64Info, + # value : UInt64, + # negative : Bool, + # invalid : Bool + + # private macro gen_to_(method, max_positive = nil, max_negative = nil) + # info = to_u64_info(base, whitespace, underscore, prefix, strict) + # return yield if info.invalid + + # if info.negative + # {% if max_negative %} + # return yield if info.value > {{max_negative}} + # -info.value.to_{{method}} + # {% else %} + # return yield + # {% end %} + # else + # {% if max_positive %} + # return yield if info.value > {{max_positive}} + # {% end %} + # info.value.to_{{method}} + # end + # end + # TODO: Implement this! + # private def to_u64_info(base, whitespace, underscore, prefix, strict) + # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 + + # ptr = to_unsafe + + # # Skip leading whitespace + # if whitespace + # while ptr.value.unsafe_chr.ascii_whitespace? + # ptr += 1 + # end + # end + + # negative = false + # found_digit = false + # mul_overflow = ~0_u64 / base + + # # Check + and - + # case ptr.value.unsafe_chr + # when '+' + # ptr += 1 + # when '-' + # negative = true + # ptr += 1 + # end + + # # Check leading zero + # if ptr.value.unsafe_chr == '0' + # ptr += 1 + + # if prefix + # case ptr.value.unsafe_chr + # when 'b' + # base = 2 + # ptr += 1 + # when 'x' + # base = 16 + # ptr += 1 + # else + # base = 8 + # end + # found_digit = false + # else + # found_digit = true + # end + # end + + # value = 0_u64 + # last_is_underscore = true + # invalid = false + + # digits = (base == 62 ? CHAR_TO_DIGIT62 : CHAR_TO_DIGIT).to_unsafe + # while ptr.value != 0 + # if ptr.value.unsafe_chr == '_' && underscore + # break if last_is_underscore + # last_is_underscore = true + # ptr += 1 + # next + # end + + # last_is_underscore = false + # digit = digits[ptr.value] + # if digit == -1 || digit >= base + # break + # end + + # if value > mul_overflow + # invalid = true + # break + # end + + # value *= base + + # old = value + # value += digit + # if value < old + # invalid = true + # break + # end + + # found_digit = true + # ptr += 1 + # end + + # if found_digit + # unless ptr.value == 0 + # if whitespace + # while ptr.value.unsafe_chr.ascii_whitespace? + # ptr += 1 + # end + # end + + # if strict && ptr.value != 0 + # invalid = true + # end + # end + # else + # invalid = true + # end + + # ToU64Info.new value, negative, invalid + # end + + # Returns the result of interpreting characters in this string as a floating point number (`Float64`). + # This method raises an exception if the string is not a valid float representation. + # + # Options: + # * **whitespace**: if `true`, leading and trailing whitespaces are allowed + # * **strict**: if `true`, extraneous characters past the end of the number are disallowed + # + # ``` + # "123.45e1".to_f # => 1234.5 + # "45.67 degrees".to_f # raises ArgumentError + # "thx1138".to_f(strict: false) # raises ArgumentError + # " 1.2".to_f(whitespace: false) # raises ArgumentError + # "1.2foo".to_f(strict: false) # => 1.2 + # ``` + # TODO: Implement this! + # def to_f(whitespace = true, strict = true) + # to_f64(whitespace: whitespace, strict: strict) + # end + + # Returns the result of interpreting characters in this string as a floating point number (`Float64`). + # This method returns `nil` if the string is not a valid float representation. + # + # Options: + # * **whitespace**: if `true`, leading and trailing whitespaces are allowed + # * **strict**: if `true`, extraneous characters past the end of the number are disallowed + # + # ``` + # "123.45e1".to_f? # => 1234.5 + # "45.67 degrees".to_f? # => nil + # "thx1138".to_f? # => nil + # " 1.2".to_f?(whitespace: false) # => nil + # "1.2foo".to_f?(strict: false) # => 1.2 + # ``` + # TODO: Implement this! + # def to_f?(whitespace = true, strict = true) + # to_f64?(whitespace: whitespace, strict: strict) + # end + + # Same as `#to_f` but returns a Float32. + # TODO: Implement this! + # def to_f32(whitespace = true, strict = true) + # to_f32?(whitespace: whitespace, strict: strict) || raise ArgumentError.new("Invalid Float32: #{self}") + # end + + # Same as `#to_f?` but returns a Float32. + # TODO: Implement this! + # def to_f32?(whitespace = true, strict = true) + # to_f_impl(whitespace: whitespace, strict: strict) do + # v = LibC.strtof self, out endptr + # {v, endptr} + # end + # end + + # Same as `#to_f`. + # TODO: Implement this! + # def to_f64(whitespace = true, strict = true) + # to_f64?(whitespace: whitespace, strict: strict) || raise ArgumentError.new("Invalid Float64: #{self}") + # end + + # Same as `#to_f?`. + # TODO: Implement this! + # def to_f64?(whitespace = true, strict = true) + # to_f_impl(whitespace: whitespace, strict: strict) do + # v = LibC.strtod self, out endptr + # {v, endptr} + # end + # end + # TODO: Implement this! + # private def to_f_impl(whitespace = true, strict = true) + # return unless whitespace || '0' <= self[0] <= '9' || self[0] == '-' || self[0] == '+' + + # v, endptr = yield + # string_end = to_unsafe + bytesize + + # # blank string + # return if endptr == to_unsafe + + # if strict + # if whitespace + # while endptr < string_end && endptr.value.chr.ascii_whitespace? + # endptr += 1 + # end + # end + # # reached the end of the string + # v if endptr == string_end + # else + # ptr = to_unsafe + # if whitespace + # while ptr < string_end && ptr.value.chr.ascii_whitespace? + # ptr += 1 + # end + # end + # # consumed some bytes + # v if endptr > ptr + # end + # end + + # Returns the `Char` at the given *index*, or raises `IndexError` if out of bounds. + # + # Negative indices can be used to start counting from the end of the string. + # + # ``` + # "hello"[0] # 'h' + # "hello"[1] # 'e' + # "hello"[-1] # 'o' + # "hello"[-2] # 'l' + # "hello"[5] # raises IndexError + # ``` + # TODO: Implement this! + # def [](index : Int) + # at(index) { raise IndexError.new } + # end + + # Returns a substring by using a Range's *begin* and *end* + # as character indices. Indices can be negative to start + # counting from the end of the string. + # + # Raises `IndexError` if the range's start is not in range. + # + # ``` + # "hello"[0..2] # "hel" + # "hello"[0...2] # "he" + # "hello"[1..-1] # "ello" + # "hello"[1...-1] # "ell" + # ``` + # TODO: Implement this! + # def [](range : Range(Int, Int)) + # self[*Indexable.range_to_index_and_count(range, size)] + # end + + # Returns a substring starting from the *start* character + # of size *count*. + # + # The *start* argument can be negative to start counting + # from the end of the string. + # + # Raises `IndexError` if *start* isn't in range. + # + # Raises `ArgumentError` if *count* is negative. + # TODO: Implement this! + # def [](start : Int, count : Int) + # if ascii_only? + # return byte_slice(start, count) + # end + + # start += size if start < 0 + + # start_pos = nil + # end_pos = nil + + # reader = Char::Reader.new(self) + # i = 0 + + # reader.each do |char| + # if i == start + # start_pos = reader.pos + # elsif count >= 0 && i == start + count + # end_pos = reader.pos + # i += 1 + # break + # end + # i += 1 + # end + + # end_pos ||= reader.pos + + # if start_pos + # raise ArgumentError.new "Negative count" if count < 0 + # return "" if count == 0 + + # count = end_pos - start_pos + # return self if count == bytesize + + # String.new(count) do |buffer| + # buffer.copy_from(to_unsafe + start_pos, count) + # {count, 0} + # end + # elsif start == i + # if count >= 0 + # return "" + # else + # raise ArgumentError.new "Negative count" + # end + # else + # raise IndexError.new + # end + # end + # TODO: Implement these! + # def []?(index : Int) + # at(index) { nil } + # end + + # def []?(str : String | Char) + # includes?(str) ? str : nil + # end + + # def []?(regex : Regex) + # self[regex, 0]? + # end + + # def []?(regex : Regex, group) + # match = match(regex) + # match[group]? if match + # end + # TODO: Implement these! + # def [](str : String | Char) + # self[str]?.not_nil! + # end + + # def [](regex : Regex) + # self[regex]?.not_nil! + # end + + # def [](regex : Regex, group) + # self[regex, group]?.not_nil! + # end + # Implement these! + # def at(index : Int) + # at(index) { raise IndexError.new } + # end + + # def at(index : Int) + # if ascii_only? + # byte = byte_at?(index) + # if byte + # return byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT + # else + # return yield + # end + # end + + # index += size if index < 0 + + # byte_index = char_index_to_byte_index(index) + # if byte_index + # reader = Char::Reader.new(self, pos: byte_index) + # return reader.current_char + # else + # yield + # end + # end + # TODO: Implement this! + # def byte_slice(start : Int, count : Int) + # start += bytesize if start < 0 + # single_byte_optimizable = ascii_only? + + # if 0 <= start < bytesize + # raise ArgumentError.new "Negative count" if count < 0 + + # count = bytesize - start if start + count > bytesize + # return "" if count == 0 + # return self if count == bytesize + + # String.new(count) do |buffer| + # buffer.copy_from(to_unsafe + start, count) + # slice_size = single_byte_optimizable ? count : 0 + # {count, slice_size} + # end + # elsif start == bytesize + # if count >= 0 + # return "" + # else + # raise ArgumentError.new "Negative count" + # end + # else + # raise IndexError.new + # end + # end + # TODO: Implement this! + # def byte_slice(start : Int) + # byte_slice start, bytesize - start + # end + # TODO: Implement these! + # def codepoint_at(index) + # char_at(index).ord + # end + + # def char_at(index) + # self[index] + # end + # Implement these! + # def byte_at(index) + # byte_at(index) { raise IndexError.new } + # end + + # def byte_at?(index) + # byte_at(index) { nil } + # end + # Implement this! + # def byte_at(index) + # index += bytesize if index < 0 + # if 0 <= index < bytesize + # to_unsafe[index] + # else + # yield + # end + # end + + def unsafe_byte_at(index) + to_unsafe[index] + end + + # Returns a new `String` with each uppercase letter replaced with its lowercase + # counterpart. + # + # ``` + # "hEllO".downcase # => "hello" + # ``` + # TODO: Implement this! + # def downcase(options = Unicode::CaseOptions::None) + # return self if empty? + + # if ascii_only? && (options.none? || options.ascii?) + # String.new(bytesize) do |buffer| + # bytesize.times do |i| + # buffer[i] = to_unsafe[i].unsafe_chr.downcase.ord.to_u8 + # end + # {@bytesize, @length} + # end + # else + # String.build(bytesize) do |io| + # each_char do |char| + # char.downcase(options) do |res| + # io << res + # end + # end + # end + # end + # end + + # Returns a new `String` with each lowercase letter replaced with its uppercase + # counterpart. + # + # ``` + # "hEllO".upcase # => "HELLO" + # ``` + # TODO: Implement this! + # def upcase(options = Unicode::CaseOptions::None) + # return self if empty? + + # if ascii_only? && (options.none? || options.ascii?) + # String.new(bytesize) do |buffer| + # bytesize.times do |i| + # buffer[i] = to_unsafe[i].unsafe_chr.upcase.ord.to_u8 + # end + # {@bytesize, @length} + # end + # else + # String.build(bytesize) do |io| + # each_char do |char| + # char.upcase(options) do |res| + # io << res + # end + # end + # end + # end + # end + + # Returns a new `String` with the first letter converted to uppercase and every + # subsequent letter converted to lowercase. + # + # ``` + # "hEllO".capitalize # => "Hello" + # ``` + # TODO: Implement this! + # def capitalize(options = Unicode::CaseOptions::None) + # return self if empty? + + # if ascii_only? && (options.none? || options.ascii?) + # String.new(bytesize) do |buffer| + # bytesize.times do |i| + # if i == 0 + # buffer[i] = to_unsafe[i].unsafe_chr.upcase.ord.to_u8 + # else + # buffer[i] = to_unsafe[i].unsafe_chr.downcase.ord.to_u8 + # end + # end + # {@bytesize, @length} + # end + # else + # String.build(bytesize) do |io| + # each_char_with_index do |char, i| + # if i == 0 + # char.upcase(options) { |c| io << c } + # else + # char.downcase(options) { |c| io << c } + # end + # end + # end + # end + # end + + # Returns a new `String` with the last carriage return removed (that is, it + # will remove \n, \r, and \r\n). + # + # ``` + # "string\r\n".chomp # => "string" + # "string\n\r".chomp # => "string\n" + # "string\n".chomp # => "string" + # "string".chomp # => "string" + # "x".chomp.chomp # => "x" + # ``` + # Implement this! + # def chomp + # return self if empty? + + # case to_unsafe[bytesize - 1] + # when '\n' + # if bytesize > 1 && to_unsafe[bytesize - 2] === '\r' + # unsafe_byte_slice_string(0, bytesize - 2) + # else + # unsafe_byte_slice_string(0, bytesize - 1) + # end + # when '\r' + # unsafe_byte_slice_string(0, bytesize - 1) + # else + # self + # end + # end + + # Returns a new `String` with *suffix* removed from the end of the string. + # If *suffix* is `'\n'` then `"\r\n"` is also removed if the string ends with it, + # + # ``` + # "hello".chomp('o') # => "hell" + # "hello".chomp('a') # => "hello" + # ``` + # TODO: Implement this! + # def chomp(suffix : Char) + # if suffix == '\n' + # chomp + # elsif ends_with?(suffix) + # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) + # else + # self + # end + # end + + # Returns a new `String` with *suffix* removed from the end of the string. + # If *suffix* is `"\n"` then `"\r\n"` is also removed if the string ends with it, + # + # ``` + # "hello".chomp("llo") # => "he" + # "hello".chomp("ol") # => "hello" + # ``` + # TODO: Implement this! + # def chomp(suffix : String) + # if suffix.bytesize == 1 + # chomp(suffix.to_unsafe[0].unsafe_chr) + # elsif ends_with?(suffix) + # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) + # else + # self + # end + # end + + # Returns a new `String` with the first char removed from it. + # Applying lchop to an empty string returns an empty string. + # + # ``` + # "hello".lchop # => "ello" + # "".lchop # => "" + # ``` + # TODO: Implement this! + # def lchop + # return "" if empty? + + # reader = Char::Reader.new(self) + # unsafe_byte_slice_string(reader.current_char_width, bytesize - reader.current_char_width) + # end + + # Returns a new `String` with *prefix* removed from the beginning of the string. + # + # ``` + # "hello".lchop('h') # => "ello" + # "hello".lchop('g') # => "hello" + # ``` + # TODO: Implement this! + # def lchop(prefix : Char) + # if starts_with?(prefix) + # unsafe_byte_slice_string(prefix.bytesize, bytesize - prefix.bytesize) + # else + # self + # end + # end + + # Returns a new `String` with *prefix* removed from the beginning of the string. + # + # ``` + # "hello".lchop("hel") # => "lo" + # "hello".lchop("eh") # => "hello" + # ``` + # TODO: Implement this! + # def lchop(prefix : String) + # if starts_with?(prefix) + # unsafe_byte_slice_string(prefix.bytesize, bytesize - prefix.bytesize) + # else + # self + # end + # end + + # Returns a new `String` with the last character removed. + # Applying rchop to an empty string returns an empty string. + # + # ``` + # "string\r\n".rchop # => "string\r" + # "string\n\r".rchop # => "string\n" + # "string\n".rchop # => "string" + # "string".rchop # => "strin" + # "x".rchop.rchop # => "" + # ``` + # TODO: Implement this! + # def rchop + # return "" if bytesize <= 1 + + # if to_unsafe[bytesize - 1] < 128 || ascii_only? + # return unsafe_byte_slice_string(0, bytesize - 1) + # end + + # self[0, size - 1] + # end + + # Returns a new `String` with *suffix* removed from the end of the string. + # + # ``` + # "string".rchop('g') # => "strin" + # "string".rchop('x') # => "string" + # ``` + # TODO: Implement this! + # def rchop(suffix : Char) + # return "" if empty? + + # if ends_with?(suffix) + # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) + # else + # self + # end + # end + + # Returns a new `String` with *suffix* removed from the end of the string. + # + # ``` + # "string".rchop("ing") # => "str" + # "string".rchop("inx") # => "string" + # ``` + # TODO: Implement this! + # def rchop(suffix : String) + # return "" if empty? + + # if ends_with?(suffix) + # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) + # else + # self + # end + # end + + # Returns a slice of bytes containing this string encoded in the given encoding. + # + # The *invalid* argument can be: + # * `nil`: an exception is raised on invalid byte sequences + # * `:skip`: invalid byte sequences are ignored + # + # ``` + # "好".encode("GB2312") # => Bytes[186, 195] + # "好".bytes # => [229, 165, 189] + # ``` + # TODO: Implement this! + # def encode(encoding : String, invalid : Symbol? = nil) : Bytes + # io = IO::Memory.new + # String.encode(to_slice, "UTF-8", encoding, io, invalid) + # io.to_slice + # end + + # :nodoc: + # TODO: Implement this! + # protected def self.encode(slice, from, to, io, invalid) + # IO::EncodingOptions.check_invalid(invalid) + + # inbuf_ptr = slice.to_unsafe + # inbytesleft = LibC::SizeT.new(slice.size) + # outbuf = uninitialized UInt8[1024] + + # Iconv.new(from, to, invalid) do |iconv| + # while inbytesleft > 0 + # outbuf_ptr = outbuf.to_unsafe + # outbytesleft = LibC::SizeT.new(outbuf.size) + # err = iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft)) + # if err == -1 + # iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft)) + # end + # io.write(outbuf.to_slice[0, outbuf.size - outbytesleft]) + # end + # end + # end + + # Interprets this string as containing a sequence of hexadecimal values + # and decodes it as a slice of bytes. Two consecutive bytes in the string + # represent a byte in the returned slice. + # + # Raises `ArgumentError` if this string does not denote an hexstring. + # + # ``` + # "0102031aff".hexbytes # => Bytes[1, 2, 3, 26, 255] + # "1".hexbytes # raises ArgumentError + # "hello world".hexbytes # raises ArgumentError + # ``` + # TODO: Implement this! + # def hexbytes : Bytes + # hexbytes? || raise(ArgumentError.new("#{self} is not a hexstring")) + # end + + # Interprets this string as containing a sequence of hexadecimal values + # and decodes it as a slice of bytes. Two consecutive bytes in the string + # represent a byte in the returned slice. + # + # Returns `nil` if this string does not denote an hexstring. + # + # ``` + # "0102031aff".hexbytes? # => Bytes[1, 2, 3, 26, 255] + # "1".hexbytes? # => nil + # "hello world".hexbytes? # => nil + # ``` + # TODO: Implement this! + # def hexbytes? : Bytes? + # return unless bytesize.divisible_by?(2) + + # bytes = Bytes.new(bytesize / 2) + + # i = 0 + # while i < bytesize + # high_nibble = to_unsafe[i].unsafe_chr.to_u8?(16) + # low_nibble = to_unsafe[i + 1].unsafe_chr.to_u8?(16) + # return unless high_nibble && low_nibble + + # bytes[i / 2] = (high_nibble << 4) | low_nibble + # i += 2 + # end + + # bytes + # end + + # Returns a new `String` that results of inserting *other* in `self` at *index*. + # Negative indices count from the end of the string, and insert **after** + # the given index. + # + # Raises `IndexError` if the index is out of bounds. + # + # ``` + # "abcd".insert(0, 'X') # => "Xabcd" + # "abcd".insert(3, 'X') # => "abcXd" + # "abcd".insert(4, 'X') # => "abcdX" + # "abcd".insert(-3, 'X') # => "abXcd" + # "abcd".insert(-1, 'X') # => "abcdX" + # + # "abcd".insert(5, 'X') # raises IndexError + # "abcd".insert(-6, 'X') # raises IndexError + # ``` + # TODO: Implement this! + # def insert(index : Int, other : Char) + # index = index.to_i + # index += size + 1 if index < 0 + + # byte_index = char_index_to_byte_index(index) + # raise IndexError.new unless byte_index + + # bytes, count = String.char_bytes_and_bytesize(other) + + # new_bytesize = bytesize + count + # new_size = (ascii_only? && other.ascii?) ? new_bytesize : 0 + + # insert_impl(byte_index, bytes.to_unsafe, count, new_bytesize, new_size) + # end + + # Returns a new `String` that results of inserting *other* in `self` at *index*. + # Negative indices count from the end of the string, and insert **after** + # the given index. + # + # Raises `IndexError` if the index is out of bounds. + # + # ``` + # "abcd".insert(0, "FOO") # => "FOOabcd" + # "abcd".insert(3, "FOO") # => "abcFOOd" + # "abcd".insert(4, "FOO") # => "abcdFOO" + # "abcd".insert(-3, "FOO") # => "abFOOcd" + # "abcd".insert(-1, "FOO") # => "abcdFOO" + # + # "abcd".insert(5, "FOO") # raises IndexError + # "abcd".insert(-6, "FOO") # raises IndexError + # ``` + # TODO: Implement this! + # def insert(index : Int, other : String) + # index = index.to_i + # index += size + 1 if index < 0 + + # byte_index = char_index_to_byte_index(index) + # raise IndexError.new unless byte_index + + # new_bytesize = bytesize + other.bytesize + # new_size = ascii_only? && other.ascii_only? ? new_bytesize : 0 + + # insert_impl(byte_index, other.to_unsafe, other.bytesize, new_bytesize, new_size) + # end + # TODO: Implement this! + # private def insert_impl(byte_index, other, other_bytesize, new_bytesize, new_size) + # String.new(new_bytesize) do |buffer| + # buffer.copy_from(to_unsafe, byte_index) + # buffer += byte_index + # buffer.copy_from(other, other_bytesize) + # buffer += other_bytesize + # buffer.copy_from(to_unsafe + byte_index, bytesize - byte_index) + # {new_bytesize, new_size} + # end + # end + + # Returns a new `String` with leading and trailing whitespace removed. + # + # ``` + # " hello ".strip # => "hello" + # "\tgoodbye\r\n".strip # => "goodbye" + # ``` + # TODO: Implement this! + # def strip + # excess_left = calc_excess_left + # if excess_left == bytesize + # return "" + # end + + # excess_right = calc_excess_right + # remove_excess(excess_left, excess_right) + # end + + # Returns a new string where leading and trailing occurrences of *char* are removed. + # + # ``` + # "aaabcdaaa".strip('a') # => "bcd" + # ``` + # TODO: Implement this! + # def strip(char : Char) + # return self if empty? + + # excess_left = calc_excess_left(char) + # if excess_left == bytesize + # return "" + # end + + # excess_right = calc_excess_right(char) + # remove_excess(excess_left, excess_right) + # end + + # Returns a new string where leading and trailing occurrences of any char + # in *chars* are removed. The *chars* argument is not a prefix or suffix; + # rather; all combinations of its values are stripped. + # + # ``` + # "abcdefcba".strip("abc") # => "def" + # ``` + # TODO: Implement this! + # def strip(chars : String) + # return self if empty? + + # case chars.size + # when 0 + # return self + # when 1 + # return strip(chars[0]) + # end + + # excess_left = calc_excess_left(chars) + # if excess_left == bytesize + # return "" + # end + + # excess_right = calc_excess_right(chars) + # remove_excess(excess_left, excess_right) + # end + + # Returns a new string where leading and trailing characters for which + # the block returns a *truthy* value are removed. + # + # ``` + # "bcadefcba".strip { |c| 'a' <= c <= 'c' } # => "def" + # ``` + # TODO: Implement this! + # def strip(&block : Char -> _) + # return self if empty? + + # excess_left = calc_excess_left { |c| yield c } + # if excess_left == bytesize + # return "" + # end + + # excess_right = calc_excess_right { |c| yield c } + # remove_excess(excess_left, excess_right) + # end + + # Returns a new `String` with trailing whitespace removed. + # + # ``` + # " hello ".rstrip # => " hello" + # "\tgoodbye\r\n".rstrip # => "\tgoodbye" + # ``` + # TODO: Implement this! + # def rstrip + # remove_excess_right(calc_excess_right) + # end + + # Returns a new string with trailing occurrences of *char* removed. + # + # ``` + # "aaabcdaaa".rstrip('a') # => "aaabcd" + # ``` + # TODO: Implement this! + # def rstrip(char : Char) + # return self if empty? + + # remove_excess_right(calc_excess_right(char)) + # end + + # Returns a new string where trailing occurrences of any char + # in *chars* are removed. The *chars* argument is not a suffix; + # rather; all combinations of its values are stripped. + # + # ``` + # "abcdefcba".rstrip("abc") # => "abcdef" + # ``` + # TODO: Implement this! + # def rstrip(chars : String) + # return self if empty? + + # case chars.size + # when 0 + # self + # when 1 + # rstrip(chars[0]) + # else + # remove_excess_right(calc_excess_right(chars)) + # end + # end + + # Returns a new string where trailing characters for which + # the block returns a *truthy* value are removed. + # + # ``` + # "bcadefcba".rstrip { |c| 'a' <= c <= 'c' } # => "bcadef" + # ``` + # TODO: Implement this! + # def rstrip(&block : Char -> _) + # return self if empty? + + # excess_right = calc_excess_right { |c| yield c } + # remove_excess_right(excess_right) + # end + + # Returns a new `String` with leading whitespace removed. + # + # ``` + # " hello ".lstrip # => "hello " + # "\tgoodbye\r\n".lstrip # => "goodbye\r\n" + # ``` + # TODO: Implement this! + # def lstrip + # remove_excess_left(calc_excess_left) + # end + + # Returns a new string with leading occurrences of *char* removed. + # + # ``` + # "aaabcdaaa".lstrip('a') # => "bcdaaa" + # ``` + # TODO: Implement this! + # def lstrip(char : Char) + # return self if empty? + + # remove_excess_left(calc_excess_left(char)) + # end + + # Returns a new string where leading occurrences of any char + # in *chars* are removed. The *chars* argument is not a suffix; + # rather; all combinations of its values are stripped. + # + # ``` + # "bcadefcba".lstrip("abc") # => "defcba" + # ``` + # TODO: Implement this! + # def lstrip(chars : String) + # return self if empty? + + # case chars.size + # when 0 + # self + # when 1 + # lstrip(chars[0]) + # else + # remove_excess_left(calc_excess_left(chars)) + # end + # end + + # Returns a new string where leading characters for which + # the block returns a *truthy* value are removed. + # + # ``` + # "bcadefcba".lstrip { |c| 'a' <= c <= 'c' } # => "defcba" + # ``` + # TODO: Implement this! + # def lstrip(&block : Char -> _) + # return self if empty? + + # excess_left = calc_excess_left { |c| yield c } + # remove_excess_left(excess_left) + # end + # TODO: Implement these! + # private def calc_excess_right + # i = bytesize - 1 + # while i >= 0 && to_unsafe[i].unsafe_chr.ascii_whitespace? + # i -= 1 + # end + # bytesize - 1 - i + # end + + # private def calc_excess_right(char : Char) + # calc_excess_right do |reader_char| + # char == reader_char + # end + # end + + # private def calc_excess_right(chars : String) + # calc_excess_right do |reader_char| + # chars.includes?(reader_char) + # end + # end + + # private def calc_excess_right(&block) + # byte_index = bytesize + # reader = Char::Reader.new(at_end: self) + # while (yield reader.current_char) + # byte_index = reader.pos + # if byte_index == 0 + # return bytesize + # else + # reader.previous_char + # end + # end + # bytesize - byte_index + # end + # TODO: Implement these! + # private def calc_excess_left + # excess_left = 0 + # # All strings end with '\0', and it's not a whitespace + # # so it's safe to access past 1 byte beyond the string data + # while to_unsafe[excess_left].unsafe_chr.ascii_whitespace? + # excess_left += 1 + # end + # excess_left + # end + + # private def calc_excess_left(char : Char) + # calc_excess_left do |reader_char| + # char == reader_char + # end + # end + + # private def calc_excess_left(chars : String) + # calc_excess_left do |reader_char| + # chars.includes?(reader_char) + # end + # end + + # private def calc_excess_left(&block) + # reader = Char::Reader.new(self) + # while (yield reader.current_char) + # reader.next_char + # return bytesize unless reader.has_next? + # end + # reader.pos + # end + # TODO: Implement these! + # private def remove_excess(excess_left, excess_right) + # if excess_right == 0 && excess_left == 0 + # self + # else + # unsafe_byte_slice_string(excess_left, bytesize - excess_right - excess_left) + # end + # end + + # private def remove_excess_right(excess_right) + # case excess_right + # when 0 + # self + # when bytesize + # "" + # else + # unsafe_byte_slice_string(0, bytesize - excess_right) + # end + # end + + # private def remove_excess_left(excess_left) + # case excess_left + # when 0 + # self + # when bytesize + # "" + # else + # unsafe_byte_slice_string(excess_left) + # end + # end + + # Returns a new string _tr_anslating characters using *from* and *to* as a + # map. If *to* is shorter than *from*, the last character in *to* is used for + # the rest. If *to* is empty, this acts like `String#delete`. + # + # ``` + # "aabbcc".tr("abc", "xyz") # => "xxyyzz" + # "aabbcc".tr("abc", "x") # => "xxxxxx" + # "aabbcc".tr("a", "xyz") # => "xxbbcc" + # ``` + # TODO: Implement this! + # def tr(from : String, to : String) + # return delete(from) if to.empty? + # multi = nil + # table = StaticArray(Int32, 256).new(-1) + # reader = Char::Reader.new(to) + # char = reader.current_char + # next_char = reader.next_char + # from.each_char do |ch| + # if ch.ord >= 256 + # multi ||= {} of Char => Char + # multi[ch] = char + # else + # table[ch.ord] = char.ord + # end + # if next_char != Char::ZERO + # char = next_char + # reader.next_char + # next_char = reader.current_char + # end + # end + + # String.build(bytesize) do |buffer| + # each_char do |ch| + # if ch.ord < 256 + # if (a = table[ch.ord]) >= 0 + # buffer << a.unsafe_chr + # else + # buffer << ch + # end + # else + # if a = multi.try &.[ch]? + # buffer << a + # else + # buffer << ch + # end + # end + # end + # end + # end + + # Returns a new `String` where the first character is yielded to the given + # block and replaced by its return value. + # + # ``` + # "hello".sub { |char| char + 1 } # => "iello" + # "hello".sub { "hi" } # => "hiello" + # ``` + # TODO: Implement this! + # def sub(&block : Char -> _) + # return self if empty? + + # String.build(bytesize) do |buffer| + # reader = Char::Reader.new(self) + # buffer << yield reader.current_char + # reader.next_char + # buffer.write unsafe_byte_slice(reader.pos) + # end + # end + + # Returns a `String` where the first occurrence of *char* is replaced by + # *replacement*. + # + # ``` + # "hello".sub('l', "lo") # => "helolo" + # "hello world".sub('o', 'a') # => "hella world" + # ``` + # TODO: Implement this! + # def sub(char : Char, replacement) + # if includes?(char) + # String.build(bytesize) do |buffer| + # reader = Char::Reader.new(self) + # while reader.has_next? + # if reader.current_char == char + # buffer << replacement + # break + # else + # buffer << reader.current_char + # end + # reader.next_char + # end + # reader.next_char + # buffer.write unsafe_byte_slice(reader.pos) + # end + # else + # self + # end + # end + + # Returns a `String` where the first occurrence of *pattern* is replaced by + # the block's return value. + # + # ``` + # "hello".sub(/./) { |s| s[0].ord.to_s + ' ' } # => "104 ello" + # ``` + # TODO: Implement this! + # def sub(pattern : Regex) + # sub_append(pattern) do |str, match, buffer| + # $~ = match + # buffer << yield str, match + # end + # end + + # Returns a `String` where the first occurrence of *pattern* is replaced by + # *replacement* + # + # ``` + # "hello".sub(/[aeiou]/, "*") # => "h*llo" + # ``` + # + # Within *replacement*, the special match variable `$~` will not refer to the + # current match. + # + # If *backreferences* is `true` (the default value), *replacement* can include backreferences: + # + # ``` + # "hello".sub(/[aeiou]/, "(\\0)") # => "h(e)llo" + # ``` + # + # When substitution is performed, any backreferences found in *replacement* + # will be replaced with the contents of the corresponding capture group in + # *pattern*. Backreferences to capture groups that were not present in + # *pattern* or that did not match will be skipped. See `Regex` for information + # about capture groups. + # + # Backreferences are expressed in the form `"\\d"`, where *d* is a group + # number, or `"\\k<name>"` where *name* is the name of a named capture group. + # A sequence of literal characters resembling a backreference can be + # expressed by placing `"\\"` before the sequence. + # + # ``` + # "foo".sub(/o/, "x\\0x") # => "fxoxo" + # "foofoo".sub(/(?oo)/, "|\\k|") # => "f|oo|foo" + # "foo".sub(/o/, "\\\\0") # => "f\\0o" + # ``` + # + # Raises `ArgumentError` if an incomplete named back-reference is present in + # *replacement*. + # + # Raises `IndexError` if a named group referenced in *replacement* is not present + # in *pattern*. + # TODO: Implement this! + # def sub(pattern : Regex, replacement, backreferences = true) + # if backreferences && replacement.is_a?(String) && replacement.has_back_references? + # sub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } + # else + # sub(pattern) { replacement } + # end + # end + + # Returns a `String` where the first occurrences of the given *pattern* is replaced + # with the matching entry from the *hash* of replacements. If the first match + # is not included in the *hash*, nothing is replaced. + # + # ``` + # "hello".sub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "hallo" + # "hello".sub(/(he|l|o)/, {"l": "la"}) # => "hello" + # ``` + # TODO: Implement this! + # def sub(pattern : Regex, hash : Hash(String, _) | NamedTuple) + # sub(pattern) do |match| + # if hash.has_key?(match) + # hash[match] + # else + # return self + # end + # end + # end + + # Returns a `String` where the first occurrences of the given *string* is replaced + # with the given *replacement*. + # + # ``` + # "hello yellow".sub("ll", "dd") # => "heddo yellow" + # ``` + # TODO: Implement this! + # def sub(string : String, replacement) + # sub(string) { replacement } + # end + + # Returns a `String` where the first occurrences of the given *string* is replaced + # with the block's value. + # + # ``` + # "hello yellow".sub("ll") { "dd" } # => "heddo yellow" + # ``` + # TODO: Implement this! + # def sub(string : String, &block) + # index = self.byte_index(string) + # return self unless index + + # String.build(bytesize) do |buffer| + # buffer.write unsafe_byte_slice(0, index) + # buffer << yield string + # buffer.write unsafe_byte_slice(index + string.bytesize) + # end + # end + + # Returns a `String` where the first char in the string matching a key in the + # given *hash* is replaced by the corresponding hash value. + # + # ``` + # "hello".sub({'a' => 'b', 'l' => 'd'}) # => "hedlo" + # ``` + # TODO: Implement this! + # def sub(hash : Hash(Char, _)) + # return self if empty? + + # String.build(bytesize) do |buffer| + # reader = Char::Reader.new(self) + # while reader.has_next? + # if hash.has_key?(reader.current_char) + # buffer << hash[reader.current_char] + # reader.next_char + # break + # else + # buffer << reader.current_char + # reader.next_char + # end + # end + + # buffer << reader.current_char + + # if reader.has_next? + # reader.next_char + # buffer.write unsafe_byte_slice(reader.pos) + # end + # end + # end + + # Returns a new `String` with the character at the given index + # replaced by *replacement*. + # + # ``` + # "hello".sub(1, 'a') # => "hallo" + # ``` + # TODO: Implement this! + # def sub(index : Int, replacement : Char) + # sub_index(index.to_i, replacement) do |buffer| + # replacement.each_byte do |byte| + # buffer.value = byte + # buffer += 1 + # end + # {buffer, @length} + # end + # end + + # Returns a new `String` with the character at the given index + # replaced by *replacement*. + # + # ``` + # "hello".sub(1, "eee") # => "heeello" + # ``` + # TODO: Implement this! + # def sub(index : Int, replacement : String) + # sub_index(index.to_i, replacement) do |buffer| + # buffer.copy_from(replacement.to_unsafe, replacement.bytesize) + # buffer += replacement.bytesize + # {buffer, self.size_known? && replacement.size_known? ? self.size + replacement.size - 1 : 0} + # end + # end + # TODO: Implement this! + # private def sub_index(index, replacement) + # index += size + 1 if index < 0 + + # byte_index = char_index_to_byte_index(index) + # raise IndexError.new unless byte_index + + # reader = Char::Reader.new(self, pos: byte_index) + # width = reader.current_char_width + # replacement_width = replacement.bytesize + # new_bytesize = bytesize - width + replacement_width + + # String.new(new_bytesize) do |buffer| + # buffer.copy_from(to_unsafe, byte_index) + # buffer += byte_index + # buffer, length = yield buffer + # buffer.copy_from(to_unsafe + byte_index + width, bytesize - byte_index - width) + # {new_bytesize, length} + # end + # end + + # Returns a new `String` with characters at the given range + # replaced by *replacement*. + # + # ``` + # "hello".sub(1..2, 'a') # => "halo" + # ``` + # TODO: Implement this! + # def sub(range : Range(Int, Int), replacement : Char) + # sub_range(range, replacement) do |buffer, from_index, to_index| + # replacement.each_byte do |byte| + # buffer.value = byte + # buffer += 1 + # end + # {buffer, ascii_only? ? bytesize - (to_index - from_index) + 1 : 0} + # end + # end + + # Returns a new `String` with characters at the given range + # replaced by *replacement*. + # + # ``` + # "hello".sub(1..2, "eee") # => "heeelo" + # ``` + # TODO: Implement this! + # def sub(range : Range(Int, Int), replacement : String) + # sub_range(range, replacement) do |buffer| + # buffer.copy_from(replacement.to_unsafe, replacement.bytesize) + # buffer += replacement.bytesize + # {buffer, 0} + # end + # end + # TODO: Implement this! + # private def sub_range(range, replacement) + # from, size = Indexable.range_to_index_and_count(range, self.size) + + # from_index = char_index_to_byte_index(from) + # raise IndexError.new unless from_index + + # if size == 0 + # to_index = from_index + # else + # to_index = char_index_to_byte_index(from + size) + # raise IndexError.new unless to_index + # end + + # new_bytesize = bytesize - (to_index - from_index) + replacement.bytesize + + # String.new(new_bytesize) do |buffer| + # buffer.copy_from(to_unsafe, from_index) + # buffer += from_index + # buffer, length = yield buffer, from_index, to_index + # buffer.copy_from(to_unsafe + to_index, bytesize - to_index) + # {new_bytesize, length} + # end + # end + + # This returns `true` if this string has `'\\'` in it. It might not be a back reference, + # but `'\\'` is probably used for back references, so this check is faster than parsing + # the whole thing. + # TODO: Implement this! + # def has_back_references? + # to_slice.index('\\'.ord.to_u8) + # end + # TODO: Implement this! + # private def scan_backreferences(replacement, match_data, buffer) + # # We only append to the buffer in chunks, so if we have "foo\\1", we remember that + # # the chunk starts at index 0 (first_index) and when we find a "\\" we append + # # from 0 to 3 in a single write. When we find a "\\0" or "\\k<...>", we append + # # from the first_index, process the backreference, and then reset first_index + # # to the new index. + # first_index = 0 + # index = 0 + + # while index = replacement.byte_index('\\'.ord.to_u8, index) + # index += 1 + # chr = replacement.to_unsafe[index].unsafe_chr + # case chr + # when '\\' + # buffer.write(replacement.unsafe_byte_slice(first_index, index - first_index)) + # index += 1 + # first_index = index + # when '0'..'9' + # buffer.write(replacement.unsafe_byte_slice(first_index, index - 1 - first_index)) + # buffer << match_data[chr - '0']? + # index += 1 + # first_index = index + # when 'k' + # index += 1 + # chr = replacement.to_unsafe[index].unsafe_chr + # next unless chr == '<' + + # buffer.write(replacement.unsafe_byte_slice(first_index, index - 2 - first_index)) + + # index += 1 + # start_index = index + # end_index = replacement.byte_index('>'.ord.to_u8, start_index) + # raise ArgumentError.new("Missing ending '>' for '\\\\k<...'") unless end_index + + # name = replacement.byte_slice(start_index, end_index - start_index) + # capture = match_data[name]? + # raise IndexError.new("Undefined group name reference: #{name.inspect}") unless capture + + # buffer << capture + # index = end_index + 1 + # first_index = index + # end + # end + + # if first_index != replacement.bytesize + # buffer.write(replacement.unsafe_byte_slice(first_index)) + # end + # end + + # Returns a `String` where each character yielded to the given block + # is replaced by the block's return value. + # + # ``` + # "hello".gsub { |char| char + 1 } # => "ifmmp" + # "hello".gsub { "hi" } # => "hihihihihi" + # ``` + # TODO: Implement this! + # def gsub(&block : Char -> _) + # String.build(bytesize) do |buffer| + # each_char do |my_char| + # buffer << yield my_char + # end + # end + # end + + # Returns a `String` where all occurrences of the given char are + # replaced with the given *replacement*. + # + # ``` + # "hello".gsub('l', "lo") # => "heloloo" + # "hello world".gsub('o', 'a') # => "hella warld" + # ``` + # TODO: Implement this! + # def gsub(char : Char, replacement) + # if includes?(char) + # gsub { |my_char| char == my_char ? replacement : my_char } + # else + # self + # end + # end + + # Returns a `String` where all occurrences of the given *pattern* are replaced + # by the block value's value. + # + # ``` + # "hello".gsub(/./) { |s| s[0].ord.to_s + ' ' } # => "104 101 108 108 111 " + # ``` + # TODO: Implement this! + # def gsub(pattern : Regex) + # gsub_append(pattern) do |string, match, buffer| + # $~ = match + # buffer << yield string, match + # end + # end + + # Returns a `String` where all occurrences of the given *pattern* are replaced + # with the given *replacement*. + # + # ``` + # "hello".gsub(/[aeiou]/, '*') # => "h*ll*" + # ``` + # + # Within *replacement*, the special match variable `$~` will not refer to the + # current match. + # + # If *backreferences* is `true` (the default value), *replacement* can include backreferences: + # + # ``` + # "hello".gsub(/[aeiou]/, "(\\0)") # => "h(e)ll(o)" + # ``` + # + # When substitution is performed, any backreferences found in *replacement* + # will be replaced with the contents of the corresponding capture group in + # *pattern*. Backreferences to capture groups that were not present in + # *pattern* or that did not match will be skipped. See `Regex` for information + # about capture groups. + # + # Backreferences are expressed in the form `"\\d"`, where *d* is a group + # number, or `"\\k"` where *name* is the name of a named capture group. + # A sequence of literal characters resembling a backreference can be + # expressed by placing `"\\"` before the sequence. + # + # ``` + # "foo".gsub(/o/, "x\\0x") # => "fxoxxox" + # "foofoo".gsub(/(?oo)/, "|\\k|") # => "f|oo|f|oo|" + # "foo".gsub(/o/, "\\\\0") # => "f\\0\\0" + # ``` + # + # Raises `ArgumentError` if an incomplete named back-reference is present in + # *replacement*. + # + # Raises `IndexError` if a named group referenced in *replacement* is not present + # in *pattern*. + # TODO: Implement this! + # def gsub(pattern : Regex, replacement, backreferences = true) + # if backreferences && replacement.is_a?(String) && replacement.has_back_references? + # gsub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } + # else + # gsub(pattern) { replacement } + # end + # end + + # Returns a `String` where all occurrences of the given *pattern* are replaced + # with a *hash* of replacements. If the *hash* contains the matched pattern, + # the corresponding value is used as a replacement. Otherwise the match is + # not included in the returned string. + # + # ``` + # # "he" and "l" are matched and replaced, + # # but "o" is not and so is not included + # "hello".gsub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "halala" + # ``` + # Implement this! + # def gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple) + # gsub(pattern) do |match| + # hash[match]? + # end + # end + + # Returns a `String` where all occurrences of the given *string* are replaced + # with the given *replacement*. + # + # ``` + # "hello yellow".gsub("ll", "dd") # => "heddo yeddow" + # ``` + # TODO: Implement this! + # def gsub(string : String, replacement) + # gsub(string) { replacement } + # end + + # Returns a `String` where all occurrences of the given *string* are replaced + # with the block's value. + # + # ``` + # "hello yellow".gsub("ll") { "dd" } # => "heddo yeddow" + # ``` + # TODO: Implement this! + # def gsub(string : String, &block) + # byte_offset = 0 + # index = self.byte_index(string, byte_offset) + # return self unless index + + # last_byte_offset = 0 + + # String.build(bytesize) do |buffer| + # while index + # buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset) + # buffer << yield string + + # if string.bytesize == 0 + # byte_offset = index + 1 + # last_byte_offset = index + # else + # byte_offset = index + string.bytesize + # last_byte_offset = byte_offset + # end + + # index = self.byte_index(string, byte_offset) + # end + + # if last_byte_offset < bytesize + # buffer.write unsafe_byte_slice(last_byte_offset) + # end + # end + # end + + # Returns a `String` where all chars in the given hash are replaced + # by the corresponding *hash* values. + # + # ``` + # "hello".gsub({'e' => 'a', 'l' => 'd'}) # => "haddo" + # ``` + # TODO: Implement this! + # def gsub(hash : Hash(Char, _)) + # gsub do |char| + # hash[char]? || char + # end + # end + + # Returns a `String` where all chars in the given named tuple are replaced + # by the corresponding *tuple* values. + # + # ``` + # "hello".gsub({e: 'a', l: 'd'}) # => "haddo" + # ``` + # TODO: Implement this! + # def gsub(tuple : NamedTuple) + # gsub do |char| + # tuple[char.to_s]? || char + # end + # end + # TODO: Implement this! + # private def gsub_append(pattern : Regex) + # byte_offset = 0 + # match = pattern.match_at_byte_index(self, byte_offset) + # return self unless match + + # last_byte_offset = 0 + + # String.build(bytesize) do |buffer| + # while match + # index = match.byte_begin(0) + + # buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset) + # str = match[0] + # $~ = match + # yield str, match, buffer + + # if str.bytesize == 0 + # byte_offset = index + 1 + # last_byte_offset = index + # else + # byte_offset = index + str.bytesize + # last_byte_offset = byte_offset + # end + + # match = pattern.match_at_byte_index(self, byte_offset) + # end + + # if last_byte_offset < bytesize + # buffer.write unsafe_byte_slice(last_byte_offset) + # end + # end + # end + + # Yields each char in this string to the block, + # returns the number of times the block returned a truthy value. + # + # ``` + # "aabbcc".count { |c| ['a', 'b'].includes?(c) } # => 4 + # ``` + # TODO: Implement this! + # def count + # count = 0 + # each_char do |char| + # count += 1 if yield char + # end + # count + # end + + # Counts the occurrences of *other* char in this string. + # + # ``` + # "aabbcc".count('a') # => 2 + # ``` + # TODO: Implement this! + # def count(other : Char) + # count { |char| char == other } + # end + + # Sets should be a list of strings following the rules + # described at `Char#in_set?`. Returns the number of characters + # in this string that match the given set. + # TODO: Implement this! + # def count(*sets) + # count { |char| char.in_set?(*sets) } + # end + + # Yields each char in this string to the block. + # Returns a new `String` with all characters for which the + # block returned a truthy value removed. + # + # ``` + # "aabbcc".delete { |c| ['a', 'b'].includes?(c) } # => "cc" + # ``` + # TODO: Implement this! + # def delete + # String.build(bytesize) do |buffer| + # each_char do |char| + # buffer << char unless yield char + # end + # end + # end + + # Returns a new `String` with all occurrences of *char* removed. + # + # ``` + # "aabbcc".delete('b') # => "aacc" + # ``` + # TODO: Implement this! + # def delete(char : Char) + # delete { |my_char| my_char == char } + # end + + # Sets should be a list of strings following the rules + # described at `Char#in_set?`. Returns a new `String` with + # all characters that match the given set removed. + # + # ``` + # "aabbccdd".delete("a-c") # => "dd" + # ``` + # TODO: Implement this! + # def delete(*sets) + # delete { |char| char.in_set?(*sets) } + # end + + # Yields each char in this string to the block. + # Returns a new `String`, that has all characters removed, + # that were the same as the previous one and for which the given + # block returned a truthy value. + # + # ``` + # "aaabbbccc".squeeze { |c| ['a', 'b'].includes?(c) } # => "abccc" + # "aaabbbccc".squeeze { |c| ['a', 'c'].includes?(c) } # => "abbbc" + # ``` + # TODO: Implement this! + # def squeeze + # previous = nil + # String.build(bytesize) do |buffer| + # each_char do |char| + # buffer << char unless yield(char) && previous == char + # previous = char + # end + # end + # end + + # Returns a new `String`, with all runs of char replaced by one instance. + # + # ``` + # "a bbb".squeeze(' ') # => "a bbb" + # ``` + # TODO: Implement this! + # def squeeze(char : Char) + # squeeze { |my_char| char == my_char } + # end + + # Sets should be a list of strings following the rules + # described at `Char#in_set?`. Returns a new `String` with all + # runs of the same character replaced by one instance, if + # they match the given set. + # + # If no set is given, all characters are matched. + # + # ``` + # "aaabbbcccddd".squeeze("b-d") # => "aaabcd" + # "a bbb".squeeze # => "a b" + # ``` + # TODO: Implement this! + # def squeeze(*sets : String) + # squeeze { |char| char.in_set?(*sets) } + # end + + # Returns a new `String`, that has all characters removed, + # that were the same as the previous one. + # + # ``` + # "a bbb".squeeze # => "a b" + # ``` + # TODO: Implement this! + # def squeeze + # squeeze { true } + # end + + # Returns `true` if this is the empty string, `""`. + def empty? + bytesize == 0 + end + + # Returns `true` if this string consists exclusively of unicode whitespace. + # + # ``` + # "".blank? # => true + # " ".blank? # => true + # " a ".blank? # => false + # ``` + # TODO: Implement this! + # def blank? + # each_char do |char| + # return false unless char.whitespace? + # end + # true + # end + # TODO: Implement this! + # def ==(other : self) + # return true if same?(other) + # return false unless bytesize == other.bytesize + # to_unsafe.memcmp(other.to_unsafe, bytesize) == 0 + # end + + # Compares this string with *other*, returning `-1`, `0` or `+1` depending on whether + # this string is less, equal or greater than *other*. + # + # Comparison is done byte-per-byte: if a byte is less then the other corresponding + # byte, `-1` is returned and so on. + # + # If the strings are of different lengths, and the strings are equal when compared + # up to the shortest length, then the longer string is considered greater than + # the shorter one. + # + # ``` + # "abcdef" <=> "abcde" # => 1 + # "abcdef" <=> "abcdef" # => 0 + # "abcdef" <=> "abcdefg" # => -1 + # "abcdef" <=> "ABCDEF" # => 1 + # ``` + # TODO: Implement this! + # def <=>(other : self) + # return 0 if same?(other) + # min_bytesize = Math.min(bytesize, other.bytesize) + + # cmp = to_unsafe.memcmp(other.to_unsafe, min_bytesize) + # cmp == 0 ? (bytesize <=> other.bytesize) : cmp.sign + # end + + # Compares this string with *other*, returning `-1`, `0` or `+1` depending on whether + # this string is less, equal or greater than *other*, optionally in a *case_insensitive* + # manner. + # + # If *case_insitive* is `false`, this method delegates to `<=>`. Otherwise, + # the strings are compared char-by-char, and ASCII characters are compared in a + # case-insensitive way. + # + # ``` + # "abcdef".compare("abcde") # => 1 + # "abcdef".compare("abcdef") # => 0 + # "abcdef".compare("abcdefg") # => -1 + # "abcdef".compare("ABCDEF") # => 1 + # + # "abcdef".compare("ABCDEF", case_insensitive: true) # => 0 + # "abcdef".compare("ABCDEG", case_insensitive: true) # => -1 + # ``` + # TODO: Implement this! + # def compare(other : String, case_insensitive = false) + # return self <=> other unless case_insensitive + + # reader1 = Char::Reader.new(self) + # reader2 = Char::Reader.new(other) + # ch1 = reader1.current_char + # ch2 = reader2.current_char + + # while reader1.has_next? && reader2.has_next? + # cmp = ch1.downcase <=> ch2.downcase + # return cmp.sign if cmp != 0 + + # ch1 = reader1.next_char + # ch2 = reader2.next_char + # end + + # if reader1.has_next? + # 1 + # elsif reader2.has_next? + # -1 + # else + # 0 + # end + # end + + # Tests whether *str* matches *regex*. + # If successful, it returns the position of the first match. + # If unsuccessful, it returns `nil`. + # + # If the argument isn't a `Regex`, it returns `nil`. + # + # ``` + # "Haystack" =~ /ay/ # => 1 + # "Haystack" =~ /z/ # => nil + # + # "Haystack" =~ 45 # => nil + # ``` + # TODO: Implement this! + # def =~(regex : Regex) + # match = regex.match(self) + # $~ = match + # match.try &.begin(0) + # end + + # ditto + # TODO: Implement this! + # def =~(other) + # nil + # end + + # Concatenates *str* and *other*. + # + # ``` + # "abc" + "def" # => "abcdef" + # "abc" + 'd' # => "abcd" + # ``` + # TODO: Implement this! + # def +(other : self) + # return self if other.empty? + # return other if self.empty? + + # size = bytesize + other.bytesize + # String.new(size) do |buffer| + # buffer.copy_from(to_unsafe, bytesize) + # (buffer + bytesize).copy_from(other.to_unsafe, other.bytesize) + + # if size_known? && other.size_known? + # {size, @length + other.@length} + # else + # {size, 0} + # end + # end + # end + + # ditto + # TODO: Implement this! + # def +(char : Char) + # bytes, count = String.char_bytes_and_bytesize(char) + # size = bytesize + count + # String.new(size) do |buffer| + # buffer.copy_from(to_unsafe, bytesize) + # (buffer + bytesize).copy_from(bytes.to_unsafe, count) + + # if size_known? + # {size, @length + 1} + # else + # {size, 0} + # end + # end + # end + + # Makes a new `String` by adding *str* to itself *times* times. + # + # ``` + # "Developers! " * 4 + # # => "Developers! Developers! Developers! Developers!" + # ``` + # TODO: Implement this! + # def *(times : Int) + # raise ArgumentError.new "Negative argument" if times < 0 + + # if times == 0 || bytesize == 0 + # return "" + # elsif bytesize == 1 + # return String.new(times) do |buffer| + # Intrinsics.memset(buffer.as(Void*), to_unsafe[0], times, 0, false) + # {times, times} + # end + # end + + # total_bytesize = bytesize * times + # String.new(total_bytesize) do |buffer| + # buffer.copy_from(to_unsafe, bytesize) + # n = bytesize + + # while n <= total_bytesize / 2 + # (buffer + n).copy_from(buffer, n) + # n *= 2 + # end + + # (buffer + n).copy_from(buffer, total_bytesize - n) + # {total_bytesize, @length * times} + # end + # end + + # Prime number constant for Rabin-Karp algorithm `String#index`. + private PRIME_RK = 2097169u32 + + # Update rolling hash for Rabin-Karp algorithm `String#index`. + # TODO: Implement this! + # private macro update_hash(n) + # {% for i in 1..n %} + # {% if i != 1 %} + # byte = head_pointer.value + # {% end %} + # hash = hash * PRIME_RK + pointer.value - pow * byte + # pointer += 1 + # head_pointer += 1 + # {% end %} + # end + + # Returns the index of *search* in the string, or `nil` if the string is not present. + # If *offset* is present, it defines the position to start the search. + # + # ``` + # "Hello, World".index('o') # => 4 + # "Hello, World".index('Z') # => nil + # "Hello, World".index("o", 5) # => 8 + # "Hello, World".index("H", 2) # => nil + # "Hello, World".index(/[ ]+/) # => 6 + # "Hello, World".index(/\d+/) # => nil + # ``` + # TODO: Implement this! + # def index(search : Char, offset = 0) + # # If it's ASCII we can delegate to slice + # if search.ascii? && ascii_only? + # return to_slice.index(search.ord.to_u8, offset) + # end + + # offset += size if offset < 0 + # return nil if offset < 0 + + # each_char_with_index do |char, i| + # if i >= offset && char == search + # return i + # end + # end + + # nil + # end + # TODO: Implement this! + # ditto + # def index(search : String, offset = 0) + # offset += size if offset < 0 + # return if offset < 0 + + # return size < offset ? nil : offset if search.empty? + + # # Rabin-Karp algorithm + # # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm + + # # calculate a rolling hash of search text (needle) + # search_hash = 0u32 + # search.each_byte do |b| + # search_hash = search_hash * PRIME_RK + b + # end + # pow = PRIME_RK ** search.bytesize + + # # Find start index with offset + # char_index = 0 + # pointer = to_unsafe + # end_pointer = pointer + bytesize + # while char_index < offset && pointer < end_pointer + # byte = pointer.value + # if byte < 0x80 + # pointer += 1 + # elsif byte < 0xe0 + # pointer += 2 + # elsif byte < 0xf0 + # pointer += 3 + # else + # pointer += 4 + # end + # char_index += 1 + # end + + # head_pointer = pointer + + # # calculate a rolling hash of this text (haystack) + # hash = 0u32 + # hash_end_pointer = pointer + search.bytesize + # return if hash_end_pointer > end_pointer + # while pointer < hash_end_pointer + # hash = hash * PRIME_RK + pointer.value + # pointer += 1 + # end + + # while true + # # check hash equality and real string equality + # if hash == search_hash && head_pointer.memcmp(search.to_unsafe, search.bytesize) == 0 + # return char_index + # end + + # return if pointer >= end_pointer + + # byte = head_pointer.value + + # # update a rolling hash of this text (heystack) + # # thanks @MaxLap for suggesting this loop reduction + # if byte < 0x80 + # update_hash 1 + # elsif byte < 0xe0 + # update_hash 2 + # elsif byte < 0xf0 + # update_hash 3 + # else + # update_hash 4 + # end + # char_index += 1 + # end + # end + + # ditto + # TODO: Implement this! + # def index(search : Regex, offset = 0) + # offset += size if offset < 0 + # return nil unless 0 <= offset <= size + + # self.match(search, offset).try &.begin + # end + + # Returns the index of the _last_ appearance of *search* in the string, + # If *offset* is present, it defines the position to _end_ the search + # (characters beyond this point are ignored). + # + # ``` + # "Hello, World".rindex('o') # => 8 + # "Hello, World".rindex('Z') # => nil + # "Hello, World".rindex("o", 5) # => 4 + # "Hello, World".rindex("W", 2) # => nil + # ``` + # TODO: Implement this! + # def rindex(search : Char, offset = size - 1) + # # If it's ASCII we can delegate to slice + # if search.ascii? && ascii_only? + # return to_slice.rindex(search.ord.to_u8, offset) + # end + + # offset += size if offset < 0 + # return nil if offset < 0 + + # if offset == size - 1 + # reader = Char::Reader.new(at_end: self) + # else + # byte_index = char_index_to_byte_index(offset) + # raise IndexError.new unless byte_index + # reader = Char::Reader.new(self, pos: byte_index) + # end + + # while true + # if reader.current_char == search + # return offset + # elsif reader.has_previous? + # reader.previous_char + # offset -= 1 + # else + # return nil + # end + # end + # end + + # ditto + # TODO: Implement this! + # def rindex(search : String, offset = size - search.size) + # offset += size if offset < 0 + # return if offset < 0 + + # # Rabin-Karp algorithm + # # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm + + # # calculate a rolling hash of search text (needle) + # search_hash = 0u32 + # search.to_slice.reverse_each do |b| + # search_hash = search_hash * PRIME_RK + b + # end + # pow = PRIME_RK ** search.bytesize + + # hash = 0u32 + # char_index = size + + # begin_pointer = to_unsafe + # pointer = begin_pointer + bytesize + # tail_pointer = pointer + # hash_begin_pointer = pointer - search.bytesize + + # return if hash_begin_pointer < begin_pointer + + # # calculate a rolling hash of this text (haystack) + # while hash_begin_pointer < pointer + # pointer -= 1 + # byte = pointer.value + # char_index -= 1 if (byte & 0xC0) != 0x80 + + # hash = hash * PRIME_RK + byte + # end + + # while true + # # check hash equality and real string equality + # if hash == search_hash && char_index <= offset && + # pointer.memcmp(search.to_unsafe, search.bytesize) == 0 + # return char_index + # end + + # return if begin_pointer == pointer + + # pointer -= 1 + # tail_pointer -= 1 + # byte = pointer.value + # char_index -= 1 if (byte & 0xC0) != 0x80 + + # # update a rolling hash of this text (haystack) + # hash = hash * PRIME_RK + byte - pow * tail_pointer.value + # end + # end + + # ditto + # TODO: Implement this! + # def rindex(search : Regex, offset = 0) + # offset += size if offset < 0 + # return nil unless 0 <= offset <= size + + # match_result = nil + # self[0, self.size - offset].scan(search) do |match_data| + # match_result = match_data + # end + + # match_result.try &.begin(0) + # end + + # Searches separator or pattern (`Regex`) in the string, and returns + # a `Tuple` with the part before it, the match, and the part after it. + # If it is not found, returns str followed by two empty strings. + # + # ``` + # "hello".partition("l") # => {"he", "l", "lo"} + # "hello".partition("x") # => {"hello", "", ""} + # ``` + # TODO: Implement this! + # def partition(search : (Char | String)) : Tuple(String, String, String) + # pre = mid = post = "" + # search_size = search.is_a?(Char) ? 1 : search.size + # case pos = self.index(search) + # when .nil? + # pre = self + # when 0 + # mid = search.to_s + # post = self[(pos + search_size)..-1] + # else + # pre = self[0..(pos - 1)] + # mid = search.to_s + # post = self[(pos + search_size)..-1] + # end + # {pre, mid, post} + # end + + # ditto + # TODO: Implement this! + # def partition(search : Regex) : Tuple(String, String, String) + # pre = mid = post = "" + # case m = self.match(search) + # when .nil? + # pre = self + # else + # pre = m.pre_match + # mid = m[0] + # post = m.post_match + # end + # {pre, mid, post} + # end + + # Searches separator or pattern (`Regex`) in the string from the end of the string, + # and returns a `Tuple` with the part before it, the match, and the part after it. + # If it is not found, returns two empty strings and str. + # + # ``` + # "hello".rpartition("l") # => {"hel", "l", "o"} + # "hello".rpartition("x") # => {"", "", "hello"} + # "hello".rpartition(/.l/) # => {"he", "ll", "o"} + # ``` + # TODO: Implement this! + # def rpartition(search : (Char | String)) : Tuple(String, String, String) + # pos = self.rindex(search) + # search_size = search.is_a?(Char) ? 1 : search.size + + # pre = mid = post = "" + + # case pos + # when .nil? + # post = self + # when 0 + # mid = search.to_s + # post = self[(pos + search_size)..-1] + # else + # pre = self[0..(pos - 1)] + # mid = search.to_s + # post = self[(pos + search_size)..-1] + # end + # {pre, mid, post} + # end + + # ditto + # TODO: Implement this! + # def rpartition(search : Regex) : Tuple(String, String, String) + # match_result = nil + # pos = self.size - 1 + + # while pos >= 0 + # self[pos..-1].scan(search) do |m| + # match_result = m + # end + # break unless match_result.nil? + # pos -= 1 + # end + + # pre = mid = post = "" + + # case + # when match_result.nil? + # post = self + # when pos == 0 + # mid = match_result[0] + # post = self[match_result[0].size..-1] + # else + # pre = self[0..pos - 1] + # mid = match_result.not_nil![0] + # post = self[pos + match_result.not_nil![0].size..-1] + # end + # {pre, mid, post} + # end + + def byte_index(byte : Int, offset = 0) + offset.upto(bytesize - 1) do |i| + if to_unsafe[i] == byte + return i + end + end + nil + end + # TODO: Implement this! + # def byte_index(string : String, offset = 0) + # offset += bytesize if offset < 0 + # return nil if offset < 0 + + # end_pos = bytesize - string.bytesize + + # offset.upto(end_pos) do |pos| + # if (to_unsafe + pos).memcmp(string.to_unsafe, string.bytesize) == 0 + # return pos + # end + # end + + # nil + # end + + # Returns the byte index of a char index, or `nil` if out of bounds. + # + # It is valid to pass `#size` to *index*, and in this case the answer + # will be the bytesize of this string. + # + # ``` + # "hello".char_index_to_byte_index(1) # => 1 + # "hello".char_index_to_byte_index(5) # => 5 + # "こんにちは".char_index_to_byte_index(1) # => 3 + # "こんにちは".char_index_to_byte_index(5) # => 15 + # ``` + # TODO: Implement this! + # def char_index_to_byte_index(index) + # if ascii_only? + # return 0 <= index <= bytesize ? index : nil + # end + + # size = each_byte_index_and_char_index do |byte_index, char_index| + # return byte_index if index == char_index + # end + # return @bytesize if index == size + # nil + # end + + # Returns the char index of a byte index, or `nil` if out of bounds. + # + # It is valid to pass `#bytesize` to *index*, and in this case the answer + # will be the size of this string. + # TODO: Implement this! + # def byte_index_to_char_index(index) + # if ascii_only? + # return 0 <= index <= bytesize ? index : nil + # end + + # size = each_byte_index_and_char_index do |byte_index, char_index| + # return char_index if index == byte_index + # end + # return size if index == @bytesize + # nil + # end + + # Returns `true` if the string contains *search*. + # + # ``` + # "Team".includes?('i') # => false + # "Dysfunctional".includes?("fun") # => true + # ``` + # TODO: Implement this! + # def includes?(search : Char | String) + # !!index(search) + # end + + # Makes an array by splitting the string on any ASCII whitespace characters + # (and removing that whitespace). + # + # If *limit* is present, up to *limit* new strings will be created, + # with the entire remainder added to the last string. + # + # ``` + # old_pond = " + # Old pond + # a frog leaps in + # water's sound + # " + # old_pond.split # => ["Old", "pond", "a", "frog", "leaps", "in", "water's", "sound"] + # old_pond.split(3) # => ["Old", "pond", "a frog leaps in\n water's sound\n"] + # ``` + # TODO: Implement this! + # def split(limit : Int32? = nil) + # ary = Array(String).new + # split(limit) do |string| + # ary << string + # end + # ary + # end + + # Splits the string after any ASCII whitespace character and yields each part to a block. + # + # If *limit* is present, up to *limit* new strings will be created, + # with the entire remainder added to the last string. + # + # ``` + # ary = [] of String + # old_pond = " + # Old pond + # a frog leaps in + # water's sound + # " + # + # old_pond.split { |s| ary << s } + # ary # => ["Old", "pond", "a", "frog", "leaps", "in", "water's", "sound"] + # ary.clear + # + # old_pond.split(3) { |s| ary << s } + # ary # => ["Old", "pond", "a frog leaps in\n water's sound\n"] + # ``` + # TODO: Implement this! + # def split(limit : Int32? = nil, &block : String -> _) + # if limit && limit <= 1 + # yield self + # return + # end + + # yielded = 0 + # single_byte_optimizable = ascii_only? + # index = 0 + # i = 0 + # looking_for_space = false + # limit_reached = false + # while i < bytesize + # if looking_for_space + # while i < bytesize + # c = to_unsafe[i] + # i += 1 + # if c.unsafe_chr.ascii_whitespace? + # piece_bytesize = i - 1 - index + # piece_size = single_byte_optimizable ? piece_bytesize : 0 + # yield String.new(to_unsafe + index, piece_bytesize, piece_size) + # yielded += 1 + # looking_for_space = false + + # if limit && yielded + 1 == limit + # limit_reached = true + # end + + # break + # end + # end + # else + # while i < bytesize + # c = to_unsafe[i] + # i += 1 + # unless c.unsafe_chr.ascii_whitespace? + # index = i - 1 + # looking_for_space = true + # break + # end + # end + + # break if limit_reached + # end + # end + # if looking_for_space + # piece_bytesize = bytesize - index + # piece_size = single_byte_optimizable ? piece_bytesize : 0 + # yield String.new(to_unsafe + index, piece_bytesize, piece_size) + # end + # end + + # Makes an `Array` by splitting the string on the given character *separator* + # (and removing that character). + # + # If *limit* is present, up to *limit* new strings will be created, + # with the entire remainder added to the last string. + # + # ``` + # "foo,bar,baz".split(',') # => ["foo", "bar", "baz"] + # "foo,bar,baz".split(',', 2) # => ["foo", "bar,baz"] + # ``` + # TODO: Implement this! + # def split(separator : Char, limit = nil) + # ary = Array(String).new + # split(separator, limit) do |string| + # ary << string + # end + # ary + # end + + # Splits the string after each character *separator* and yields each part to a block. + # + # If *limit* is present, up to *limit* new strings will be created, + # with the entire remainder added to the last string. + # + # ``` + # ary = [] of String + # + # "foo,bar,baz".split(',') { |string| ary << string } + # ary # => ["foo", "bar", "baz"] + # ary.clear + # + # "foo,bar,baz".split(',', 2) { |string| ary << string } + # ary # => ["foo", "bar,baz"] + # ``` + # TODO: Implement this! + # def split(separator : Char, limit = nil, &block : String -> _) + # if empty? || limit && limit <= 1 + # yield self + # return + # end -# The part of this file and line numbers are taken from: -# https://github.com/crystal-lang/crystal/blob/0.21.0/src/string.cr -class String - # Returns the number of bytes in this string. + # yielded = 0 + # byte_offset = 0 + + # reader = Char::Reader.new(self) + # reader.each do |char| + # if char == separator + # piece_bytesize = reader.pos - byte_offset + # yield String.new(to_unsafe + byte_offset, piece_bytesize) + # yielded += 1 + # byte_offset = reader.pos + reader.current_char_width + # break if limit && yielded + 1 == limit + # end + # end + + # piece_bytesize = bytesize - byte_offset + # yield String.new(to_unsafe + byte_offset, piece_bytesize) + # end + + # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). + # + # If *limit* is present, the array will be limited to *limit* items and + # the final item will contain the remainder of the string. + # + # If *separator* is an empty string (`""`), the string will be separated into one-character strings. # # ``` - # "hello".bytesize # => 5 - # "你好".bytesize # => 6 + # long_river_name = "Mississippi" + # long_river_name.split("ss") # => ["Mi", "i", "ippi"] + # long_river_name.split("i") # => ["M", "ss", "ss", "pp", ""] + # long_river_name.split("") # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] # ``` - # line: 280 - def bytesize - @bytesize + # TODO: Implement this! + # def split(separator : String, limit = nil) + # ary = Array(String).new + # split(separator, limit) do |string| + # ary << string + # end + # ary + # end + + # Splits the string after each string *separator* and yields each part to a block. + # + # If *limit* is present, the array will be limited to *limit* items and + # the final item will contain the remainder of the string. + # + # If *separator* is an empty string (`""`), the string will be separated into one-character strings. + # + # ``` + # ary = [] of String + # long_river_name = "Mississippi" + # + # long_river_name.split("ss") { |s| ary << s } + # ary # => ["Mi", "i", "ippi"] + # ary.clear + # + # long_river_name.split("i") { |s| ary << s } + # ary # => ["M", "ss", "ss", "pp", ""] + # ary.clear + # + # long_river_name.split("") { |s| ary << s } + # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] + # ``` + # TODO: Implement this! + # def split(separator : String, limit = nil, &block : String -> _) + # if empty? || (limit && limit <= 1) + # yield self + # return + # end + + # if separator.empty? + # split_by_empty_separator(limit) do |string| + # yield string + # end + # return + # end + + # yielded = 0 + # byte_offset = 0 + # separator_bytesize = separator.bytesize + + # single_byte_optimizable = ascii_only? + + # i = 0 + # stop = bytesize - separator.bytesize + 1 + # while i < stop + # if (to_unsafe + i).memcmp(separator.to_unsafe, separator_bytesize) == 0 + # piece_bytesize = i - byte_offset + # piece_size = single_byte_optimizable ? piece_bytesize : 0 + # yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size) + # yielded += 1 + # byte_offset = i + separator_bytesize + # i += separator_bytesize - 1 + # break if limit && yielded + 1 == limit + # end + # i += 1 + # end + + # piece_bytesize = bytesize - byte_offset + # piece_size = single_byte_optimizable ? piece_bytesize : 0 + # yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size) + # end + + # Splits the string after each regex *separator* and yields each part to a block. + # + # If *limit* is present, the array will be limited to *limit* items and + # the final item will contain the remainder of the string. + # + # If *separator* is an empty regex (`//`), the string will be separated into one-character strings. + # + # ``` + # ary = [] of String + # long_river_name = "Mississippi" + # + # long_river_name.split(/s+/) { |s| ary << s } + # ary # => ["Mi", "i", "ippi"] + # ary.clear + # + # long_river_name.split(//) { |s| ary << s } + # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] + # ``` + # TODO: Implement this! + # def split(separator : Regex, limit = nil) + # ary = Array(String).new + # split(separator, limit) do |string| + # ary << string + # end + # ary + # end + + # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). + # + # If *limit* is present, the array will be limited to *limit* items and + # the final item will contain the remainder of the string. + # + # If *separator* is an empty regex (`//`), the string will be separated into one-character strings. + # + # ``` + # long_river_name = "Mississippi" + # long_river_name.split(/s+/) # => ["Mi", "i", "ippi"] + # long_river_name.split(//) # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] + # ``` + # TODO: Implement this! + # def split(separator : Regex, limit = nil, &block : String -> _) + # if empty? || (limit && limit <= 1) + # yield self + # return + # end + + # if separator.source.empty? + # split_by_empty_separator(limit) do |string| + # yield string + # end + # return + # end + + # count = 0 + # match_offset = slice_offset = 0 + + # while match = separator.match_at_byte_index(self, match_offset) + # index = match.byte_begin(0) + # match_bytesize = match[0].bytesize + # next_offset = index + match_bytesize + + # if next_offset == slice_offset + # match_offset = next_offset + char_bytesize_at(next_offset) + # else + # slice_size = index - slice_offset + + # yield byte_slice(slice_offset, slice_size) + # count += 1 + + # 1.upto(match.size) do |i| + # if group = match[i]? + # yield group + # end + # end + + # slice_offset = match_offset = next_offset + # end + + # break if limit && count + 1 == limit + # break if match_offset >= bytesize + # end + + # yield byte_slice(slice_offset) + # end + # TODO: Implement this! + # private def split_by_empty_separator(limit, &block : String -> _) + # yielded = 0 + + # each_char do |c| + # yield c.to_s + # yielded += 1 + # break if limit && yielded + 1 == limit + # end + + # if limit && yielded != size + # yield self[yielded..-1] + # yielded += 1 + # end + # end + # TODO: Implement this! + # def lines(chomp = true) + # lines = [] of String + # each_line(chomp: chomp) do |line| + # lines << line + # end + # lines + # end + + # Splits the string after each newline and yields each line to a block. + # + # ``` + # haiku = "the first cold shower + # even the monkey seems to want + # a little coat of straw" + # haiku.each_line do |stanza| + # puts stanza.upcase + # end + # # => THE FIRST COLD SHOWER + # # => EVEN THE MONKEY SEEMS TO WANT + # # => A LITTLE COAT OF STRAW + # ``` + # TODO: Implement this! + # def each_line(chomp = true) : Nil + # return if empty? + + # offset = 0 + + # while byte_index = byte_index('\n'.ord.to_u8, offset) + # count = byte_index - offset + 1 + # if chomp + # count -= 1 + # if offset + count > 0 && to_unsafe[offset + count - 1] === '\r' + # count -= 1 + # end + # end + + # yield unsafe_byte_slice_string(offset, count) + # offset = byte_index + 1 + # end + + # unless offset == bytesize + # yield unsafe_byte_slice_string(offset) + # end + # end + + # Returns an `Iterator` which yields each line of this string (see `String#each_line`). + # TODO: Implement this! + # def each_line(chomp = true) + # LineIterator.new(self, chomp) + # end + + # Converts camelcase boundaries to underscores. + # + # ``` + # "DoesWhatItSaysOnTheTin".underscore # => "does_what_it_says_on_the_tin" + # "PartyInTheUSA".underscore # => "party_in_the_usa" + # "HTTP_CLIENT".underscore # => "http_client" + # "3.14IsPi".underscore # => "3.14_is_pi" + # ``` + # TODO: Implement this! + # def underscore + # first = true + # last_is_downcase = false + # last_is_upcase = false + # last_is_digit = false + # mem = nil + + # String.build(bytesize + 10) do |str| + # each_char do |char| + # digit = '0' <= char <= '9' + # downcase = 'a' <= char <= 'z' || digit + # upcase = 'A' <= char <= 'Z' + + # if first + # str << char.downcase + # elsif last_is_downcase && upcase + # if mem + # # This is the case of A1Bcd, we need to put 'mem' (not to need to convert as downcase + # # ^ + # # because 'mem' is digit surely) before putting this char as downcase. + # str << mem + # mem = nil + # end + # # This is the case of AbcDe, we need to put an underscore before the 'D' + # # ^ + # str << '_' + # str << char.downcase + # elsif (last_is_upcase || last_is_digit) && (upcase || digit) + # # This is the case of 1) A1Bcd, 2) A1BCd or 3) A1B_cd:if the next char is upcase (case 1) we need + # # ^ ^ ^ + # # 1) we need to append this char as downcase + # # 2) we need to append an underscore and then the char as downcase, so we save this char + # # in 'mem' and decide later + # # 3) we need to append this char as downcase and then a single underscore + # if mem + # # case 2 + # str << mem.downcase + # end + # mem = char + # else + # if mem + # if char == '_' + # # case 3 + # elsif last_is_upcase && downcase + # # case 1 + # str << '_' + # end + # str << mem.downcase + # mem = nil + # end + + # str << char.downcase + # end + + # last_is_downcase = downcase + # last_is_upcase = upcase + # last_is_digit = digit + # first = false + # end + + # str << mem.downcase if mem + # end + # end + + # Converts underscores to camelcase boundaries. + # + # ``` + # "eiffel_tower".camelcase # => "EiffelTower" + # ``` + # TODO: Implement this! + # def camelcase + # return self if empty? + + # first = true + # last_is_underscore = false + + # String.build(bytesize) do |str| + # each_char do |char| + # if first + # str << char.upcase + # elsif char == '_' + # last_is_underscore = true + # elsif last_is_underscore + # str << char.upcase + # last_is_underscore = false + # else + # str << char + # end + # first = false + # end + # end + # end + + # Reverses the order of characters in the string. + # + # ``` + # "Argentina".reverse # => "anitnegrA" + # "racecar".reverse # => "racecar" + # ``` + # TODO: Implement this! + # def reverse + # return self if bytesize <= 1 + + # if ascii_only? + # String.new(bytesize) do |buffer| + # bytesize.times do |i| + # buffer[i] = self.to_unsafe[bytesize - i - 1] + # end + # {@bytesize, @length} + # end + # else + # # Iterate grpahemes to reverse the string, + # # so combining characters are placed correctly + # String.new(bytesize) do |buffer| + # buffer += bytesize + # scan(/\X/) do |match| + # grapheme = match[0] + # buffer -= grapheme.bytesize + # buffer.copy_from(grapheme.to_unsafe, grapheme.bytesize) + # end + # {@bytesize, @length} + # end + # end + # end + + # Adds instances of *char* to right of the string until it is at least size of *len*. + # + # ``` + # "Purple".ljust(8) # => "Purple " + # "Purple".ljust(8, '-') # => "Purple--" + # "Aubergine".ljust(8) # => "Aubergine" + # ``` + # TODO: Implemenet this! + # def ljust(len, char : Char = ' ') + # just len, char, true + # end + + # Adds instances of *char* to left of the string until it is at least size of *len*. + # + # ``` + # "Purple".rjust(8) # => " Purple" + # "Purple".rjust(8, '-') # => "--Purple" + # "Aubergine".rjust(8) # => "Aubergine" + # ``` + # TODO: Implement this! + # def rjust(len, char : Char = ' ') + # just len, char, false + # end + # TODO: Implement this! + # private def just(len, char, left) + # return self if size >= len + + # bytes, count = String.char_bytes_and_bytesize(char) + + # difference = len - size + # new_bytesize = bytesize + difference * count + + # String.new(new_bytesize) do |buffer| + # if left + # buffer.copy_from(to_unsafe, bytesize) + # buffer += bytesize + # end + + # if count == 1 + # Intrinsics.memset(buffer.as(Void*), char.ord.to_u8, difference.to_u32, 0_u32, false) + # buffer += difference + # else + # difference.times do + # buffer.copy_from(bytes.to_unsafe, count) + # buffer += count + # end + # end + + # unless left + # buffer.copy_from(to_unsafe, bytesize) + # end + + # {new_bytesize, len} + # end + # end + + # Returns the successor of the string. The successor is calculated + # by incrementing characters starting from the rightmost alphanumeric + # (or the rightmost character if there are no alphanumerics) in the string. + # Incrementing a digit always results in another digit, and incrementing + # a letter results in another letter of the same case. + # + # If the increment generates a "carry", the character to the left of it is + # incremented. This process repeats until there is no carry, + # adding an additional character if necessary. + # + # ``` + # "abcd".succ # => "abce" + # "THX1138".succ # => "THX1139" + # "((koala))".succ # => "((koalb))" + # "1999zzz".succ # => "2000aaa" + # "ZZZ9999".succ # => "AAAA0000" + # "***".succ # => "**+" + # ``` + # TODO: Implement this! + # def succ + # return self if empty? + + # chars = self.chars + + # carry = nil + # last_alnum = 0 + # index = size - 1 + + # while index >= 0 + # s = chars[index] + # if s.ascii_alphanumeric? + # carry = 0 + # if ('0' <= s && s < '9') || + # ('a' <= s && s < 'z') || + # ('A' <= s && s < 'Z') + # chars[index] = s.succ + # break + # elsif s == '9' + # chars[index] = '0' + # carry = '1' + # elsif s == 'z' + # chars[index] = carry = 'a' + # elsif s == 'Z' + # chars[index] = carry = 'A' + # end + + # last_alnum = index + # end + # index -= 1 + # end + + # if carry.nil? # there were no alphanumeric chars + # chars[size - 1] = chars[size - 1].succ + # end + + # if carry.is_a?(Char) && index < 0 # we still have a carry and already reached the beginning + # chars.insert(last_alnum, carry) + # end + + # String.build(chars.size) do |str| + # chars.each do |char| + # str << char + # end + # end + # end + + # Finds match of *regex*, starting at *pos*. + # TODO: Implement this! + # def match(regex : Regex, pos = 0) : Regex::MatchData? + # match = regex.match self, pos + # $~ = match + # match + # end + + # Searches the string for instances of *pattern*, + # yielding a `Regex::MatchData` for each match. + # TODO: Implement this! + # def scan(pattern : Regex) + # byte_offset = 0 + + # while match = pattern.match_at_byte_index(self, byte_offset) + # index = match.byte_begin(0) + # $~ = match + # yield match + # match_bytesize = match[0].bytesize + # match_bytesize += 1 if match_bytesize == 0 + # byte_offset = index + match_bytesize + # end + + # self + # end + + # Searches the string for instances of *pattern*, + # returning an `Array` of `Regex::MatchData` for each match. + # def scan(pattern : Regex) + # matches = [] of Regex::MatchData + # scan(pattern) do |match| + # matches << match + # end + # matches + # end + + # Searches the string for instances of *pattern*, + # yielding the matched string for each match. + # TODO: Implement this! + # def scan(pattern : String) + # return self if pattern.empty? + # index = 0 + # while index = byte_index(pattern, index) + # yield pattern + # index += pattern.bytesize + # end + # self + # end + + # Searches the string for instances of *pattern*, + # returning an array of the matched string for each match. + # TODO: Implement this! + # def scan(pattern : String) + # matches = [] of String + # scan(pattern) do |match| + # matches << match + # end + # matches + # end + + # Yields each character in the string to the block. + # + # ``` + # array = [] of Char + # "ab☃".each_char do |char| + # array << char + # end + # array # => ['a', 'b', '☃'] + # ``` + # TODO: Implement this! + # def each_char : Nil + # if ascii_only? + # each_byte do |byte| + # yield (byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT) + # end + # else + # Char::Reader.new(self).each do |char| + # yield char + # end + # end + # end + + # Returns an `Iterator` over each character in the string. + # + # ``` + # chars = "ab☃".each_char + # chars.next # => 'a' + # chars.next # => 'b' + # chars.next # => '☃' + # ``` + # TODO: Implement this! + # def each_char + # CharIterator.new(Char::Reader.new(self)) + # end + + # Yields each character and its index in the string to the block. + # + # ``` + # array = [] of Tuple(Char, Int32) + # "ab☃".each_char_with_index do |char, index| + # array << {char, index} + # end + # array # => [{'a', 0}, {'b', 1}, {'☃', 2}] + # ``` + # TODO: Implement this! + # def each_char_with_index + # i = 0 + # each_char do |char| + # yield char, i + # i += 1 + # end + # end + + # Returns an `Array` of all characters in the string. + # + # ``` + # "ab☃".chars # => ['a', 'b', '☃'] + # ``` + # TODO: Implement this! + # def chars + # chars = Array(Char).new(@length > 0 ? @length : bytesize) + # each_char do |char| + # chars << char + # end + # chars + # end + + # Yields each codepoint to the block. + # + # ``` + # array = [] of Int32 + # "ab☃".each_codepoint do |codepoint| + # array << codepoint + # end + # array # => [97, 98, 9731] + # ``` + # + # See also: `Char#ord`. + # TODO: Implement this! + # def each_codepoint + # each_char do |char| + # yield char.ord + # end + # end + + # Returns an `Iterator` for each codepoint. + # + # ``` + # codepoints = "ab☃".each_codepoint + # codepoints.next # => 97 + # codepoints.next # => 98 + # codepoints.next # => 9731 + # ``` + # + # See also: `Char#ord`. + # TODO: Implement this! + # def each_codepoint + # each_char.map &.ord + # end + + # Returns an `Array` of the codepoints that make the string. + # + # ``` + # "ab☃".codepoints # => [97, 98, 9731] + # ``` + # + # See also: `Char#ord`. + # TODO: Implement this! + # def codepoints + # codepoints = Array(Int32).new(@length > 0 ? @length : bytesize) + # each_codepoint do |codepoint| + # codepoints << codepoint + # end + # codepoints + # end + + # Yields each byte in the string to the block. + # + # ``` + # array = [] of UInt8 + # "ab☃".each_byte do |byte| + # array << byte + # end + # array # => [97, 98, 226, 152, 131] + # ``` + def each_byte + # TODO: Implement this! + # to_slice.each do |byte| + # yield byte + # end + # nil + + size.times do |i| + yield bytes[i], i + end end + # Returns an `Iterator` over each byte in the string. + # + # ``` + # bytes = "ab☃".each_byte + # bytes.next # => 97 + # bytes.next # => 98 + # bytes.next # => 226 + # bytes.next # => 152 + # bytes.next # => 131 + # ``` + # TODO: Implement this! + # def each_byte + # to_slice.each + # end + + # Returns this string's bytes as an `Array(UInt8)`. + # + # ``` + # "hello".bytes # => [104, 101, 108, 108, 111] + # "你好".bytes # => [228, 189, 160, 229, 165, 189] + # ``` def bytes + # TODO: Implement this! # Array.new(bytesize) { |i| to_unsafe[i] } pointerof(@c) end - def each_byte - size.times do |i| - yield bytes[i], i + # TODO: Implement these! + # def inspect(io) + # dump_or_inspect(io) do |char, error| + # inspect_char(char, error, io) + # end + # end + + # def pretty_print(pp) + # pp.text(inspect) + # end + + # def inspect_unquoted + # String.build do |io| + # inspect_unquoted(io) + # end + # end + + # def inspect_unquoted(io) + # dump_or_inspect_unquoted(io) do |char, error| + # inspect_char(char, error, io) + # end + # end + + # def dump + # String.build do |io| + # dump io + # end + # end + + # def dump(io) + # dump_or_inspect(io) do |char, error| + # dump_char(char, error, io) + # end + # end + + # def dump_unquoted + # String.build do |io| + # dump_unquoted(io) + # end + # end + + # def dump_unquoted(io) + # dump_or_inspect_unquoted(io) do |char, error| + # dump_char(char, error, io) + # end + # end + # TODO: Implement these! + # private def dump_or_inspect(io) + # io << "\"" + # dump_or_inspect_unquoted(io) do |char, error| + # yield char, error + # end + # io << "\"" + # end + # TODO: Implement these! + # private def dump_or_inspect_unquoted(io) + # reader = Char::Reader.new(self) + # while reader.has_next? + # current_char = reader.current_char + # case current_char + # when '"' then io << "\\\"" + # when '\\' then io << "\\\\" + # when '\b' then io << "\\b" + # when '\e' then io << "\\e" + # when '\f' then io << "\\f" + # when '\n' then io << "\\n" + # when '\r' then io << "\\r" + # when '\t' then io << "\\t" + # when '\v' then io << "\\v" + # when '#' + # current_char = reader.next_char + # if current_char == '{' + # io << "\\\#{" + # reader.next_char + # next + # else + # io << '#' + # next + # end + # else + # if reader.error + # reader.current_char_width.times do |i| + # yield '\0', to_unsafe[reader.pos + i] + # end + # else + # yield current_char, nil + # end + # end + # reader.next_char + # end + # end + + # private def inspect_char(char, error, io) + # dump_or_inspect_char char, error, io do + # char.ascii_control? + # end + # end + + # private def dump_char(char, error, io) + # dump_or_inspect_char(char, error, io) do + # char.ascii_control? || char.ord >= 0x80 + # end + # end + + # private def dump_or_inspect_char(char, error, io) + # if error + # dump_hex(error, io) + # elsif yield + # dump_unicode(char, io) + # else + # io << char + # end + # end + + # private def dump_hex(error, io) + # io << "\\x" + # io << "0" if error < 16 + # error.to_s(16, io, upcase: true) + # end + + # private def dump_unicode(char, io) + # io << "\\u" + # io << "0" if char.ord < 4096 + # io << "0" if char.ord < 256 + # io << "0" if char.ord < 16 + # char.ord.to_s(16, io) + # io << "" + # end + # TODO: Implement these! + # def starts_with?(str : String) + # return false if str.bytesize > bytesize + # to_unsafe.memcmp(str.to_unsafe, str.bytesize) == 0 + # end + + # def starts_with?(char : Char) + # each_char do |c| + # return c == char + # end + + # false + # end + # TODO: Implement these! + # def ends_with?(str : String) + # return false if str.bytesize > bytesize + # (to_unsafe + bytesize - str.bytesize).memcmp(str.to_unsafe, str.bytesize) == 0 + # end + + # def ends_with?(char : Char) + # return false unless bytesize > 0 + + # if char.ascii? || ascii_only? + # return to_unsafe[bytesize - 1] == char.ord + # end + + # bytes, count = String.char_bytes_and_bytesize(char) + # return false if bytesize < count + + # count.times do |i| + # return false unless to_unsafe[bytesize - count + i] == bytes[i] + # end + + # true + # end + + # Interpolates *other* into the string using `Kernel#sprintf`. + # + # ``` + # "Party like it's %d!!!" % 1999 # => "Party like it's 1999!!!" + # ``` + # TODO: Implement this! + # def %(other) + # sprintf self, other + # end + + # Returns a hash based on this string’s size and content. + # + # See also: `Object#hash`. + # TODO: Implement this! + # def hash + # h = 0 + # each_byte do |c| + # h = 31 * h + c + # end + # h + # end + + # Returns the number of unicode codepoints in this string. + # + # ``` + # "hello".size # => 5 + # "你好".size # => 2 + # ``` + def size + if @length > 0 || @bytesize == 0 + return @length end - end - # line: 905 - def unsafe_byte_at(index) - to_unsafe[index] + @length = each_byte_index_and_char_index { } end - # line: 3065 + # Returns `true` if this String is comprised in its entirety + # by ASCII characters. + # + # ``` + # "hello".ascii_only? # => true + # "你好".ascii_only? # => false + # ``` + # TODO: Implement this! + # def ascii_only? + # @bytesize == size + # end + + # Returns `true` if this String is encoded correctly + # according to the UTF-8 encoding. + # TODO: Implement this! + # def valid_encoding? + # reader = Char::Reader.new(self) + # while reader.has_next? + # return false if reader.error + # reader.next_char + # end + # true + # end + + # Returns a String where bytes that are invalid in the + # UTF-8 encoding are replaced with *replacement*. + # TODO: Implement this! + # def scrub(replacement = Char::REPLACEMENT) : String + # # If the string is valid we have a chance of returning self + # # to avoid creating a new string + # result = nil + + # reader = Char::Reader.new(self) + # while reader.has_next? + # if reader.error + # unless result + # result = String::Builder.new(bytesize) + # result.write(to_slice[0, reader.pos]) + # end + # result << replacement + # else + # result << reader.current_char if result + # end + # reader.next_char + # end + + # result ? result.to_s : self + # end + protected def char_bytesize_at(byte_index) first = unsafe_byte_at(byte_index) @@ -78,22 +4172,10 @@ class String return 4 end - # Returns the number of unicode codepoints in this string. - # - # ``` - # "hello".size # => 5 - # "你好".size # => 2 - # ``` - # line: 3911 - def size - if @length > 0 || @bytesize == 0 - return @length - end - - @length = each_byte_index_and_char_index { } + protected def size_known? + @bytesize == 0 || @length > 0 end - # line: 4009 protected def each_byte_index_and_char_index byte_index = 0 char_index = 0 @@ -107,9 +4189,158 @@ class String char_index end + def clone + self + end + + def dup + self + end + + def to_s + self + end + # TODO: Implement this! + # def to_s(io) + # io.write_utf8(to_slice) + # end + + # Returns the underlying bytes of this String in an **unsafe** way. + # + # The returned slice is read-only. + # TODO: Implement this! + # def to_slice : Bytes + # Slice.new(to_unsafe, bytesize, read_only: true) + # end + # Returns a pointer to the underlying bytes of this String. - # line: 4046 def to_unsafe : UInt8* pointerof(@c) end + # TODO: Implement these! + # def unsafe_byte_slice(byte_offset, count) + # Slice.new(to_unsafe + byte_offset, count, read_only: true) + # end + + # def unsafe_byte_slice(byte_offset) + # Slice.new(to_unsafe + byte_offset, bytesize - byte_offset, read_only: true) + # end + + # protected def unsafe_byte_slice_string(byte_offset) + # String.new(unsafe_byte_slice(byte_offset)) + # end + + # protected def unsafe_byte_slice_string(byte_offset, count) + # String.new(unsafe_byte_slice(byte_offset, count)) + # end + # TODO: Implement this! + # protected def self.char_bytes_and_bytesize(char : Char) + # bytes = uninitialized UInt8[4] + + # bytesize = 0 + # char.each_byte do |byte| + # bytes[bytesize] = byte + # bytesize += 1 + # end + + # {bytes, bytesize} + # end + + # Raises an `ArgumentError` if `self` has null bytes. Returns `self` otherwise. + # + # This method should sometimes be called before passing a `String` to a C function. + # TODO: Implement this! + # def check_no_null_byte + # raise ArgumentError.new("String contains null byte") if byte_index(0) + # self + # end + + # :nodoc: + # TODO: Implement this! + # def self.check_capacity_in_bounds(capacity) + # if capacity < 0 + # raise ArgumentError.new("Negative capacity") + # end + + # if capacity.to_u64 > (UInt32::MAX - HEADER_SIZE - 1) + # raise ArgumentError.new("Capacity too big") + # end + # end + # TODO: Implement this! + # private class CharIterator + # include Iterator(Char) + + # @reader : Char::Reader + # @end : Bool + + # def initialize(@reader, @end = false) + # check_empty + # end + + # def next + # return stop if @end + + # value = @reader.current_char + # @reader.next_char + # @end = true unless @reader.has_next? + + # value + # end + + # def rewind + # @reader.pos = 0 + # @end = false + # check_empty + # self + # end + + # private def check_empty + # @end = true if @reader.string.bytesize == 0 + # end + # end + # TODO: Implement this! + # private class LineIterator + # include Iterator(String) + + # def initialize(@string : String, @chomp : Bool) + # @offset = 0 + # @end = false + # end + + # def next + # return stop if @end + + # byte_index = @string.byte_index('\n'.ord.to_u8, @offset) + # if byte_index + # count = byte_index - @offset + 1 + # if @chomp + # count -= 1 + # if @offset + count > 0 && @string.to_unsafe[@offset + count - 1] === '\r' + # count -= 1 + # end + # end + + # value = @string.unsafe_byte_slice_string(@offset, count) + # @offset = byte_index + 1 + # else + # if @offset == @string.bytesize + # value = stop + # else + # value = @string.unsafe_byte_slice_string(@offset) + # end + # @end = true + # end + + # value + # end + + # def rewind + # @offset = 0 + # @end = false + # self + # end + # end end + +# require "./string/formatter" +# require "./string/builder" diff --git a/src/core/tuple.cr b/src/core/tuple.cr new file mode 100644 index 0000000..2afda56 --- /dev/null +++ b/src/core/tuple.cr @@ -0,0 +1,511 @@ +# A tuple is a fixed-size, immutable, stack-allocated sequence of values +# of possibly different types. +# +# You can think of a `Tuple` as an immutable `Array` whose types for each position +# are known at compile time. +# +# A tuple can be created with the usual `new` method or with a tuple literal: +# +# ``` +# tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char) +# tuple[0] # => 1 +# tuple[1] # => "hello" +# tuple[2] # => 'x' +# ``` +# +# The compiler knows what types are in each position, so when indexing +# a tuple with an integer literal the compiler will return +# the value in that index and with the expected type, like in the above +# snippet. Indexing with an integer literal outside the bounds of the tuple +# will give a compile-time error. +# +# Indexing with an integer value that is only known at runtime will return +# a value whose type is the union of all the types in the tuple, and might raise +# `IndexError`. +# +# Tuples are the preferred way to return fixed-size multiple return +# values because no memory is needed to be allocated for them: +# +# ``` +# def one_and_hello +# {1, "hello"} +# end +# +# one, hello = one_and_hello +# one # => 1 +# hello # => "hello" +# ``` +# +# Good examples of the above are `Number#divmod` and `Enumerable#minmax`. +# +# Tuples can be splat with the `*` operator and passed to methods: +# +# ``` +# def multiply(string, value) +# string * value +# end +# +# tuple = {"hey", 2} +# value = multiply(*tuple) # same as multiply tuple[0], tuple[1] +# value # => "heyhey" +# ``` +# +# Finally, when using a splat argument in a method definition its type +# will be a tuple of the call arguments: +# +# ``` +# def splat_test(*args) +# args +# end +# +# tuple = splat_test 1, "hello", 'x' +# tuple.class # => Tuple(Int32, String, Char) +# tuple # => {1, "hello", 'x'} +# ``` +struct Tuple + # include Indexable(Union(*T)) + include Comparable(Tuple) + + # Creates a tuple that will contain the given arguments. + # + # This method is useful in macros and generic code because with it you can + # creates empty tuples, something that you can't do with a tuple literal. + # + # ``` + # Tuple.new(1, "hello", 'x') #=> {1, "hello", 'x'} + # Tuple.new #=> {} + # + # {} # syntax error + # ``` + def self.new(*args : *T) + args + end + + # Creates a tuple from the given array, with elements casted to the given types. + # + # ``` + # Tuple(String, Int64).from(["world", 2]) # => {"world", 2} + # Tuple(String, Int64).from(["world", 2]).class # => {String, Int64} + # ``` + # + # See also: `#from`. + def self.from(array : Array) : self + {% begin %} + Tuple.new(*{{T}}).from(array) + {% end %} + end + + # Expects to be called on a tuple of types, creates a tuple from the given array, + # with types casted appropriately. + # + # This allows you to easily pass an array as individual arguments to a method. + # + # ``` + # def speak_about(thing : String, n : Int64) + # "I see #{n} #{thing}s" + # end + # + # data = JSON.parse(%(["world", 2])).as_a + # speak_about(*{String, Int64}.from(data)) # => "I see 2 worlds" + # ``` + def from(array : Array) + if size != array.size + raise ArgumentError.new "Expected array of size #{size} but one of size #{array.size} was given." + end + + {% begin %} + Tuple.new( + {% for i in 0...@type.size %} + self[{{i}}].cast(array[{{i}}]), + {% end %} + ) + {% end %} + end + + def unsafe_at(index : Int) + self[index] + end + + # Returns the element at the given *index*. Read the type docs to understand + # the difference between indexing with a number literal or a variable. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple[0] # => 1 (Int32) + # tuple[3] # compile error: index out of bounds for tuple {Int32, String, Char} + # + # i = 0 + # tuple[i] # => 1 (Int32 | String | Char) + # + # i = 3 + # tuple[i] # raises IndexError + # ``` + def [](index : Int) + at(index) + end + + # Returns the element at the given *index* or `nil` if out of bounds. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple[0]? # => 1 + # tuple[3]? # => nil + # ``` + def []?(index : Int) + at(index) { nil } + end + + # Returns the element at the given *index* or raises IndexError if out of bounds. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple.at(0) # => 1 + # tuple.at(3) # raises IndexError + # ``` + def at(index : Int) + at(index) { raise IndexError.new } + end + + # Returns the element at the given *index* or the value returned by the block if + # out of bounds. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple.at(0) { 10 } # => 1 + # tuple.at(3) { 10 } # => 10 + # ``` + def at(index : Int) + {% for i in 0...T.size %} + return self[{{i}}] if {{i}} == index + {% end %} + yield + end + + # Yields each of the elements in this tuple. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple.each do |value| + # puts value + # end + # ``` + # + # Output: + # + # ```text + # 1 + # "hello" + # 'x' + # ``` + def each : Nil + {% for i in 0...T.size %} + yield self[{{i}}] + {% end %} + end + + # Returns `true` if this tuple has the same size as the other tuple + # and their elements are equal to each other when compared with `==`. + # + # ``` + # t1 = {1, "hello"} + # t2 = {1.0, "hello"} + # t3 = {2, "hello"} + # + # t1 == t2 # => true + # t1 == t3 # => false + # ``` + def ==(other : self) + {% for i in 0...T.size %} + return false unless self[{{i}}] == other[{{i}}] + {% end %} + true + end + + # ditto + def ==(other : Tuple) + return false unless size == other.size + + size.times do |i| + return false unless self[i] == other[i] + end + true + end + + def ==(other) + false + end + + # Returns `true` if case equality holds for the elements in `self` and *other*. + # + # ``` + # {1, 2} === {1, 2} # => true + # {1, 2} === {1, 3} # => false + # ``` + # + # See also: `Object#===`. + def ===(other : self) + {% for i in 0...T.size %} + return false unless self[{{i}}] === other[{{i}}] + {% end %} + true + end + + # Returns `true` if `self` and *other* have the same size and case equality holds + # for the elements in `self` and *other*. + # + # ``` + # {1, 2} === {1, 2, 3} # => false + # {/o+/, "bar"} === {"foo", "bar"} # => true + # ``` + # + # See also: `Object#===`. + def ===(other : Tuple) + return false unless size == other.size + + size.times do |i| + return false unless self[i] === other[i] + end + true + end + + # Implements the comparison operator. + # + # Each object in each tuple is compared (using the `<=>` operator). + # + # Tuples are compared in an "element-wise" manner; the first element of this tuple is + # compared with the first one of *other* using the `<=>` operator, then each of the second elements, + # etc. As soon as the result of any such comparison is non zero + # (i.e. the two corresponding elements are not equal), that result is returned for the whole tuple comparison. + # + # If all the elements are equal, then the result is based on a comparison of the tuple sizes. + # Thus, two tuples are "equal" according to `<=>` if, and only if, they have the same size + # and the value of each element is equal to the value of the corresponding element in the other tuple. + # + # ``` + # {"a", "a", "c"} <=> {"a", "b", "c"} # => -1 + # {1, 2, 3, 4, 5, 6} <=> {1, 2} # => +1 + # {1, 2} <=> {1, 2.0} # => 0 + # ``` + # + # See also: `Object#<=>`. + def <=>(other : self) + {% for i in 0...T.size %} + cmp = self[{{i}}] <=> other[{{i}}] + return cmp unless cmp == 0 + {% end %} + 0 + end + + # ditto + def <=>(other : Tuple) + min_size = Math.min(size, other.size) + min_size.times do |i| + cmp = self[i] <=> other[i] + return cmp unless cmp == 0 + end + size <=> other.size + end + + # Returns a hash value based on this tuple's length and contents. + # + # See also: `Object#hash`. + def hash + hash = 31 * size + {% for i in 0...T.size %} + hash = 31 * hash + self[{{i}}].hash + {% end %} + hash + end + + # Returns a tuple containing cloned elements of this tuple using the `clone` method. + def clone + {% if true %} + Tuple.new( + {% for i in 0...T.size %} + self[{{i}}].clone, + {% end %} + ) + {% end %} + end + + # Returns a tuple that contains `self`'s elements followed by *other*'s elements. + # + # ``` + # t1 = {1, 2} + # t2 = {"foo", "bar"} + # t3 = t1 + t2 + # t3 # => {1, 2, "foo", "bar"} + # typeof(t3) # => Tuple(Int32, Int32, String, String) + # ``` + def +(other : Tuple) + plus_implementation(other) + end + + private def plus_implementation(other : U) forall U + {% begin %} + Tuple.new( + {% for i in 0...@type.size %} + self[{{i}}], + {% end %} + {% for i in 0...U.size %} + other[{{i}}], + {% end %} + ) + {% end %} + end + + # Returns the number of elements in this tuple. + # + # ``` + # {'a', 'b'}.size # => 2 + # ``` + def size + {{T.size}} + end + + # Returns the types of this tuple. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple.types # => Tuple(Int32, String, Char) + # ``` + def types + T + end + + # Same as `to_s`. + def inspect + to_s + end + + # Appends a string representation of this tuple to the given `IO`. + # + # ``` + # tuple = {1, "hello"} + # tuple.to_s # => "{1, \"hello\"}" + # ``` + def to_s(io) + io << "{" + join ", ", io, &.inspect(io) + io << "}" + end + + def pretty_print(pp) : Nil + pp.list("{", self, "}") + end + + # Returns a new tuple where elements are mapped by the given block. + # + # ``` + # tuple = {1, 2.5, "a"} + # tuple.map &.to_s # => {"1", "2.5", "a"} + # ``` + def map + {% if true %} + Tuple.new( + {% for i in 0...T.size %} + (yield self[{{i}}]), + {% end %} + ) + {% end %} + end + + # Returns a new tuple where the elements are in reverse order. + # + # ``` + # tuple = {1, 2.5, "a"} + # tuple.reverse # => {"a", 2.5, 1} + # ``` + def reverse + {% if true %} + Tuple.new( + {% for i in 1..T.size %} + self[{{T.size - i}}], + {% end %} + ) + {% end %} + end + + # Yields each of the elements in this tuple in reverse order. + # + # ``` + # tuple = {1, "hello", 'x'} + # tuple.reverse_each do |value| + # puts value + # end + # ``` + # + # Output: + # + # ```text + # 'x' + # "hello" + # 1 + # ``` + def reverse_each + {% for i in 1..T.size %} + yield self[{{T.size - i}}] + {% end %} + nil + end + + # Returns the first element of this tuple. Doesn't compile + # if the tuple is empty. + # + # ``` + # tuple = {1, 2.5} + # tuple.first # => 1 + # ``` + def first + self[0] + end + + # Returns the first element of this tuple, or `nil` if this + # is the empty tuple. + # + # ``` + # tuple = {1, 2.5} + # tuple.first? # => 1 + # + # empty = Tuple.new + # empty.first? # => nil + # ``` + def first? + {% if T.size == 0 %} + nil + {% else %} + self[0] + {% end %} + end + + # Returns the last element of this tuple. Doesn't compile + # if the tuple is empty. + # + # ``` + # tuple = {1, 2.5} + # tuple.last # => 2.5 + # ``` + def last + {% if true %} + self[{{T.size - 1}}] + {% end %} + end + + # Returns the last element of this tuple, or `nil` if this + # is the empty tuple. + # + # ``` + # tuple = {1, 2.5} + # tuple.last? # => 2.5 + # + # empty = Tuple.new + # empty.last? # => nil + # ``` + def last? + {% if T.size == 0 %} + nil + {% else %} + self[{{T.size - 1}}] + {% end %} + end +end diff --git a/src/core_test/comparable_test.cr b/src/core_test/comparable_test.cr new file mode 100644 index 0000000..7cf521e --- /dev/null +++ b/src/core_test/comparable_test.cr @@ -0,0 +1,18 @@ +private class ComparableTestClass + include Comparable(Int) + + def initialize(@value : Int32) + end + + def <=>(other : Int) + @value <=> other + end +end + +# TODO: include Comparable in Number +# def comparable_test +# obj = ComparableTestClass.new(4) +# puts "It can compare against Int" unless obj == 3 +# puts "It can compare against Int" unless obj < 3 +# puts "It can compare against Int" if obj > 3 +# end diff --git a/src/core_test/pointer_test.cr b/src/core_test/pointer_test.cr new file mode 100644 index 0000000..5105548 --- /dev/null +++ b/src/core_test/pointer_test.cr @@ -0,0 +1,39 @@ +def pointer_test + puts "----------------" + puts "Test for Pointer" + puts "----------------" + + # #null? + a = 1 + puts "pointerof(1) returns false" unless pointerof(a).null? + + b = Pointer(Int32).new(0) + puts "Pointer(Int32).new(0) returns true" if b.null? + + # #+ + ptr1 = Pointer(Int32).new(1234) + puts "ptr1: address is 1234" if ptr1.address == 1234 + + # An Int32 occupies four bytes + puts "ptr1 + 1 (Int32, 4 bytes)" + ptr2 = ptr1 + 1 + puts "ptr2: address is 1238" if ptr2.address == 1238 + + + puts "ptr2 - 1 (Int32, 4 bytes)" + ptr3 = ptr2 - 1 + puts "ptr3: address is 1234" if ptr3.address == 1234 + + # self.null + ptr = Pointer(Int32).null + puts "address of self.null returns 0" if ptr.address == 0 + + # #clone + ptr = Pointer(Int32).new(123) + puts "pointer can be cloned" if ptr.clone.address == 123 +end + +private def reset(p1, p2) + p1.value = 10 + p2.value = 20 +end diff --git a/src/core_test/prelude.cr b/src/core_test/prelude.cr new file mode 100644 index 0000000..5baf775 --- /dev/null +++ b/src/core_test/prelude.cr @@ -0,0 +1,3 @@ +require "./string_test" +require "./pointer_test" +require "./comparable_test" diff --git a/src/core_test/string_test.cr b/src/core_test/string_test.cr new file mode 100644 index 0000000..2d167ff --- /dev/null +++ b/src/core_test/string_test.cr @@ -0,0 +1,67 @@ +def string_test + puts "---------------" + puts "Test for String" + puts "---------------" + + # #bytesize + # "hello".bytesize # => 5 + "你好".bytesize # => 6 + "你好".bytesize.times do + print "你好" # It's garbled, but print 6 times + end + puts + + # #unsafe_byte_at + "hello".unsafe_byte_at(0) #=> 104 + + # #empty? + puts "Not empty" unless "hello".empty? + + # #byte_index + "hello".byte_index('l'.ord) #=> 3 + "foo bar booz".byte_index('o'.ord, 3) #=> 9 + + # #each_byte + "ab".each_byte do |byte| #=> 'a' is 97, 'b' is 98 + (byte - 96).times do + print "each_byte" #=> So, print "each_byte" 3 times + end + end + puts + + # "你好".size #=> 2 + "你好".size.times do + print "你好" # It's garbled, but print 2 times + end + puts + + # #dup + dup = "foo".dup + puts dup #=> puts "foo" + + # #clone + clone = "foo".clone + puts clone #=> puts "foo" + + # Testing for Int + # #upto + sum = 0 + 1.upto(3) do |n| + sum += n + end + sum.times do + print "upto" #=> print "upto" 6 times + end + puts + + # #downto + sum = 0 + 3.downto(1) do |n| + sum += n + end + sum.times do + print "downto" #=> print "downto" 6 times + end + puts + +end diff --git a/src/core_test/test.cr b/src/core_test/test.cr new file mode 100644 index 0000000..fd9ab13 --- /dev/null +++ b/src/core_test/test.cr @@ -0,0 +1,7 @@ +require "./prelude" + +def core_test + string_test + pointer_test + # comparable_test +end diff --git a/src/kernel/main.cr b/src/kernel/main.cr index 50cbbac..04d79ed 100644 --- a/src/kernel/main.cr +++ b/src/kernel/main.cr @@ -7,8 +7,6 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. -require "../core/prelude" -require "./prelude" require "./utero_init" print "24: This line uses print method + \\n\n" @@ -34,46 +32,7 @@ cprint(LibU.dummy_exception) # This would fail... # LibU.create_foo -# puts "---------strlen------------" -# ab = "abc" -# ab_len = LibString.strlen(ab.as(LibCR::Char*)) -# ab_len.times do -# print "3 bytes " -# end -# -# puts -# -# ai = "あい" -# ai_len1 = LibString.strlen(ai.as(LibCR::Char*)) -# ai_len1.times do -# print "6 bytes " -# end -# puts - -# puts -# puts "---------memcmp------------" -# puts "Comparing 'abcx' and 'abcv' returns 2" -# ptr1 = "abcx" -# ptr2 = "abcv" -# -# result = LibCR.memcmp(ptr1.as(Void*), ptr2.as(Void*), 4 * sizeof(String)) -# puts "Displays 'memcmp' twice" -# result.times do -# puts "memcmp" -# end - -# puts -# puts "---------strcmp------------" -# str1 = "abcde" -# str2 = "abcdf" -# puts "Comparing 'abcde' and 'abcde'" -# strcmp_result = LibString.strcmp(str1.as(LibCR::Char*), str1.as(LibCR::Char*)) -# puts "returns 0" if strcmp_result == 0 -# -# puts "Comparing 'abcde' and 'abcdf'" -# strcmp_result = LibString.strcmp(str1.as(LibCR::Char*), str2.as(LibCR::Char*)) -# puts "returns -1" if strcmp_result == -1 -# -# puts "Comparing 'abcdf' and 'abcde'" -# strcmp_result = LibString.strcmp(str2.as(LibCR::Char*), str1.as(LibCR::Char*)) -# puts "returns 1" if strcmp_result == 1 +# Calling core test method +# Uncomment these lines when you test +# clear +# core_test diff --git a/src/kernel/prelude.cr b/src/kernel/prelude.cr index 5023bfe..40115e3 100644 --- a/src/kernel/prelude.cr +++ b/src/kernel/prelude.cr @@ -7,6 +7,9 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. +# Core libraries +require "../core/prelude" + # Order-dependent list require "./lib_u" require "./lib_u/**" diff --git a/src/kernel/utero_init.cr b/src/kernel/utero_init.cr index 4714441..b26d737 100644 --- a/src/kernel/utero_init.cr +++ b/src/kernel/utero_init.cr @@ -1,5 +1,6 @@ -# TODO: Initializing struct Scrn here to make it understand easier? +require "./prelude" require "./scrn" +require "../core_test/test" lib LibU fun make_kernel_info : LibU::Char*