diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
commit | be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch) | |
tree | 9754ff1ca740f6346cf8483ec915d4054bc5da2d /web/server/h2o/libh2o/deps/mruby/mrbgems | |
parent | Initial commit. (diff) | |
download | netdata-upstream.tar.xz netdata-upstream.zip |
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
139 files changed, 31064 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox b/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox new file mode 100644 index 00000000..64f05de1 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox @@ -0,0 +1,79 @@ +MRuby::GemBox.new do |conf| + # Use standard Kernel#sprintf method + conf.gem :core => "mruby-sprintf" + + # Use standard print/puts/p + conf.gem :core => "mruby-print" + + # Use standard Math module + conf.gem :core => "mruby-math" + + # Use standard Time class + conf.gem :core => "mruby-time" + + # Use standard Struct class + conf.gem :core => "mruby-struct" + + # Use Enumerable module extension + conf.gem :core => "mruby-enum-ext" + + # Use String class extension + conf.gem :core => "mruby-string-ext" + + # Use Numeric class extension + conf.gem :core => "mruby-numeric-ext" + + # Use Array class extension + conf.gem :core => "mruby-array-ext" + + # Use Hash class extension + conf.gem :core => "mruby-hash-ext" + + # Use Range class extension + conf.gem :core => "mruby-range-ext" + + # Use Proc class extension + conf.gem :core => "mruby-proc-ext" + + # Use Symbol class extension + conf.gem :core => "mruby-symbol-ext" + + # Use Random class + conf.gem :core => "mruby-random" + + # Use Object class extension + conf.gem :core => "mruby-object-ext" + + # Use ObjectSpace class + conf.gem :core => "mruby-objectspace" + + # Use Fiber class + conf.gem :core => "mruby-fiber" + + # Use Enumerator class (require mruby-fiber) + conf.gem :core => "mruby-enumerator" + + # Use Enumerator::Lazy class (require mruby-enumerator) + conf.gem :core => "mruby-enum-lazy" + + # Use toplevel object (main) methods extension + conf.gem :core => "mruby-toplevel-ext" + + # Generate mirb command + conf.gem :core => "mruby-bin-mirb" + + # Generate mruby command + conf.gem :core => "mruby-bin-mruby" + + # Generate mruby-strip command + conf.gem :core => "mruby-bin-strip" + + # Use Kernel module extension + conf.gem :core => "mruby-kernel-ext" + + # Use class/module extension + conf.gem :core => "mruby-class-ext" + + # Use mruby-compiler to build other mrbgems + conf.gem :core => "mruby-compiler" +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox b/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox new file mode 100644 index 00000000..9a5b7081 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox @@ -0,0 +1,9 @@ +MRuby::GemBox.new do |conf| + conf.gem :core => "mruby-sprintf" + conf.gem :core => "mruby-print" + + Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| + g = File.basename File.dirname x + conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/ + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake new file mode 100644 index 00000000..882caf1a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-array-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Array class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb new file mode 100644 index 00000000..e28e5238 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb @@ -0,0 +1,811 @@ +class Array + ## + # call-seq: + # Array.try_convert(obj) -> array or nil + # + # Tries to convert +obj+ into an array, using +to_ary+ method. + # converted array or +nil+ if +obj+ cannot be converted for any reason. + # This method can be used to check if an argument is an array. + # + # Array.try_convert([1]) #=> [1] + # Array.try_convert("1") #=> nil + # + # if tmp = Array.try_convert(arg) + # # the argument is an array + # elsif tmp = String.try_convert(arg) + # # the argument is a string + # end + # + def self.try_convert(obj) + if obj.respond_to?(:to_ary) + obj.to_ary + else + nil + end + end + + ## + # call-seq: + # ary.uniq! -> ary or nil + # ary.uniq! { |item| ... } -> ary or nil + # + # Removes duplicate elements from +self+. + # Returns <code>nil</code> if no changes are made (that is, no + # duplicates are found). + # + # a = [ "a", "a", "b", "b", "c" ] + # a.uniq! #=> ["a", "b", "c"] + # b = [ "a", "b", "c" ] + # b.uniq! #=> nil + # c = [["student","sam"], ["student","george"], ["teacher","matz"]] + # c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] + # + def uniq!(&block) + ary = self.dup + result = [] + if block + hash = {} + while ary.size > 0 + val = ary.shift + key = block.call(val) + hash[key] = val unless hash.has_key?(key) + end + hash.each_value do |value| + result << value + end + else + while ary.size > 0 + result << ary.shift + ary.delete(result.last) + end + end + if result.size == self.size + nil + else + self.replace(result) + end + end + + ## + # call-seq: + # ary.uniq -> new_ary + # ary.uniq { |item| ... } -> new_ary + # + # Returns a new array by removing duplicate values in +self+. + # + # a = [ "a", "a", "b", "b", "c" ] + # a.uniq #=> ["a", "b", "c"] + # + # b = [["student","sam"], ["student","george"], ["teacher","matz"]] + # b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] + # + def uniq(&block) + ary = self.dup + ary.uniq!(&block) + ary + end + + ## + # call-seq: + # ary - other_ary -> new_ary + # + # Array Difference---Returns a new array that is a copy of + # the original array, removing any items that also appear in + # <i>other_ary</i>. (If you need set-like behavior, see the + # library class Set.) + # + # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] + # + def -(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + hash = {} + array = [] + idx = 0 + len = elem.size + while idx < len + hash[elem[idx]] = true + idx += 1 + end + idx = 0 + len = size + while idx < len + v = self[idx] + array << v unless hash[v] + idx += 1 + end + array + end + + ## + # call-seq: + # ary | other_ary -> new_ary + # + # Set Union---Returns a new array by joining this array with + # <i>other_ary</i>, removing duplicates. + # + # [ "a", "b", "c" ] | [ "c", "d", "a" ] + # #=> [ "a", "b", "c", "d" ] + # + def |(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + ary = self + elem + ary.uniq! or ary + end + + ## + # call-seq: + # ary & other_ary -> new_ary + # + # Set Intersection---Returns a new array + # containing elements common to the two arrays, with no duplicates. + # + # [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ] + # + def &(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + hash = {} + array = [] + idx = 0 + len = elem.size + while idx < len + hash[elem[idx]] = true + idx += 1 + end + idx = 0 + len = size + while idx < len + v = self[idx] + if hash[v] + array << v + hash.delete v + end + idx += 1 + end + array + end + + ## + # call-seq: + # ary.flatten -> new_ary + # ary.flatten(level) -> new_ary + # + # Returns a new array that is a one-dimensional flattening of this + # array (recursively). That is, for every element that is an array, + # extract its elements into the new array. If the optional + # <i>level</i> argument determines the level of recursion to flatten. + # + # s = [ 1, 2, 3 ] #=> [1, 2, 3] + # t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] + # a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] + # a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten(1) #=> [1, 2, 3, [4, 5]] + # + def flatten(depth=nil) + res = dup + res.flatten! depth + res + end + + ## + # call-seq: + # ary.flatten! -> ary or nil + # ary.flatten!(level) -> array or nil + # + # Flattens +self+ in place. + # Returns <code>nil</code> if no modifications were made (i.e., + # <i>ary</i> contains no subarrays.) If the optional <i>level</i> + # argument determines the level of recursion to flatten. + # + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten! #=> [1, 2, 3, 4, 5] + # a.flatten! #=> nil + # a #=> [1, 2, 3, 4, 5] + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten!(1) #=> [1, 2, 3, [4, 5]] + # + def flatten!(depth=nil) + modified = false + ar = [] + idx = 0 + len = size + while idx < len + e = self[idx] + if e.is_a?(Array) && (depth.nil? || depth > 0) + ar += e.flatten(depth.nil? ? nil : depth - 1) + modified = true + else + ar << e + end + idx += 1 + end + if modified + self.replace(ar) + else + nil + end + end + + ## + # call-seq: + # ary.compact -> new_ary + # + # Returns a copy of +self+ with all +nil+ elements removed. + # + # [ "a", nil, "b", nil, "c", nil ].compact + # #=> [ "a", "b", "c" ] + # + def compact + result = self.dup + result.compact! + result + end + + ## + # call-seq: + # ary.compact! -> ary or nil + # + # Removes +nil+ elements from the array. + # Returns +nil+ if no changes were made, otherwise returns + # <i>ary</i>. + # + # [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] + # [ "a", "b", "c" ].compact! #=> nil + # + def compact! + result = self.select { |e| !e.nil? } + if result.size == self.size + nil + else + self.replace(result) + end + end + + # for efficiency + def reverse_each(&block) + return to_enum :reverse_each unless block + + i = self.size - 1 + while i>=0 + block.call(self[i]) + i -= 1 + end + self + end + + NONE=Object.new + ## + # call-seq: + # ary.fetch(index) -> obj + # ary.fetch(index, default) -> obj + # ary.fetch(index) { |index| block } -> obj + # + # Tries to return the element at position +index+, but throws an IndexError + # exception if the referenced +index+ lies outside of the array bounds. This + # error can be prevented by supplying a second argument, which will act as a + # +default+ value. + # + # Alternatively, if a block is given it will only be executed when an + # invalid +index+ is referenced. + # + # Negative values of +index+ count from the end of the array. + # + # a = [ 11, 22, 33, 44 ] + # a.fetch(1) #=> 22 + # a.fetch(-1) #=> 44 + # a.fetch(4, 'cat') #=> "cat" + # a.fetch(100) { |i| puts "#{i} is out of bounds" } + # #=> "100 is out of bounds" + # + + def fetch(n=nil, ifnone=NONE, &block) + warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block + + idx = n + if idx < 0 + idx += size + end + if idx < 0 || size <= idx + return block.call(n) if block + if ifnone == NONE + raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}" + end + return ifnone + end + self[idx] + end + + ## + # call-seq: + # ary.fill(obj) -> ary + # ary.fill(obj, start [, length]) -> ary + # ary.fill(obj, range ) -> ary + # ary.fill { |index| block } -> ary + # ary.fill(start [, length] ) { |index| block } -> ary + # ary.fill(range) { |index| block } -> ary + # + # The first three forms set the selected elements of +self+ (which + # may be the entire array) to +obj+. + # + # A +start+ of +nil+ is equivalent to zero. + # + # A +length+ of +nil+ is equivalent to the length of the array. + # + # The last three forms fill the array with the value of the given block, + # which is passed the absolute index of each element to be filled. + # + # Negative values of +start+ count from the end of the array, where +-1+ is + # the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.fill("x") #=> ["x", "x", "x", "x"] + # a.fill("w", -1) #=> ["x", "x", "x", "w"] + # a.fill("z", 2, 2) #=> ["x", "x", "z", "z"] + # a.fill("y", 0..1) #=> ["y", "y", "z", "z"] + # a.fill { |i| i*i } #=> [0, 1, 4, 9] + # a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27] + # a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27] + # a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27] + # + + def fill(arg0=nil, arg1=nil, arg2=nil, &block) + if arg0.nil? && arg1.nil? && arg2.nil? && !block + raise ArgumentError, "wrong number of arguments (0 for 1..3)" + end + + beg = len = 0 + ary = [] + if block + if arg0.nil? && arg1.nil? && arg2.nil? + # ary.fill { |index| block } -> ary + beg = 0 + len = self.size + elsif !arg0.nil? && arg0.kind_of?(Range) + # ary.fill(range) { |index| block } -> ary + beg = arg0.begin + beg += self.size if beg < 0 + len = arg0.end + len += self.size if len < 0 + len += 1 unless arg0.exclude_end? + elsif !arg0.nil? + # ary.fill(start [, length] ) { |index| block } -> ary + beg = arg0 + beg += self.size if beg < 0 + if arg1.nil? + len = self.size + else + len = arg0 + arg1 + end + end + else + if !arg0.nil? && arg1.nil? && arg2.nil? + # ary.fill(obj) -> ary + beg = 0 + len = self.size + elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range) + # ary.fill(obj, range ) -> ary + beg = arg1.begin + beg += self.size if beg < 0 + len = arg1.end + len += self.size if len < 0 + len += 1 unless arg1.exclude_end? + elsif !arg0.nil? && !arg1.nil? + # ary.fill(obj, start [, length]) -> ary + beg = arg1 + beg += self.size if beg < 0 + if arg2.nil? + len = self.size + else + len = beg + arg2 + end + end + end + + i = beg + if block + while i < len + self[i] = block.call(i) + i += 1 + end + else + while i < len + self[i] = arg0 + i += 1 + end + end + self + end + + ## + # call-seq: + # ary.rotate(count=1) -> new_ary + # + # Returns a new array by rotating +self+ so that the element at +count+ is + # the first element of the new array. + # + # If +count+ is negative then it rotates in the opposite direction, starting + # from the end of +self+ where +-1+ is the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.rotate #=> ["b", "c", "d", "a"] + # a #=> ["a", "b", "c", "d"] + # a.rotate(2) #=> ["c", "d", "a", "b"] + # a.rotate(-3) #=> ["b", "c", "d", "a"] + + def rotate(count=1) + ary = [] + len = self.length + + if len > 0 + idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count + len.times do + ary << self[idx] + idx += 1 + idx = 0 if idx > len-1 + end + end + ary + end + + ## + # call-seq: + # ary.rotate!(count=1) -> ary + # + # Rotates +self+ in place so that the element at +count+ comes first, and + # returns +self+. + # + # If +count+ is negative then it rotates in the opposite direction, starting + # from the end of the array where +-1+ is the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.rotate! #=> ["b", "c", "d", "a"] + # a #=> ["b", "c", "d", "a"] + # a.rotate!(2) #=> ["d", "a", "b", "c"] + # a.rotate!(-3) #=> ["a", "b", "c", "d"] + + def rotate!(count=1) + self.replace(self.rotate(count)) + end + + ## + # call-seq: + # ary.delete_if { |item| block } -> ary + # ary.delete_if -> Enumerator + # + # Deletes every element of +self+ for which block evaluates to +true+. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Array#reject! + # + # If no block is given, an Enumerator is returned instead. + # + # scores = [ 97, 42, 75 ] + # scores.delete_if {|score| score < 80 } #=> [97] + + def delete_if(&block) + return to_enum :delete_if unless block + + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + self + end + + ## + # call-seq: + # ary.reject! { |item| block } -> ary or nil + # ary.reject! -> Enumerator + # + # Equivalent to Array#delete_if, deleting elements from +self+ for which the + # block evaluates to +true+, but returns +nil+ if no changes were made. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Enumerable#reject and Array#delete_if. + # + # If no block is given, an Enumerator is returned instead. + + def reject!(&block) + return to_enum :reject! unless block + + len = self.size + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + if self.size == len + nil + else + self + end + end + + ## + # call-seq: + # ary.insert(index, obj...) -> ary + # + # Inserts the given values before the element with the given +index+. + # + # Negative indices count backwards from the end of the array, where +-1+ is + # the last element. + # + # a = %w{ a b c d } + # a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] + # a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] + + def insert(idx, *args) + idx += self.size + 1 if idx < 0 + self[idx, 0] = args + self + end + + ## + # call-seq: + # ary.bsearch {|x| block } -> elem + # + # By using binary search, finds a value from this array which meets + # the given condition in O(log n) where n is the size of the array. + # + # You can use this method in two use cases: a find-minimum mode and + # a find-any mode. In either case, the elements of the array must be + # monotone (or sorted) with respect to the block. + # + # In find-minimum mode (this is a good choice for typical use case), + # the block must return true or false, and there must be an index i + # (0 <= i <= ary.size) so that: + # + # - the block returns false for any element whose index is less than + # i, and + # - the block returns true for any element whose index is greater + # than or equal to i. + # + # This method returns the i-th element. If i is equal to ary.size, + # it returns nil. + # + # ary = [0, 4, 7, 10, 12] + # ary.bsearch {|x| x >= 4 } #=> 4 + # ary.bsearch {|x| x >= 6 } #=> 7 + # ary.bsearch {|x| x >= -1 } #=> 0 + # ary.bsearch {|x| x >= 100 } #=> nil + # + # In find-any mode (this behaves like libc's bsearch(3)), the block + # must return a number, and there must be two indices i and j + # (0 <= i <= j <= ary.size) so that: + # + # - the block returns a positive number for ary[k] if 0 <= k < i, + # - the block returns zero for ary[k] if i <= k < j, and + # - the block returns a negative number for ary[k] if + # j <= k < ary.size. + # + # Under this condition, this method returns any element whose index + # is within i...j. If i is equal to j (i.e., there is no element + # that satisfies the block), this method returns nil. + # + # ary = [0, 4, 7, 10, 12] + # # try to find v such that 4 <= v < 8 + # ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7 + # # try to find v such that 8 <= v < 10 + # ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil + # + # You must not mix the two modes at a time; the block must always + # return either true/false, or always return a number. It is + # undefined which value is actually picked up at each iteration. + + def bsearch(&block) + return to_enum :bsearch unless block + + if idx = bsearch_index(&block) + self[idx] + else + nil + end + end + + ## + # call-seq: + # ary.bsearch_index {|x| block } -> int or nil + # + # By using binary search, finds an index of a value from this array which + # meets the given condition in O(log n) where n is the size of the array. + # + # It supports two modes, depending on the nature of the block and they are + # exactly the same as in the case of #bsearch method with the only difference + # being that this method returns the index of the element instead of the + # element itself. For more details consult the documentation for #bsearch. + + def bsearch_index(&block) + return to_enum :bsearch_index unless block + + low = 0 + high = size + satisfied = false + + while low < high + mid = ((low+high)/2).truncate + res = block.call self[mid] + + case res + when 0 # find-any mode: Found! + return mid + when Numeric # find-any mode: Continue... + in_lower_half = res < 0 + when true # find-min mode + in_lower_half = true + satisfied = true + when false, nil # find-min mode + in_lower_half = false + else + raise TypeError, 'invalid block result (must be numeric, true, false or nil)' + end + + if in_lower_half + high = mid + else + low = mid + 1 + end + end + + satisfied ? low : nil + end + + ## + # call-seq: + # ary.delete_if { |item| block } -> ary + # ary.delete_if -> Enumerator + # + # Deletes every element of +self+ for which block evaluates to +true+. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Array#reject! + # + # If no block is given, an Enumerator is returned instead. + # + # scores = [ 97, 42, 75 ] + # scores.delete_if {|score| score < 80 } #=> [97] + + def delete_if(&block) + return to_enum :delete_if unless block + + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + self + end + + ## + # call-seq: + # ary.keep_if { |item| block } -> ary + # ary.keep_if -> Enumerator + # + # Deletes every element of +self+ for which the given block evaluates to + # +false+. + # + # See also Array#select! + # + # If no block is given, an Enumerator is returned instead. + # + # a = [1, 2, 3, 4, 5] + # a.keep_if { |val| val > 3 } #=> [4, 5] + + def keep_if(&block) + return to_enum :keep_if unless block + + idx = 0 + len = self.size + while idx < self.size do + if block.call(self[idx]) + idx += 1 + else + self.delete_at(idx) + end + end + self + end + + ## + # call-seq: + # ary.select! {|item| block } -> ary or nil + # ary.select! -> Enumerator + # + # Invokes the given block passing in successive elements from +self+, + # deleting elements for which the block returns a +false+ value. + # + # If changes were made, it will return +self+, otherwise it returns +nil+. + # + # See also Array#keep_if + # + # If no block is given, an Enumerator is returned instead. + + def select!(&block) + return to_enum :select! unless block + + result = [] + idx = 0 + len = size + while idx < len + elem = self[idx] + result << elem if block.call(elem) + idx += 1 + end + return nil if len == result.size + self.replace(result) + end + + ## + # call-seq: + # ary.index(val) -> int or nil + # ary.index {|item| block } -> int or nil + # + # Returns the _index_ of the first object in +ary+ such that the object is + # <code>==</code> to +obj+. + # + # If a block is given instead of an argument, returns the _index_ of the + # first object for which the block returns +true+. Returns +nil+ if no + # match is found. + # + # ISO 15.2.12.5.14 + def index(val=NONE, &block) + return to_enum(:find_index, val) if !block && val == NONE + + if block + idx = 0 + len = size + while idx < len + return idx if block.call self[idx] + idx += 1 + end + else + return self.__ary_index(val) + end + nil + end + + ## + # call-seq: + # ary.to_ary -> ary + # + # Returns +self+. + # + def to_ary + self + end + + ## + # call-seq: + # ary.dig(idx, ...) -> object + # + # Extracts the nested value specified by the sequence of <i>idx</i> + # objects by calling +dig+ at each step, returning +nil+ if any + # intermediate step is +nil+. + # + def dig(idx,*args) + n = self[idx] + if args.size > 0 + n&.dig(*args) + else + n + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c new file mode 100644 index 00000000..e99599b0 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c @@ -0,0 +1,244 @@ +#include <mruby.h> +#include <mruby/value.h> +#include <mruby/array.h> +#include <mruby/range.h> +#include <mruby/hash.h> + +/* + * call-seq: + * ary.assoc(obj) -> new_ary or nil + * + * Searches through an array whose elements are also arrays + * comparing _obj_ with the first element of each contained array + * using obj.==. + * Returns the first contained array that matches (that + * is, the first associated array), + * or +nil+ if no match is found. + * See also <code>Array#rassoc</code>. + * + * s1 = [ "colors", "red", "blue", "green" ] + * s2 = [ "letters", "a", "b", "c" ] + * s3 = "foo" + * a = [ s1, s2, s3 ] + * a.assoc("letters") #=> [ "letters", "a", "b", "c" ] + * a.assoc("foo") #=> nil + */ + +static mrb_value +mrb_ary_assoc(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, k; + + mrb_get_args(mrb, "o", &k); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); + if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 && + mrb_equal(mrb, RARRAY_PTR(v)[0], k)) + return v; + } + return mrb_nil_value(); +} + +/* + * call-seq: + * ary.rassoc(obj) -> new_ary or nil + * + * Searches through the array whose elements are also arrays. Compares + * _obj_ with the second element of each contained array using + * <code>==</code>. Returns the first contained array that matches. See + * also <code>Array#assoc</code>. + * + * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] + * a.rassoc("two") #=> [2, "two"] + * a.rassoc("four") #=> nil + */ + +static mrb_value +mrb_ary_rassoc(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, value; + + mrb_get_args(mrb, "o", &value); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = RARRAY_PTR(ary)[i]; + if (mrb_type(v) == MRB_TT_ARRAY && + RARRAY_LEN(v) > 1 && + mrb_equal(mrb, RARRAY_PTR(v)[1], value)) + return v; + } + return mrb_nil_value(); +} + +/* + * call-seq: + * ary.at(index) -> obj or nil + * + * Returns the element at _index_. A + * negative index counts from the end of +self+. Returns +nil+ + * if the index is out of range. See also <code>Array#[]</code>. + * + * a = [ "a", "b", "c", "d", "e" ] + * a.at(0) #=> "a" + * a.at(-1) #=> "e" + */ + +static mrb_value +mrb_ary_at(mrb_state *mrb, mrb_value ary) +{ + mrb_int pos; + mrb_get_args(mrb, "i", &pos); + + return mrb_ary_entry(ary, pos); +} + +static mrb_value +mrb_ary_values_at(mrb_state *mrb, mrb_value self) +{ + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + + return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); +} + +/* + * call-seq: + * ary.to_h -> Hash + * + * Returns the result of interpreting <i>aray</i> as an array of + * <tt>[key, value]</tt> paris. + * + * [[:foo, :bar], [1, 2]].to_h + * # => {:foo => :bar, 1 => 2} + * + */ + +static mrb_value +mrb_ary_to_h(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, hash; + + hash = mrb_hash_new_capa(mrb, 0); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + mrb_value elt = RARRAY_PTR(ary)[i]; + v = mrb_check_array_type(mrb, elt); + + if (mrb_nil_p(v)) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)", + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, elt)), + mrb_fixnum_value(i) + ); + } + + if (RARRAY_LEN(v) != 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)", + mrb_fixnum_value(i), + mrb_fixnum_value(RARRAY_LEN(v)) + ); + } + + mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]); + } + + return hash; +} + +/* + * call-seq: + * ary.slice!(index) -> obj or nil + * ary.slice!(start, length) -> new_ary or nil + * ary.slice!(range) -> new_ary or nil + * + * Deletes the element(s) given by an +index+ (optionally up to +length+ + * elements) or by a +range+. + * + * Returns the deleted object (or objects), or +nil+ if the +index+ is out of + * range. + * + * a = [ "a", "b", "c" ] + * a.slice!(1) #=> "b" + * a #=> ["a", "c"] + * a.slice!(-1) #=> "c" + * a #=> ["a"] + * a.slice!(100) #=> nil + * a #=> ["a"] + */ + +static mrb_value +mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int i, j, k, len, alen = ARY_LEN(a); + mrb_value index; + mrb_value val; + mrb_value *ptr; + mrb_value ary; + + mrb_ary_modify(mrb, a); + + if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { + switch (mrb_type(index)) { + case MRB_TT_RANGE: + if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) { + goto delete_pos_len; + } + else { + return mrb_nil_value(); + } + case MRB_TT_FIXNUM: + val = mrb_funcall(mrb, self, "delete_at", 1, index); + return val; + default: + val = mrb_funcall(mrb, self, "delete_at", 1, index); + return val; + } + } + + i = mrb_fixnum(index); + delete_pos_len: + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); + if (len < 0) return mrb_nil_value(); + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; + + ary = mrb_ary_new_capa(mrb, len); + ptr = ARY_PTR(a); + for (j = i, k = 0; k < len; ++j, ++k) { + mrb_ary_push(mrb, ary, ptr[j]); + } + + ptr += i; + for (j = i; j < alen - len; ++j) { + *ptr = *(ptr+len); + ++ptr; + } + + mrb_ary_resize(mrb, self, alen - len); + return ary; +} + +void +mrb_mruby_array_ext_gem_init(mrb_state* mrb) +{ + struct RClass * a = mrb->array_class; + + mrb_define_method(mrb, a, "assoc", mrb_ary_assoc, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); + mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0)); + mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY()); +} + +void +mrb_mruby_array_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb new file mode 100644 index 00000000..c0db1b1c --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb @@ -0,0 +1,383 @@ +## +# Array(Ext) Test + +assert("Array.try_convert") do + assert_nil Array.try_convert(0) + assert_nil Array.try_convert(nil) + assert_equal [], Array.try_convert([]) + assert_equal [1,2,3], Array.try_convert([1,2,3]) +end + +assert("Array#assoc") do + s1 = [ "colors", "red", "blue", "green" ] + s2 = [ "letters", "a", "b", "c" ] + s3 = "foo" + a = [ s1, s2, s3 ] + + assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters") + assert_nil a.assoc("foo") +end + +assert("Array#at") do + a = [ "a", "b", "c", "d", "e" ] + assert_equal "a", a.at(0) + assert_equal "e", a.at(-1) +end + +assert("Array#rassoc") do + a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] + + assert_equal [2, "two"], a.rassoc("two") + assert_nil a.rassoc("four") +end + +assert("Array#uniq!") do + a = [1, 2, 3, 1] + a.uniq! + assert_equal [1, 2, 3], a + + b = [ "a", "b", "c" ] + assert_nil b.uniq! + + c = [["student","sam"], ["student","george"], ["teacher","matz"]] + assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first } + + d = [["student","sam"], ["teacher","matz"]] + assert_nil d.uniq! { |s| s.first } +end + +assert("Array#uniq") do + a = [1, 2, 3, 1] + assert_equal [1, 2, 3], a.uniq + assert_equal [1, 2, 3, 1], a + + b = [["student","sam"], ["student","george"], ["teacher","matz"]] + assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first } +end + +assert("Array#-") do + a = [1, 2, 3, 1] + b = [1] + c = 1 + + assert_raise(TypeError) { a - c } + assert_equal [2, 3], (a - b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#|") do + a = [1, 2, 3, 1] + b = [1, 4] + c = 1 + + assert_raise(TypeError) { a | c } + assert_equal [1, 2, 3, 4], (a | b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#&") do + a = [1, 2, 3, 1] + b = [1, 4] + c = 1 + + assert_raise(TypeError) { a & c } + assert_equal [1], (a & b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#flatten") do + assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten + assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0) + assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1) + assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2) + assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3) + assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4) + assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5) +end + +assert("Array#flatten!") do + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten! +end + +assert("Array#compact") do + a = [1, nil, "2", nil, :t, false, nil] + assert_equal [1, "2", :t, false], a.compact + assert_equal [1, nil, "2", nil, :t, false, nil], a +end + +assert("Array#compact!") do + a = [1, nil, "2", nil, :t, false, nil] + a.compact! + assert_equal [1, "2", :t, false], a +end + +assert("Array#fetch") do + a = [ 11, 22, 33, 44 ] + assert_equal 22, a.fetch(1) + assert_equal 44, a.fetch(-1) + assert_equal 'cat', a.fetch(4, 'cat') + ret = 0 + a.fetch(100) { |i| ret = i } + assert_equal 100, ret + assert_raise(IndexError) { a.fetch(100) } +end + +assert("Array#fill") do + a = [ "a", "b", "c", "d" ] + assert_equal ["x", "x", "x", "x"], a.fill("x") + assert_equal ["x", "x", "x", "w"], a.fill("w", -1) + assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2) + assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1) + assert_equal [0, 1, 4, 9], a.fill { |i| i*i } + assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i } + assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 } + assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 } + assert_raise(ArgumentError) { a.fill } + + assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1)) + assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3)) + assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4)) + assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4)) + assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2)) + assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2)) + assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10}) + assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10}) + assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10}) + assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10}) + + assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1) + assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1) + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2) + assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2) + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0) +end + +assert("Array#reverse_each") do + a = [ "a", "b", "c", "d" ] + b = [] + a.reverse_each do |i| + b << i + end + assert_equal [ "d", "c", "b", "a" ], b + + if Object.const_defined?(:Enumerator) + assert_equal [ "d", "c", "b", "a" ], a.reverse_each.to_a + else + true + end +end + +assert("Array#rotate") do + a = ["a", "b", "c", "d"] + assert_equal ["b", "c", "d", "a"], a.rotate + assert_equal ["a", "b", "c", "d"], a + assert_equal ["c", "d", "a", "b"], a.rotate(2) + assert_equal ["b", "c", "d", "a"], a.rotate(-3) + assert_equal ["c", "d", "a", "b"], a.rotate(10) + assert_equal [], [].rotate +end + +assert("Array#rotate!") do + a = ["a", "b", "c", "d"] + assert_equal ["b", "c", "d", "a"], a.rotate! + assert_equal ["b", "c", "d", "a"], a + assert_equal ["d", "a", "b", "c"], a.rotate!(2) + assert_equal ["a", "b", "c", "d"], a.rotate!(-3) + assert_equal ["c", "d", "a", "b"], a.rotate(10) + assert_equal [], [].rotate! +end + +assert("Array#delete_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.delete_if { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.delete_if { true } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3], a.delete_if { |i| i > 3 } + assert_equal [1, 2, 3], a +end + +assert("Array#reject!") do + a = [1, 2, 3, 4, 5] + assert_nil a.reject! { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.reject! { true } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3], a.reject! { |val| val > 3 } + assert_equal [1, 2, 3], a +end + +assert("Array#insert") do + a = ["a", "b", "c", "d"] + assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99) + assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3) + + b = ["a", "b", "c", "d"] + assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99) +end + +assert("Array#bsearch") do + # Find minimum mode + a = [0, 2, 4] + assert_equal 0, a.bsearch{ |x| x >= -1 } + assert_equal 0, a.bsearch{ |x| x >= 0 } + assert_equal 2, a.bsearch{ |x| x >= 1 } + assert_equal 2, a.bsearch{ |x| x >= 2 } + assert_equal 4, a.bsearch{ |x| x >= 3 } + assert_equal 4, a.bsearch{ |x| x >= 4 } + assert_nil a.bsearch{ |x| x >= 5 } + + # Find any mode + a = [0, 4, 8] + def between(lo, x, hi) + if x < lo + 1 + elsif x > hi + -1 + else + 0 + end + end + assert_nil a.bsearch{ |x| between(-3, x, -1) } + assert_equal 0, a.bsearch{ |x| between(-1, x, 1) } + assert_nil a.bsearch{ |x| between( 1, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 3, x, 5) } + assert_nil a.bsearch{ |x| between( 5, x, 7) } + assert_equal 8, a.bsearch{ |x| between( 7, x, 9) } + assert_nil a.bsearch{ |x| between( 9, x, 11) } + + assert_equal 0, a.bsearch{ |x| between( 0, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 0, x, 4) } + assert_equal 4, a.bsearch{ |x| between( 4, x, 8) } + assert_equal 8, a.bsearch{ |x| between( 5, x, 8) } + + # Invalid block result + assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do + a.bsearch{ 'I like to watch the world burn' } + end +end + +assert("Array#bsearch_index") do + # tested through Array#bsearch +end + +assert("Array#delete_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.delete_if { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.delete_if { true } + assert_equal [], a + + a = [ 1, 2, 3, 4, 5 ] + assert_equal [1, 2, 3], a.delete_if { |val| val > 3 } +end + +assert("Array#keep_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.keep_if { true } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.keep_if { false } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [4, 5], a.keep_if { |val| val > 3 } + assert_equal [4, 5], a +end + +assert("Array#select!") do + a = [1, 2, 3, 4, 5] + assert_nil a.select! { true } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.select! { false } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [4, 5], a.select! { |val| val > 3 } + assert_equal [4, 5], a +end + +assert('Array#values_at') do + a = %w{red green purple white none} + + assert_equal %w{red purple none}, a.values_at(0, 2, 4) + assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7) + assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7) + assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) + assert_raise(TypeError) { a.values_at 'tt' } +end + +assert('Array#to_h') do + assert_equal({}, [].to_h) + assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) + + assert_raise(TypeError) { [1].to_h } + assert_raise(ArgumentError) { [[1]].to_h } +end + +assert('Array#to_h (Modified)') do + class A + def to_ary + $a.clear + nil + end + end + $a = [A.new] + assert_raise(TypeError) { $a.to_h } +end + +assert("Array#index (block)") do + assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } + assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } +end + +assert("Array#to_ary") do + assert_equal [], [].to_ary + assert_equal [1,2,3], [1,2,3].to_ary +end + +assert("Array#dig") do + h = [[[1]], 0] + assert_equal(1, h.dig(0, 0, 0)) + assert_nil(h.dig(2, 0)) + assert_raise(TypeError) {h.dig(:a)} +end + +assert("Array#slice!") do + a = [1, 2, 3] + b = a.slice!(0) + c = [1, 2, 3, 4, 5] + d = c.slice!(0, 2) + e = [1, 2, 3, 4, 5] + f = e.slice!(1..3) + g = [1, 2, 3] + h = g.slice!(-1) + i = [1, 2, 3] + j = i.slice!(0, -1) + + assert_equal(a, [2, 3]) + assert_equal(b, 1) + assert_equal(c, [3, 4, 5]) + assert_equal(d, [1, 2]) + assert_equal(e, [1, 5]) + assert_equal(f, [2, 3, 4]) + assert_equal(g, [1, 2]) + assert_equal(h, 3) + assert_equal(i, [1, 2, 3]) + assert_equal(j, nil) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb new file mode 100644 index 00000000..26f3138a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb @@ -0,0 +1,286 @@ +require 'open3' +require 'tempfile' + +class BinTest_MrubyBinDebugger + @debug1=false + @debug2=true + @debug3=true + def self.test(rubysource, testcase) + script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) + + # .rb + script.write rubysource + script.flush + + # compile + `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"` + + # add mrdb quit + testcase << {:cmd=>"quit"} + + stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" + + ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd| + o, s = Open3.capture2(cmd, :stdin_data => stdin_data) + + exp_vals = testcase.map{|t| t.fetch(:exp, nil)} + unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)} + +if @debug1 + o.split("\n").each_with_index do |i,actual| + p [i,actual] + end +end + # compare actual / expected + o.split("\n").each do |actual| + next if actual.empty? + exp = exp_vals.shift +if @debug2 + a = true + a = actual.include?(exp) unless exp.nil? + p [actual, exp] unless a +end + assert_true actual.include?(exp) unless exp.nil? + end + # compare actual / unexpected + o.split("\n").each do |actual| + next if actual.empty? + unexp = unexp_vals.shift +if @debug3 + a = false + a = actual.include?(unexp) unless unexp.nil? + p [actual, unexp] if a +end + assert_false actual.include?(unexp) unless unexp.nil? + end + end + end +end + +INVCMD = "invalid command" + +assert('mruby-bin-debugger(mrdb) command line') do + # ruby source + src = "foo = 'foo'\n" + + str = "" + 103.times { + str += "1234567890" + } + cmd = "p a=#{str}" + + # test case + BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}]) +end + +assert('mruby-bin-debugger(mrdb) command: "break"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"b", :unexp=>INVCMD} + tc << {:cmd=>"br", :unexp=>INVCMD} + tc << {:cmd=>"brea", :unexp=>INVCMD} + tc << {:cmd=>"break", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "continue"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}]) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "delete"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"d 1", :unexp=>INVCMD} + tc << {:cmd=>"de 1", :unexp=>INVCMD} + tc << {:cmd=>"delet 1", :unexp=>INVCMD} + tc << {:cmd=>"delete 1", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "disable"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"dis", :unexp=>INVCMD} + tc << {:cmd=>"disa", :unexp=>INVCMD} + tc << {:cmd=>"disabl", :unexp=>INVCMD} + tc << {:cmd=>"disable", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "enable"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"en", :unexp=>INVCMD} + tc << {:cmd=>"ena", :unexp=>INVCMD} + tc << {:cmd=>"enabl", :unexp=>INVCMD} + tc << {:cmd=>"enable", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "eval"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"ev", :unexp=>INVCMD} + tc << {:cmd=>"eva", :unexp=>INVCMD} + tc << {:cmd=>"eval", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "help"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"h", :unexp=>INVCMD} + tc << {:cmd=>"he", :unexp=>INVCMD} + tc << {:cmd=>"hel", :unexp=>INVCMD} + tc << {:cmd=>"help", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"i b", :unexp=>INVCMD} + tc << {:cmd=>"in b", :unexp=>INVCMD} + tc << {:cmd=>"i br", :unexp=>INVCMD} + tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD} + tc << {:cmd=>"info breakpoints", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "list"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"l", :unexp=>INVCMD} + tc << {:cmd=>"li", :unexp=>INVCMD} + tc << {:cmd=>"lis", :unexp=>INVCMD} + tc << {:cmd=>"list", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "print"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"p", :unexp=>INVCMD} + tc << {:cmd=>"pr", :unexp=>INVCMD} + tc << {:cmd=>"prin", :unexp=>INVCMD} + tc << {:cmd=>"print", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "quit"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}]) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "run"') do + # ruby source + src = "foo = 'foo'\n" + + # test case + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}]) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}]) +end + +assert('mruby-bin-debugger(mrdb) command: "step"') do + # ruby source + src = <<"SRC" +while true + foo = 'foo' +end +SRC + + # test case + tc = [] + tc << {:cmd=>"s", :unexp=>INVCMD} + tc << {:cmd=>"st", :unexp=>INVCMD} + tc << {:cmd=>"ste", :unexp=>INVCMD} + tc << {:cmd=>"step", :unexp=>INVCMD} + BinTest_MrubyBinDebugger.test(src, tc) + + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}]) + BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}]) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb new file mode 100644 index 00000000..1bc96c47 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb @@ -0,0 +1,701 @@ +require 'open3' +require 'tempfile' + +class BinTest_MrubyBinDebugger + @debug1=false + @debug2=true + def self.test(rubysource, testcase) + script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) + + # .rb + script.write rubysource + script.flush + + # compile + `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"` + + # add mrdb quit + testcase << {:cmd=>"quit"} + + stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" + + ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd| + o, s = Open3.capture2(cmd, :stdin_data => stdin_data) + + exp_vals = testcase.map{|t| t.fetch(:exp, nil)} +=begin +if @debug1 + o.split("\n").each_with_index do |i,actual| + p [i,actual] + end +end + # compare actual / expected + o.split("\n").each do |actual| + next if actual.empty? + exp = exp_vals.shift +if @debug2 + a = true + a = actual.include?(exp) unless exp.nil? + p [actual, exp] unless a +end + assert_true actual.include?(exp) unless exp.nil? + end +=end + idx = 0 + exp_vals.each do |exp| + next if exp.nil? + idx = o.index(exp, idx) + assert_false idx.nil? + break unless idx + idx += 1 + end + end + end +end + +assert('mruby-bin-debugger(print) invalid arguments') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"p", :exp=>"Parameter not specified."} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) nomal') do + # ruby source + src = <<"SRC" +foo = 'foo' +bar = foo +baz = bar +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'} + tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'} + tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'} + tc << {:cmd=>"s"} + tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) error') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'} + tc << {:cmd=>"p bar", :exp=>'$2 = NoMethodError'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +# Kernel#instance_eval(string) does't work multiple statements. +=begin +assert('mruby-bin-debugger(print) multiple statements') do + # ruby source + src = <<"SRC" +x = 0 +y = 0 +z = 0 +SRC + + # test case + tc = [] + tc << {:cmd=>"s",} + tc << {:cmd=>"p x=1;x+=2", :exp=>"3"} + tc << {:cmd=>"s",} + tc << {:cmd=>"p x", :exp=>"3"} + + BinTest_MrubyBinDebugger.test(src, tc) +end +=end + +assert('mruby-bin-debugger(print) scope:top') do + # ruby source (bp is break point) + src = "bp=nil\n" + + # test case + tc = [] + tc << {:cmd=>"p self", :exp=>'$1 = main'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) scope:class') do + # ruby source (bp is break point) + src = <<"SRC" +class TestClassScope + bp = nil +end +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) scope:module') do + # ruby source (bp is break point) + src = <<"SRC" +class TestModuleScope + bp = nil +end +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) scope:instance method') do + # ruby source (bp is break point) + src = <<"SRC" +class TestMethodScope + def m + bp = nil + end +end +TestMethodScope.new.m +SRC + + tc = [] + tc << {:cmd=>"b 3"} + tc << {:cmd=>"r"} + tc << {:cmd=>"p self", :exp=>'$1 = #<TestMethodScope:'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) scope:class method') do + # ruby source (bp is break point) + src = <<"SRC" +class TestClassMethodScope + def self.cm + bp = nil + end +end +TestClassMethodScope.cm +SRC + + tc = [] + tc << {:cmd=>"b 3"} + tc << {:cmd=>"r"} + tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) scope:block') do + # ruby source (bp is break point) + src = <<"SRC" +1.times do + bp = nil +end +class TestBlockScope + 1.times do + bp = nil + end + def m + 1.times do + bp = nil + end + end +end +TestBlockScope.new.m +SRC + + tc = [] + tc << {:cmd=>"b 2"} + tc << {:cmd=>"b 6"} + tc << {:cmd=>"b 10"} + tc << {:cmd=>"c"} + tc << {:cmd=>"p self", :exp=>'$1 = main'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p self", :exp=>'$3 = #<TestBlockScope:'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) same name:local variabe') do + # ruby source (bp is break point) + src = <<"SRC" +lv = 'top' +class TestLocalVariableName + lv = 'class' + def m + lv = 'instance method' + bp = nil + end + bp = nil +end +TestLocalVariableName.new.m +bp = nil +SRC + + tc = [] + tc << {:cmd=>"b 6"} + tc << {:cmd=>"b 8"} + tc << {:cmd=>"b 11"} + tc << {:cmd=>"r"} + tc << {:cmd=>"p lv", :exp=>'$1 = "class"'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p lv", :exp=>'$3 = "top"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) same name:instance variabe') do + # ruby source (bp is break point) + src = <<"SRC" +@iv = 'top' +class TestInstanceVariableName + def initialize(v) + @iv = v + end + def m + bp = nil + end +end +i1 = TestInstanceVariableName.new('instance1') +i2 = TestInstanceVariableName.new('instance2') +i1.m +i2.m +bp = nil +SRC + + tc = [] + tc << {:cmd=>"b 7"} + tc << {:cmd=>"b 14"} + tc << {:cmd=>"r"} + tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'} + tc << {:cmd=>"c"} + tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +# Kernel#instance_eval(string) does't work const. +=begin +assert('mruby-bin-debugger(print) same name:const') do + # ruby source (bp is break point) + src = <<"SRC" +CONST='top' +class TestConstNameSuperClass + CONST='super class' + def m + bp = nil + end +end +class TestConstNameSubClass < TestConstNameSuperClass + CONST='sub class' + def m + bp = nil + end +end + +TestConstNameSuperClass.new.m() +TestConstNameSubClass.new.m() +bp = nil +SRC + + # todo: wait for 'break' to be implimented + tc = [] + 9.times { tc << {:cmd=>"s"} } + tc << {:cmd=>"p CONST", :exp=>"super class"} + 3.times { tc << {:cmd=>"s"} } + tc << {:cmd=>"p CONST", :exp=>"sub class"} + 1.times { tc << {:cmd=>"s"} } + tc << {:cmd=>"p CONST", :exp=>"top"} + + BinTest_MrubyBinDebugger.test(src, tc) +end +=end + +assert('mruby-bin-debugger(print) Literal:Numeric') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>"p 100", :exp=>'$1 = 100'} + tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'} + tc << {:cmd=>"p +0100", :exp=>'$3 = 64'} + tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'} + tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'} + tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"} + tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"} + + tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'} + tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'} + tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'} + tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000.0'} + tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Literal:String') do + # ruby source + src = <<"SRC" +foo = 'foo' +bar = "bar" +baz = "baz" +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"s"} + + tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'} + tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'} + tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\001\\032"'} + tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'} + + tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'} + tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'} + tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'} + tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "#{foo+bar}"'} + + tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'} + tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'} + tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\001\\032"'} + tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'} + + tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'} + tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'} + tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\001\\032"'} + tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'} + + tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'} + tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'} + tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'} + tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "#{foo+bar}"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Literal:Array') do + # ruby source + src = <<"SRC" +foo = 'foo' +bar = "bar" +baz = "baz" +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"s"} + + tc << {:cmd=>'p []', :exp=>'$1 = []'} + tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'} + tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'} + tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'} + tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Literal:Hash') do + # ruby source + src = <<"SRC" +foo = 'foo' +bar = "bar" +baz = "baz" +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"s"} + + tc << {:cmd=>'p {}', :exp=>'$1 = {}'} + tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one"=>1, "two"=>2}'} + tc << {:cmd=>'p {:eins=>"1", :zwei=>"2", }', :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'} + tc << {:cmd=>'p {uno:"one", dos: 2}', :exp=>'$4 = {:uno=>"one", :dos=>2}'} + tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}', :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'} + tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Literal:Range') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'} + tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'} + tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'} + tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'} + + tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'} + tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Literal:Symbol') do + # ruby source + src = <<"SRC" +foo = 'foo' +bar = "bar" +baz = "baz" +SRC + + # test case + tc = [] + tc << {:cmd=>"s"} + tc << {:cmd=>"s"} + + tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'} + tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'} + tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'} + tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'} + tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'} + tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Unary operation') do + # ruby source + src = "foo = 'foo'\n" + + # test case + tc = [] + tc << {:cmd=>'p +10', :exp=>'$1 = 10'} + tc << {:cmd=>'p -100', :exp=>'$2 = -100'} + tc << {:cmd=>'p !true', :exp=>'$3 = false'} + tc << {:cmd=>'p !false', :exp=>'$4 = true'} + tc << {:cmd=>'p !nil', :exp=>'$5 = true'} + tc << {:cmd=>'p !1', :exp=>'$6 = false'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Binary operation') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, 8 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p a+1', :exp=>'$1 = 2'} + tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'} + tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'} + tc << {:cmd=>'p a/b', :exp=>'$4 = 0.2'} + tc << {:cmd=>'p c%b', :exp=>'$5 = 3'} + tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'} + tc << {:cmd=>'p ~3', :exp=>'$7 = -4'} + + tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'} + tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'} + + tc << {:cmd=>'p a|c', :exp=>'$10 = 9'} + tc << {:cmd=>'p a&b', :exp=>'$11 = 1'} + tc << {:cmd=>'p a^b', :exp=>'$12 = 4'} + + tc << {:cmd=>'p a>b', :exp=>'$13 = false'} + tc << {:cmd=>'p a<b', :exp=>'$14 = true'} + tc << {:cmd=>'p b>=5', :exp=>'$15 = true'} + tc << {:cmd=>'p b<=5', :exp=>'$16 = true'} + + tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'} + tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'} + tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'} + tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'} + + tc << {:cmd=>'p false || true', :exp=>'$21 = true'} + tc << {:cmd=>'p false && true', :exp=>'$22 = false'} + + tc << {:cmd=>'p not nil', :exp=>'$23 = true'} + tc << {:cmd=>'p false or true', :exp=>'$24 = true'} + tc << {:cmd=>'p false and true', :exp=>'$25 = false'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Ternary operation') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, -10 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'} + tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'} + tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'} + tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'} + tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Substitution:simple') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, -10 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p a=2', :exp=>'$1 = 2'} + tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'} + + tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'} + tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = NoMethodError'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Substitution:self') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, -10 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} + tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} + tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} + tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'} + tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} + + tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} + tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} + + tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'} + tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} + + tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} + tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} + tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} + tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} + + tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} + tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Substitution:multiple') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, -10 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'} + tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'} + + tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'} + tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'} + + tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'} + tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'} + +# tc << {:cmd=>'p a,*b=[123, 456, 789]'} +# tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + +assert('mruby-bin-debugger(print) Substitution:self') do + # ruby source + src = <<"SRC" +CONST = 100 +a,b,c = 1, 5, -10 +foo,bar,baz = 'foo','bar','baz' +ary = [] +SRC + + # test case + tc = [] + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + tc << {:cmd=>'s'} + + tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} + tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} + tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} + tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'} + tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} + + tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} + tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} + + tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'} + tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} + + tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} + tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} + tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} + tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} + + tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} + tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'} + + BinTest_MrubyBinDebugger.test(src, tc) +end + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake new file mode 100644 index 00000000..764f431a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake @@ -0,0 +1,9 @@ +MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby debugger command' + + spec.add_dependency('mruby-eval', :core => 'mruby-eval') + + spec.bins = %w(mrdb) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c new file mode 100644 index 00000000..dead4b2a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c @@ -0,0 +1,507 @@ +/* +** apibreak.c +** +*/ + +#include <string.h> +#include <mruby.h> +#include <mruby/irep.h> +#include "mrdb.h" +#include <mruby/debug.h> +#include <mruby/opcode.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/variable.h> +#include "mrdberror.h" +#include "apibreak.h" + +#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024) +#define MRB_DEBUG_BP_FILE_OK (0x0001) +#define MRB_DEBUG_BP_LINENO_OK (0x0002) + +static uint16_t +check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno) +{ + uint32_t count = info_file->line_entry_count; + uint16_t l_idx; + + if (info_file->line_type == mrb_debug_line_ary) { + for (l_idx = 0; l_idx < count; ++l_idx) { + if (lineno == info_file->lines.ary[l_idx]) { + return lineno; + } + } + } + else { + for (l_idx = 0; l_idx < count; ++l_idx) { + if (lineno == info_file->lines.flat_map[l_idx].line) { + return lineno; + } + } + } + + return 0; +} + +static int32_t +get_break_index(mrb_debug_context *dbg, uint32_t bpno) +{ + uint32_t i; + int32_t index; + char hit = FALSE; + + for(i = 0 ; i < dbg->bpnum; i++) { + if (dbg->bp[i].bpno == bpno) { + hit = TRUE; + index = i; + break; + } + } + + if (hit == FALSE) { + return MRB_DEBUG_BREAK_INVALID_NO; + } + + return index; +} + +static void +free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp) +{ + switch(bp->type) { + case MRB_DEBUG_BPTYPE_LINE: + mrb_free(mrb, (void*)bp->point.linepoint.file); + break; + case MRB_DEBUG_BPTYPE_METHOD: + mrb_free(mrb, (void*)bp->point.methodpoint.method_name); + if (bp->point.methodpoint.class_name != NULL) { + mrb_free(mrb, (void*)bp->point.methodpoint.class_name); + } + break; + default: + break; + } +} + +static uint16_t +check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno) +{ + mrb_irep_debug_info_file *info_file; + uint16_t result = 0; + uint16_t f_idx; + uint16_t fix_lineno; + uint16_t i; + + for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + info_file = irep->debug_info->files[f_idx]; + if (!strcmp(info_file->filename, file)) { + result = MRB_DEBUG_BP_FILE_OK; + + fix_lineno = check_lineno(info_file, lineno); + if (fix_lineno != 0) { + return result | MRB_DEBUG_BP_LINENO_OK; + } + } + for (i=0; i < irep->rlen; ++i) { + result |= check_file_lineno(irep->reps[i], file, lineno); + if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) { + return result; + } + } + } + return result; +} + +static int32_t +compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) +{ + const char* class_name; + const char* method_name; + struct RProc* m; + struct RClass* sc; + const char* sn; + mrb_sym ssym; + mrb_debug_methodpoint *method_p; + mrb_bool is_defined; + + method_name = mrb_sym2name(mrb, method_sym); + + method_p = &bp->point.methodpoint; + if (strcmp(method_p->method_name, method_name) == 0) { + class_name = mrb_class_name(mrb, class_obj); + if (class_name == NULL) { + if (method_p->class_name == NULL) { + return bp->bpno; + } + } + else if (method_p->class_name != NULL) { + m = mrb_method_search_vm(mrb, &class_obj, method_sym); + if (m == NULL) { + return MRB_DEBUG_OK; + } + if (MRB_PROC_CFUNC_P(m)) { + *isCfunc = TRUE; + } + + is_defined = mrb_class_defined(mrb, method_p->class_name); + if (is_defined == FALSE) { + return MRB_DEBUG_OK; + } + + sc = mrb_class_get(mrb, method_p->class_name); + ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name)); + m = mrb_method_search_vm(mrb, &sc, ssym); + if (m == NULL) { + return MRB_DEBUG_OK; + } + + class_name = mrb_class_name(mrb, class_obj); + sn = mrb_class_name(mrb, sc); + if (strcmp(sn, class_name) == 0) { + return bp->bpno; + } + } + } + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno) +{ + int32_t index; + char* set_file; + uint16_t result; + + if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if (dbg->bpnum >= MAX_BREAKPOINT) { + return MRB_DEBUG_BREAK_NUM_OVER; + } + + if (dbg->next_bpno > MAX_BREAKPOINTNO) { + return MRB_DEBUG_BREAK_NO_OVER; + } + + /* file and lineno check (line type mrb_debug_line_ary only.) */ + result = check_file_lineno(dbg->root_irep, file, lineno); + if (result == 0) { + return MRB_DEBUG_BREAK_INVALID_FILE; + } + else if (result == MRB_DEBUG_BP_FILE_OK) { + return MRB_DEBUG_BREAK_INVALID_LINENO; + } + + set_file = mrb_malloc(mrb, strlen(file) + 1); + + index = dbg->bpnum; + dbg->bp[index].bpno = dbg->next_bpno; + dbg->next_bpno++; + dbg->bp[index].enable = TRUE; + dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE; + dbg->bp[index].point.linepoint.lineno = lineno; + dbg->bpnum++; + + strncpy(set_file, file, strlen(file) + 1); + + dbg->bp[index].point.linepoint.file = set_file; + + return dbg->bp[index].bpno; +} + +int32_t +mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name) +{ + int32_t index; + char* set_class; + char* set_method; + + if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if (dbg->bpnum >= MAX_BREAKPOINT) { + return MRB_DEBUG_BREAK_NUM_OVER; + } + + if (dbg->next_bpno > MAX_BREAKPOINTNO) { + return MRB_DEBUG_BREAK_NO_OVER; + } + + if (class_name != NULL) { + set_class = mrb_malloc(mrb, strlen(class_name) + 1); + strncpy(set_class, class_name, strlen(class_name) + 1); + } + else { + set_class = NULL; + } + + set_method = mrb_malloc(mrb, strlen(method_name) + 1); + + strncpy(set_method, method_name, strlen(method_name) + 1); + + index = dbg->bpnum; + dbg->bp[index].bpno = dbg->next_bpno; + dbg->next_bpno++; + dbg->bp[index].enable = TRUE; + dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD; + dbg->bp[index].point.methodpoint.method_name = set_method; + dbg->bp[index].point.methodpoint.class_name = set_class; + dbg->bpnum++; + + return dbg->bp[index].bpno; +} + +int32_t +mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg) +{ + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + return dbg->bpnum; +} + +int32_t +mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp) +{ + uint32_t get_size = 0; + + if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if (dbg->bpnum >= size) { + get_size = size; + } + else { + get_size = dbg->bpnum; + } + + memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size); + + return get_size; +} + +int32_t +mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp) +{ + int32_t index; + + if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + index = get_break_index(dbg, bpno); + if (index == MRB_DEBUG_BREAK_INVALID_NO) { + return MRB_DEBUG_BREAK_INVALID_NO; + } + + bp->bpno = dbg->bp[index].bpno; + bp->enable = dbg->bp[index].enable; + bp->point = dbg->bp[index].point; + bp->type = dbg->bp[index].type; + + return 0; +} + +int32_t +mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) +{ + uint32_t i; + int32_t index; + + if ((mrb == NULL) ||(dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + index = get_break_index(dbg, bpno); + if (index == MRB_DEBUG_BREAK_INVALID_NO) { + return MRB_DEBUG_BREAK_INVALID_NO; + } + + free_breakpoint(mrb, &dbg->bp[index]); + + for(i = index ; i < dbg->bpnum; i++) { + if ((i + 1) == dbg->bpnum) { + memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint)); + } + else { + memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint)); + } + } + + dbg->bpnum--; + + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg) +{ + uint32_t i; + + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + for(i = 0 ; i < dbg->bpnum ; i++) { + free_breakpoint(mrb, &dbg->bp[i]); + } + + dbg->bpnum = 0; + + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) +{ + int32_t index = 0; + + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + index = get_break_index(dbg, bpno); + if (index == MRB_DEBUG_BREAK_INVALID_NO) { + return MRB_DEBUG_BREAK_INVALID_NO; + } + + dbg->bp[index].enable = TRUE; + + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg) +{ + uint32_t i; + + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + for(i = 0 ; i < dbg->bpnum; i++) { + dbg->bp[i].enable = TRUE; + } + + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) +{ + int32_t index = 0; + + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + index = get_break_index(dbg, bpno); + if (index == MRB_DEBUG_BREAK_INVALID_NO) { + return MRB_DEBUG_BREAK_INVALID_NO; + } + + dbg->bp[index].enable = FALSE; + + return MRB_DEBUG_OK; +} + +int32_t +mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg) +{ + uint32_t i; + + if ((mrb == NULL) || (dbg == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + for(i = 0 ; i < dbg->bpnum; i++) { + dbg->bp[i].enable = FALSE; + } + + return MRB_DEBUG_OK; +} + +static mrb_bool +check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line) +{ + if (pc > irep->iseq) { + if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) { + return FALSE; + } + } + return TRUE; +} + +int32_t +mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line) +{ + mrb_debug_breakpoint *bp; + mrb_debug_linepoint *line_p; + uint32_t i; + + if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) { + return MRB_DEBUG_OK; + } + + bp = dbg->bp; + for(i=0; i<dbg->bpnum; i++) { + switch (bp->type) { + case MRB_DEBUG_BPTYPE_LINE: + if (bp->enable == TRUE) { + line_p = &bp->point.linepoint; + if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) { + return bp->bpno; + } + } + break; + case MRB_DEBUG_BPTYPE_METHOD: + break; + case MRB_DEBUG_BPTYPE_NONE: + default: + return MRB_DEBUG_OK; + } + bp++; + } + return MRB_DEBUG_OK; +} + + +int32_t +mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) +{ + mrb_debug_breakpoint *bp; + int32_t bpno; + uint32_t i; + + if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + bp = dbg->bp; + for(i=0; i<dbg->bpnum; i++) { + if (bp->type == MRB_DEBUG_BPTYPE_METHOD) { + if (bp->enable == TRUE) { + bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc); + if (bpno > 0) { + return bpno; + } + } + } + else if (bp->type == MRB_DEBUG_BPTYPE_NONE) { + break; + } + bp++; + } + + return 0; +} + + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h new file mode 100644 index 00000000..08f1d808 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h @@ -0,0 +1,26 @@ +/* +** apibreak.h +** +*/ + +#ifndef APIBREAK_H_ +#define APIBREAK_H_ + +#include <mruby.h> +#include "mrdb.h" + +int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t); +int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *); +int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *); +int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]); +int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *); +int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t); +int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *); +int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t); +int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *); +int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t); +int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *); +int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t); +int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*); + +#endif /* APIBREAK_H_ */ diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c new file mode 100644 index 00000000..8aaa30bc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c @@ -0,0 +1,239 @@ +/* + * apilist.c + */ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "mrdb.h" +#include "mrdberror.h" +#include "apilist.h" +#include <mruby/compile.h> +#include <mruby/irep.h> +#include <mruby/debug.h> + +#define LINE_BUF_SIZE MAX_COMMAND_LINE + +typedef struct source_file { + char *path; + uint16_t lineno; + FILE *fp; +} source_file; + +static void +source_file_free(mrb_state *mrb, source_file *file) +{ + if (file != NULL) { + if (file->path != NULL) { + mrb_free(mrb, file->path); + } + if (file->fp != NULL) { + fclose(file->fp); + file->fp = NULL; + } + mrb_free(mrb, file); + } +} + +static char* +build_path(mrb_state *mrb, const char *dir, const char *base) +{ + int len; + char *path = NULL; + + len = strlen(base) + 1; + + if (strcmp(dir, ".")) { + len += strlen(dir) + sizeof("/") - 1; + } + + path = mrb_malloc(mrb, len); + memset(path, 0, len); + + if (strcmp(dir, ".")) { + strcat(path, dir); + strcat(path, "/"); + } + strcat(path, base); + + return path; +} + +static char* +dirname(mrb_state *mrb, const char *path) +{ + size_t len; + char *p, *dir; + + if (path == NULL) { + return NULL; + } + + p = strrchr(path, '/'); + len = p != NULL ? (size_t)(p - path) : strlen(path); + + dir = mrb_malloc(mrb, len + 1); + strncpy(dir, path, len); + dir[len] = '\0'; + + return dir; +} + +static source_file* +source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename) +{ + source_file *file = NULL; + + file = mrb_malloc(mrb, sizeof(source_file)); + + memset(file, '\0', sizeof(source_file)); + file->fp = fopen(filename, "rb"); + + if (file->fp == NULL) { + source_file_free(mrb, file); + return NULL; + } + + file->lineno = 1; + file->path = mrb_malloc(mrb, strlen(filename) + 1); + strcpy(file->path, filename); + return file; +} + +static mrb_bool +remove_newlines(char *s, FILE *fp) +{ + int c; + char *p; + size_t len; + + if ((len = strlen(s)) == 0) { + return FALSE; + } + + p = s + len - 1; + + if (*p != '\r' && *p != '\n') { + return FALSE; + } + + if (*p == '\r') { + /* peek the next character and skip '\n' */ + if ((c = fgetc(fp)) != '\n') { + ungetc(c, fp); + } + } + + /* remove trailing newline characters */ + while (s <= p && (*p == '\r' || *p == '\n')) { + *p-- = '\0'; + } + + return TRUE; +} + +static void +show_lines(source_file *file, uint16_t line_min, uint16_t line_max) +{ + char buf[LINE_BUF_SIZE]; + int show_lineno = 1, found_newline = 0, is_printed = 0; + + if (file->fp == NULL) { + return; + } + + while (fgets(buf, sizeof(buf), file->fp) != NULL) { + found_newline = remove_newlines(buf, file->fp); + + if (line_min <= file->lineno) { + if (show_lineno) { + printf("%-8d", file->lineno); + } + show_lineno = found_newline; + printf(found_newline ? "%s\n" : "%s", buf); + is_printed = 1; + } + + if (found_newline) { + if (line_max < ++file->lineno) { + break; + } + } + } + + if (is_printed && !found_newline) { + printf("\n"); + } +} + +char* +mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename) +{ + int i; + FILE *fp; + const char *search_path[3]; + char *path = NULL; + const char *srcname = strrchr(filename, '/'); + + if (srcname) srcname++; + else srcname = filename; + + search_path[0] = srcpath; + search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0)); + search_path[2] = "."; + + for (i = 0; i < 3; i++) { + if (search_path[i] == NULL) { + continue; + } + + if ((path = build_path(mrb, search_path[i], srcname)) == NULL) { + continue; + } + + if ((fp = fopen(path, "rb")) == NULL) { + mrb_free(mrb, path); + path = NULL; + continue; + } + fclose(fp); + break; + } + + mrb_free(mrb, (void *)search_path[1]); + + return path; +} + +int32_t +mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max) +{ + char *ext; + source_file *file; + + if (mrb == NULL || dbg == NULL || filename == NULL) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + ext = strrchr(filename, '.'); + + if (ext == NULL || strcmp(ext, ".rb")) { + printf("List command only supports .rb file.\n"); + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if (line_min > line_max) { + return MRB_DEBUG_INVALID_ARGUMENT; + } + + if ((file = source_file_new(mrb, dbg, filename)) != NULL) { + show_lines(file, line_min, line_max); + source_file_free(mrb, file); + return MRB_DEBUG_OK; + } + else { + printf("Invalid source file named %s.\n", filename); + return MRB_DEBUG_INVALID_ARGUMENT; + } +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h new file mode 100644 index 00000000..6c410788 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h @@ -0,0 +1,14 @@ +/* + * apilist.h + */ + +#ifndef APILIST_H_ +#define APILIST_H_ + +#include <mruby.h> +#include "mrdb.h" + +int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t); +char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *); + +#endif /* APILIST_H_ */ diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c new file mode 100644 index 00000000..a9c895b5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c @@ -0,0 +1,78 @@ +/* +** apiprint.c +** +*/ + +#include <string.h> +#include "mrdb.h" +#include <mruby/value.h> +#include <mruby/class.h> +#include <mruby/compile.h> +#include <mruby/error.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include "apiprint.h" + +static void +mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len) +{ + mrbc_context *c; + + c = mrbc_context_new(mrb); + c->no_exec = TRUE; + c->capture_errors = TRUE; + c->filename = (char*)dbg->prvfile; + c->lineno = dbg->prvline; + + /* Load program */ + mrb_load_nstring_cxt(mrb, expr, len, c); + + mrbc_context_free(mrb, c); +} + +mrb_value +mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc) +{ + void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *); + mrb_value ruby_code; + mrb_value s; + mrb_value v; + mrb_value recv; + + /* disable code_fetch_hook */ + tmp = mrb->code_fetch_hook; + mrb->code_fetch_hook = NULL; + + mrdb_check_syntax(mrb, dbg, expr, len); + if (mrb->exc) { + v = mrb_obj_value(mrb->exc); + mrb->exc = 0; + } + else { + /* + * begin + * expr + * rescue => e + * e + * end + */ + ruby_code = mrb_str_new_lit(mrb, "begin\n"); + ruby_code = mrb_str_cat(mrb, ruby_code, expr, len); + ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend"); + + recv = dbg->regs[0]; + + v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code); + } + + if (exc) { + *exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class); + } + + s = mrb_funcall(mrb, v, "inspect", 0); + + /* enable code_fetch_hook */ + mrb->code_fetch_hook = tmp; + + return s; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h new file mode 100644 index 00000000..e256f626 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h @@ -0,0 +1,13 @@ +/* + * apiprint.h + */ + +#ifndef APIPRINT_H_ +#define APIPRINT_H_ + +#include <mruby.h> +#include "mrdb.h" + +mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*); + +#endif /* APIPRINT_H_ */ diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c new file mode 100644 index 00000000..8e590175 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c @@ -0,0 +1,436 @@ +/* +** cmdbreak.c +** +*/ + +#include <ctype.h> +#include <string.h> +#include <mruby.h> +#include <mruby/dump.h> +#include <mruby/debug.h> +#include <mruby/string.h> +#include "mrdb.h" +#include "mrdberror.h" +#include "apibreak.h" + +#define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n" +#define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n" +#define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n" +#define BREAK_INFO_MSG_HEADER "Num Type Enb What" +#define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n" +#define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n" +#define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n" +#define BREAK_INFO_MSG_ENABLE "y" +#define BREAK_INFO_MSG_DISABLE "n" + +#define BREAK_ERR_MSG_INVALIDARG "Internal error." +#define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information." +#define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535." +#define BREAK_ERR_MSG_NUMOVER "Exceeded the setable number of breakpoint." +#define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb." +#define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n" +#define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n" +#define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n" +#define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n" +#define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n" +#define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n" +#define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables." +#define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n" +#define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n" +#define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints." + +#define LINENO_MAX_DIGIT 6 +#define BPNO_LETTER_NUM 9 + +typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *); +typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t); + +static void +print_api_common_error(int32_t error) +{ + switch(error) { + case MRB_DEBUG_INVALID_ARGUMENT: + puts(BREAK_ERR_MSG_INVALIDARG); + break; + default: + break; + } +} + +#undef STRTOUL +#define STRTOUL(ul,s) { \ + int i; \ + ul = 0; \ + for(i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \ +} + +static int32_t +parse_breakpoint_no(char* args) +{ + char* ps = args; + uint32_t l; + + if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) { + return 0; + } + + while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { + if (!ISDIGIT(*ps)) { + return 0; + } + ps++; + } + + STRTOUL(l, args); + return l; +} + +static mrb_bool +exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func) +{ + int32_t ret = MRB_DEBUG_OK; + + if (mrdb->wcnt == 1) { + ret = func(mrb, mrdb->dbg); + print_api_common_error(ret); + return TRUE; + } + return FALSE; +} + +static void +exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func) +{ + char* ps; + int32_t ret = MRB_DEBUG_OK; + int32_t bpno = 0; + int32_t i; + + for(i=1; i<mrdb->wcnt; i++) { + ps = mrdb->words[i]; + bpno = parse_breakpoint_no(ps); + if (bpno == 0) { + printf(BREAK_ERR_MSG_INVALIDBPNO, ps); + break; + } + ret = func(mrb, mrdb->dbg, (uint32_t)bpno); + if (ret == MRB_DEBUG_BREAK_INVALID_NO) { + printf(BREAK_ERR_MSG_NOBPNO, bpno); + } + else if (ret != MRB_DEBUG_OK) { + print_api_common_error(ret); + } + } +} + +mrb_debug_bptype +check_bptype(char* args) +{ + char* ps = args; + + if (ISBLANK(*ps)||ISCNTRL(*ps)) { + puts(BREAK_ERR_MSG_BLANK); + return MRB_DEBUG_BPTYPE_NONE; + } + + if (!ISDIGIT(*ps)) { + return MRB_DEBUG_BPTYPE_METHOD; + } + + while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { + if (!ISDIGIT(*ps)) { + printf(BREAK_ERR_MSG_INVALIDSTR, args); + return MRB_DEBUG_BPTYPE_NONE; + } + ps++; + } + + if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) { + puts(BREAK_ERR_MSG_RANGEOVER); + return MRB_DEBUG_BPTYPE_NONE; + } + + return MRB_DEBUG_BPTYPE_LINE; +} + +static void +print_breakpoint(mrb_debug_breakpoint *bp) +{ + const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE}; + + if (bp->type == MRB_DEBUG_BPTYPE_LINE) { + printf(BREAK_INFO_MSG_LINEBREAK, + bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno); + } + else { + if (bp->point.methodpoint.class_name == NULL) { + printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS, + bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name); + } + else { + printf(BREAK_INFO_MSG_METHODBREAK, + bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name); + } + } +} + +static void +info_break_all(mrb_state *mrb, mrdb_state *mrdb) +{ + int32_t bpnum = 0; + int32_t i = 0; + int32_t ret = MRB_DEBUG_OK; + mrb_debug_breakpoint *bp_list; + + bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg); + if (bpnum < 0) { + print_api_common_error(bpnum); + return; + } + else if (bpnum == 0) { + puts(BREAK_ERR_MSG_NOBPNO_INFOALL); + return; + } + bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint)); + + ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list); + if (ret < 0) { + print_api_common_error(ret); + return; + } + puts(BREAK_INFO_MSG_HEADER); + for(i = 0 ; i < bpnum ; i++) { + print_breakpoint(&bp_list[i]); + } + + mrb_free(mrb, bp_list); +} + +static void +info_break_select(mrb_state *mrb, mrdb_state *mrdb) +{ + int32_t ret = MRB_DEBUG_OK; + int32_t bpno = 0; + char* ps = mrdb->command; + mrb_debug_breakpoint bp; + mrb_bool isFirst = TRUE; + int32_t i; + + for(i=2; i<mrdb->wcnt; i++) { + ps = mrdb->words[i]; + bpno = parse_breakpoint_no(ps); + if (bpno == 0) { + puts(BREAK_ERR_MSG_INVALIDBPNO_INFO); + break; + } + + ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp); + if (ret == MRB_DEBUG_BREAK_INVALID_NO) { + printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno); + break; + } + else if (ret != MRB_DEBUG_OK) { + print_api_common_error(ret); + break; + } + else if (isFirst == TRUE) { + isFirst = FALSE; + puts(BREAK_INFO_MSG_HEADER); + } + print_breakpoint(&bp); + } +} + +mrb_debug_bptype +parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method) +{ + mrb_debug_context *dbg = mrdb->dbg; + char *args; + char *body; + mrb_debug_bptype type; + uint32_t l; + + if (mrdb->wcnt <= 1) { + puts(BREAK_ERR_MSG_BLANK); + return MRB_DEBUG_BPTYPE_NONE; + } + + args = mrdb->words[1]; + if ((body = strrchr(args, ':')) == NULL) { + body = args; + type = check_bptype(body); + } + else { + if (body == args) { + printf(BREAK_ERR_MSG_INVALIDSTR, args); + return MRB_DEBUG_BPTYPE_NONE; + } + *body = '\0'; + type = check_bptype(++body); + } + + switch(type) { + case MRB_DEBUG_BPTYPE_LINE: + STRTOUL(l, body); + if (l <= 65535) { + *line = l; + *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args; + } + else { + puts(BREAK_ERR_MSG_RANGEOVER); + type = MRB_DEBUG_BPTYPE_NONE; + } + break; + case MRB_DEBUG_BPTYPE_METHOD: + if (body == args) { + /* method only */ + if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) { + *method = body; + *cname = NULL; + } + else { + printf(BREAK_ERR_MSG_INVALIDMETHOD, args); + type = MRB_DEBUG_BPTYPE_NONE; + } + } + else { + if (ISUPPER(*args)) { + switch(*body) { + case '@': case '$': case '?': case '.': case ',': case ':': + case ';': case '#': case '\\': case '\'': case '\"': + printf(BREAK_ERR_MSG_INVALIDMETHOD, body); + type = MRB_DEBUG_BPTYPE_NONE; + break; + default: + *method = body; + *cname = args; + break; + } + } + else { + printf(BREAK_ERR_MSG_INVALIDCLASS, args); + type = MRB_DEBUG_BPTYPE_NONE; + } + } + break; + case MRB_DEBUG_BPTYPE_NONE: + default: + break; + } + + return type; +} + +dbgcmd_state +dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_debug_bptype type; + mrb_debug_context *dbg = mrdb->dbg; + const char *file = NULL; + uint32_t line = 0; + char *cname = NULL; + char *method = NULL; + int32_t ret; + + type = parse_breakcommand(mrdb, &file, &line, &cname, &method); + switch (type) { + case MRB_DEBUG_BPTYPE_LINE: + ret = mrb_debug_set_break_line(mrb, dbg, file, line); + break; + case MRB_DEBUG_BPTYPE_METHOD: + ret = mrb_debug_set_break_method(mrb, dbg, cname, method); + break; + case MRB_DEBUG_BPTYPE_NONE: + default: + return DBGST_PROMPT; + } + + if (ret >= 0) { + if (type == MRB_DEBUG_BPTYPE_LINE) { + printf(BREAK_SET_MSG_LINE, ret, file, line); + } + else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) { + printf(BREAK_SET_MSG_METHOD, ret, method); + } + else { + printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method); + } + } + else { + switch (ret) { + case MRB_DEBUG_BREAK_INVALID_LINENO: + printf(BREAK_ERR_MSG_INVALIDLINENO, line, file); + break; + case MRB_DEBUG_BREAK_INVALID_FILE: + printf(BREAK_ERR_MSG_INVALIDFILE, file); + break; + case MRB_DEBUG_BREAK_NUM_OVER: + puts(BREAK_ERR_MSG_NUMOVER); + break; + case MRB_DEBUG_BREAK_NO_OVER: + puts(BREAK_ERR_MSG_NOOVER); + break; + case MRB_DEBUG_INVALID_ARGUMENT: + puts(BREAK_ERR_MSG_INVALIDARG); + break; + case MRB_DEBUG_NOBUF: + puts("T.B.D."); + break; + default: + break; + } + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb) +{ + if (mrdb->wcnt == 2) { + info_break_all(mrb, mrdb); + } + else { + info_break_select(mrb, mrdb); + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_bool ret = FALSE; + + ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all); + if (ret != TRUE) { + exe_set_command_select(mrb, mrdb, mrb_debug_delete_break); + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_bool ret = FALSE; + + ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all); + if (ret != TRUE) { + exe_set_command_select(mrb, mrdb, mrb_debug_enable_break); + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_bool ret = FALSE; + + ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all); + if (ret != TRUE) { + exe_set_command_select(mrb, mrdb, mrb_debug_disable_break); + } + return DBGST_PROMPT; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c new file mode 100644 index 00000000..5984b623 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c @@ -0,0 +1,501 @@ +/* +** cmdmisc.c - mruby debugger miscellaneous command functions +** +*/ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "apilist.h" +#include <mruby/compile.h> + +typedef struct help_msg { + const char *cmd1; + const char *cmd2; + const char *short_msg; + const char *long_msg; +} help_msg; + +static help_msg help_msg_list[] = { + { + "b[reak]", NULL, "Set breakpoint", + "Usage: break [file:]line\n" + " break [class:]method\n" + "\n" + "Set breakpoint at specified line or method.\n" + "If \'[file:]line\' is specified, break at start of code for that line (in a file).\n" + "If \'[class:]method\' is specified, break at start of code for that method (of the class).\n" + }, + { + "c[ontinue]", NULL, "Continue program being debugged", + "Usage: continue [N]\n" + "\n" + "Continue program stopped by a breakpoint.\n" + "If N, which is non negative value, is passed,\n" + "proceed program until the N-th breakpoint is coming.\n" + "If N is not passed, N is assumed 1.\n" + }, + { + "d[elete]", NULL, "Delete some breakpoints", + "Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n" + "\n" + "Delete some breakpoints.\n" + "Arguments are breakpoint numbers with spaces in between.\n" + "To delete all breakpoints, give no argument.\n" + }, + { + "dis[able]", NULL, "Disable some breakpoints", + "Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n" + "\n" + "Disable some breakpoints.\n" + "Arguments are breakpoint numbers with spaces in between.\n" + "To disable all breakpoints, give no argument.\n" + }, + { + "en[able]", NULL, "Enable some breakpoints", + "Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n" + "\n" + "Enable some breakpoints.\n" + "Arguments are breakpoint numbers with spaces in between.\n" + "To enable all breakpoints, give no argument.\n" + }, + { + "ev[al]", NULL, "Evaluate expression", + "Usage: eval expr\n" + "\n" + "It evaluates and prints the value of the mruby expression.\n" + "This is equivalent to the \'print\' command.\n" + }, + { + "h[elp]", NULL, "Print this help", + "Usage: help [command]\n" + "\n" + "With no arguments, help displays a short list of commands.\n" + "With a command name as help argument, help displays how to use that command.\n" + }, + { + "i[nfo]", "b[reakpoints]", "Status of breakpoints", + "Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n" + "\n" + "Status of specified breakpoints (all user-settable breakpoints if no argument).\n" + "Arguments are breakpoint numbers with spaces in between.\n" + }, + { + "l[ist]", NULL, "List specified line", + "Usage: list\n" + " list first[,last]\n" + " list filename:first[,last]\n" + "\n" + "Print lines from a source file.\n" + "\n" + "With first and last, list prints lines from first to last.\n" + "When last is empty, it stands for ten lines away from first.\n" + "With filename, list prints lines in the specified source file.\n" + }, + { + "p[rint]", NULL, "Print value of expression", + "Usage: print expr\n" + "\n" + "It evaluates and prints the value of the mruby expression.\n" + "This is equivalent to the \'eval\' command.\n" + }, + { + "q[uit]", NULL, "Exit mrdb", + "Usage: quit\n" + "\n" + "Exit mrdb.\n" + }, + { + "r[un]", NULL, "Start debugged program", + "Usage: run\n" + "\n" + "Start debugged program.\n" + }, + { + "s[tep]", NULL, "Step program until it reaches a different source line", + "Usage: step\n" + "\n" + "Step program until it reaches a different source line.\n" + }, + { NULL, NULL, NULL, NULL } +}; + +typedef struct listcmd_parser_state { + mrb_bool parse_error; + mrb_bool has_line_min; + mrb_bool has_line_max; + char *filename; + uint16_t line_min; + uint16_t line_max; +} listcmd_parser_state; + +static listcmd_parser_state* +listcmd_parser_state_new(mrb_state *mrb) +{ + listcmd_parser_state *st = mrb_malloc(mrb, sizeof(listcmd_parser_state)); + memset(st, 0, sizeof(listcmd_parser_state)); + return st; +} + +static void +listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st) +{ + if (st != NULL) { + if (st->filename != NULL) { + mrb_free(mrb, st->filename); + } + mrb_free(mrb, st); + } +} + +static mrb_bool +parse_uint(char **sp, uint16_t *n) +{ + char *p; + int i; + + if (*sp == NULL || **sp == '\0') { + return FALSE; + } + + for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ; + + if (p != *sp && (i = atoi(*sp)) >= 0) { + *n = (uint16_t)i; + *sp = p; + return TRUE; + } + return FALSE; +} + +static mrb_bool +skip_char(char **sp, char c) +{ + if (*sp != NULL && **sp == c) { + ++*sp; + return TRUE; + } + return FALSE; +} + +static mrb_bool +parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st) +{ + if (*sp == NULL || **sp == '\0') { + return FALSE; + } + + st->has_line_min = FALSE; + st->has_line_max = FALSE; + + if (parse_uint(sp, &st->line_min)) { + st->has_line_min = TRUE; + } + else { + return FALSE; + } + + if (skip_char(sp, ',')) { + if (parse_uint(sp, &st->line_max)) { + st->has_line_max = TRUE; + } + else { + st->parse_error = TRUE; + return FALSE; + } + } + return TRUE; +} + +static mrb_bool +parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st) +{ + char *p; + int len; + + if (st->filename != NULL) { + mrb_free(mrb, st->filename); + st->filename = NULL; + } + + if ((p = strchr(*sp, ':')) != NULL) { + len = p - *sp; + } + else { + len = strlen(*sp); + } + + if (len > 0) { + st->filename = mrb_malloc(mrb, len + 1); + strncpy(st->filename, *sp, len); + st->filename[len] = '\0'; + *sp += len; + return TRUE; + } + else { + return FALSE; + } +} + +char* +replace_ext(mrb_state *mrb, const char *filename, const char *ext) +{ + size_t len; + char *p, *s; + + if (filename == NULL) { + return NULL; + } + + if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) { + len = p - filename; + } + else { + len = strlen(filename); + } + + s = mrb_malloc(mrb, len + strlen(ext) + 1); + memset(s, '\0', len + strlen(ext) + 1); + strncpy(s, filename, len); + strcat(s, ext); + + return s; +} + +static mrb_bool +parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st) +{ + char *p; + + switch (mrdb->wcnt) { + case 2: + p = mrdb->words[1]; + + /* mrdb->words[1] ::= <lineno> | <filename> ':' <lineno> | <filename> */ + if (!parse_lineno(mrb, &p, st)) { + if (parse_filename(mrb, &p, st)) { + if (skip_char(&p, ':')) { + if (!parse_lineno(mrb, &p, st)) { + st->parse_error = TRUE; + } + } + } + else { + st->parse_error = TRUE; + } + } + if (*p != '\0') { + st->parse_error = TRUE; + } + break; + case 1: + case 0: + /* do nothing */ + break; + default: + st->parse_error = TRUE; + printf("too many arguments\n"); + break; + } + + if (!st->parse_error) { + if (!st->has_line_min) { + st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1; + } + + if (!st->has_line_max) { + st->line_max = st->line_min + 9; + } + + if (st->filename == NULL) { + if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) { + st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb"); + } + } + } + + if (st->parse_error || st->filename == NULL) { + return FALSE; + } + + return TRUE; +} + +static mrb_bool +check_cmd_pattern(const char *pattern, const char *cmd) +{ + char *lbracket, *rbracket, *p, *q; + + if (pattern == NULL && cmd == NULL) { + return TRUE; + } + if (pattern == NULL || cmd == NULL) { + return FALSE; + } + if ((lbracket = strchr(pattern, '[')) == NULL) { + return !strcmp(pattern, cmd); + } + if ((rbracket = strchr(pattern, ']')) == NULL) { + return FALSE; + } + if (strncmp(pattern, cmd, lbracket - pattern)) { + return FALSE; + } + + p = lbracket + 1; + q = (char *)cmd + (lbracket - pattern); + + for ( ; p < rbracket && *q != '\0'; p++, q++) { + if (*p != *q) { + break; + } + } + return *q == '\0'; +} + +static help_msg* +get_help_msg(char *cmd1, char *cmd2) +{ + help_msg *p; + + if (cmd1 == NULL) { + return NULL; + } + for (p = help_msg_list; p->cmd1 != NULL; p++) { + if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) { + return p; + } + } + return NULL; +} + +static mrb_bool +show_short_help(void) +{ + help_msg *p; + + printf("Commands\n"); + + for (p = help_msg_list; p->cmd1 != NULL; p++) { + if (p->cmd2 == NULL) { + printf(" %s -- %s\n", p->cmd1, p->short_msg); + } + else { + printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg); + } + } + return TRUE; +} + +static mrb_bool +show_long_help(char *cmd1, char *cmd2) +{ + help_msg *help; + + if ((help = get_help_msg(cmd1, cmd2)) == NULL) { + return FALSE; + } + printf("%s", help->long_msg); + return TRUE; +} + +dbgcmd_state +dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb) +{ + char *filename; + listcmd_parser_state *st = listcmd_parser_state_new(mrb); + + if (parse_listcmd_args(mrb, mrdb, st)) { + if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) { + filename = st->filename; + } + mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max); + + if (filename != NULL && filename != st->filename) { + mrb_free(mrb, filename); + } + listcmd_parser_state_free(mrb, st); + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_bool is_valid; + int i; + + switch (mrdb->wcnt) { + case 0: + case 1: + is_valid = show_short_help(); + break; + case 2: + is_valid = show_long_help(mrdb->words[1], NULL); + break; + case 3: + is_valid = show_long_help(mrdb->words[1], mrdb->words[2]); + break; + default: + is_valid = FALSE; + break; + } + + if (!is_valid) { + printf("Invalid command \""); + for (i = 1; i < mrdb->wcnt; i++) { + printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]); + } + printf("\". Try \"help\".\n"); + } + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb) +{ + switch (mrdb->dbg->xm) { + case DBG_RUN: + case DBG_STEP: + case DBG_NEXT: + while (1) { + char c; + int buf; + + printf("The program is running. Exit anyway? (y or n) "); + fflush(stdout); + + if ((buf = getchar()) == EOF) { + mrdb->dbg->xm = DBG_QUIT; + break; + } + c = buf; + while (buf != '\n' && (buf = getchar()) != EOF) ; + + if (c == 'y' || c == 'Y') { + mrdb->dbg->xm = DBG_QUIT; + break; + } + else if (c == 'n' || c == 'N') { + break; + } + else { + printf("Please answer y or n.\n"); + } + } + break; + default: + mrdb->dbg->xm = DBG_QUIT; + break; + } + + if (mrdb->dbg->xm == DBG_QUIT) { + struct RClass *exc; + exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception")); + mrb_raise(mrb, exc, "Exit mrdb."); + } + return DBGST_PROMPT; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c new file mode 100644 index 00000000..cca63671 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c @@ -0,0 +1,58 @@ +/* +** cmdprint.c - mruby debugger print command functions +** +*/ + +#include <string.h> +#include "mrdb.h" +#include <mruby/value.h> +#include <mruby/class.h> +#include <mruby/compile.h> +#include <mruby/error.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include "apiprint.h" + +dbgcmd_state +dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_value expr; + mrb_value result; + mrb_value s; + uint8_t wcnt; + int ai; + + if (mrdb->wcnt <= 1) { + puts("Parameter not specified."); + return DBGST_PROMPT; + } + + ai = mrb_gc_arena_save(mrb); + + /* eval expr */ + expr = mrb_str_new_cstr(mrb, NULL); + for (wcnt=1; wcnt<mrdb->wcnt; wcnt++) { + expr = mrb_str_cat_lit(mrb, expr, " "); + expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]); + } + + result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL); + + /* $print_no = result */ + s = mrb_str_cat_lit(mrb, result, "\0"); + printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s)); + + if (mrdb->print_no == 0) { + mrdb->print_no = 1; + } + + mrb_gc_arena_restore(mrb, ai); + + return DBGST_PROMPT; +} + +dbgcmd_state +dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb) +{ + return dbgcmd_print(mrb, mrdb); +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c new file mode 100644 index 00000000..cb4c738f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c @@ -0,0 +1,64 @@ +/* +** cmdrun.c - mruby debugger run command functions +** +*/ + +#include <mruby/opcode.h> +#include "mrdb.h" + +dbgcmd_state +dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_debug_context *dbg = mrdb->dbg; + + if (dbg->xm == DBG_INIT){ + dbg->xm = DBG_RUN; + } + else { + dbg->xm = DBG_QUIT; + if (dbg->xphase == DBG_PHASE_RUNNING){ + struct RClass *exc; + puts("Start it from the beginning."); + exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception")); + mrb_raise(mrb, exc, "Restart mrdb."); + } + } + + return DBGST_RESTART; +} + +dbgcmd_state +dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_debug_context *dbg = mrdb->dbg; + int ccnt = 1; + + if (mrdb->wcnt > 1){ + sscanf(mrdb->words[1], "%d", &ccnt); + } + dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */ + + if (dbg->xphase == DBG_PHASE_AFTER_RUN){ + puts("The program is not running."); + dbg->xm = DBG_QUIT; + } + else { + dbg->xm = DBG_RUN; + } + return DBGST_CONTINUE; +} + +dbgcmd_state +dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb) +{ + mrdb->dbg->xm = DBG_STEP; + return DBGST_CONTINUE; +} + +dbgcmd_state +dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb) +{ + mrdb->dbg->xm = DBG_NEXT; + mrdb->dbg->prvci = mrb->c->ci; + return DBGST_CONTINUE; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c new file mode 100644 index 00000000..d12dcd5d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c @@ -0,0 +1,759 @@ +/* +** mrdb.c - mruby debugger +** +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include <mruby.h> +#include <mruby/dump.h> +#include <mruby/debug.h> +#include <mruby/class.h> +#include <mruby/opcode.h> +#include <mruby/variable.h> + +#include "mrdb.h" +#include "apibreak.h" +#include "apilist.h" + +void mrdb_state_free(mrb_state *); + +static mrb_debug_context *_debug_context = NULL; +static mrdb_state *_mrdb_state = NULL; + +struct _args { + FILE *rfp; + char* fname; + char* srcpath; + int argc; + char** argv; + mrb_bool mrbfile : 1; +}; + +typedef struct debug_command { + const char *cmd1; + const char *cmd2; + uint8_t len1; + uint8_t len2; + uint8_t div; + debug_command_id id; + debug_command_func func; +} debug_command; + +static const debug_command debug_command_list[] = { + {"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */ + {"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */ + {"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */ + {"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */ + {"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */ + {"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */ + {"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */ + {"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */ + {"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */ + {"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */ + {"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */ + {"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */ + {"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */ + {"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */ + {NULL} +}; + + +static void +usage(const char *name) +{ + static const char *const usage_msg[] = { + "switches:", + "-b load and execute RiteBinary (mrb) file", + "-d specify source directory", + "--version print the version", + "--copyright print the copyright", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches] programfile\n", name); + while (*p) { + printf(" %s\n", *p++); + } +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) +{ + char **origargv = argv; + static const struct _args args_zero = { 0 }; + + *args = args_zero; + + for (argc--,argv++; argc > 0; argc--,argv++) { + char *item; + if (argv[0][0] != '-') break; + + item = argv[0] + 1; + switch (*item++) { + case 'b': + args->mrbfile = TRUE; + break; + case 'd': + if (item[0]) { + goto append_srcpath; + } + else if (argc > 1) { + argc--; argv++; + item = argv[0]; +append_srcpath: + if (!args->srcpath) { + size_t buflen; + char *buf; + + buflen = strlen(item) + 1; + buf = (char *)mrb_malloc(mrb, buflen); + memcpy(buf, item, buflen); + args->srcpath = buf; + } + else { + size_t srcpathlen; + size_t itemlen; + + srcpathlen = strlen(args->srcpath); + itemlen = strlen(item); + args->srcpath = + (char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2); + args->srcpath[srcpathlen] = '\n'; + memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1); + } + } + else { + printf("%s: No path specified for -d\n", *origargv); + return EXIT_SUCCESS; + } + break; + case '-': + if (strcmp((*argv) + 2, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp((*argv) + 2, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + default: + return EXIT_FAILURE; + } + } + + if (args->rfp == NULL) { + if (*argv == NULL) { + printf("%s: Program file not specified.\n", *origargv); + return EXIT_FAILURE; + } + else { + args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); + if (args->rfp == NULL) { + printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); + return EXIT_FAILURE; + } + args->fname = argv[0]; + argc--; argv++; + } + } + args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); + memcpy(args->argv, argv, (argc+1) * sizeof(char*)); + args->argc = argc; + + return EXIT_SUCCESS; +} + +static void +cleanup(mrb_state *mrb, struct _args *args) +{ + if (args->rfp) + fclose(args->rfp); + if (args->srcpath) + mrb_free(mrb, args->srcpath); + if (args->argv) + mrb_free(mrb, args->argv); + mrdb_state_free(mrb); + mrb_close(mrb); +} + +static mrb_debug_context* +mrb_debug_context_new(mrb_state *mrb) +{ + mrb_debug_context *dbg = mrb_malloc(mrb, sizeof(mrb_debug_context)); + + memset(dbg, 0, sizeof(mrb_debug_context)); + + dbg->xm = DBG_INIT; + dbg->xphase = DBG_PHASE_BEFORE_RUN; + dbg->next_bpno = 1; + + return dbg; +} + +mrb_debug_context* +mrb_debug_context_get(mrb_state *mrb) +{ + if (!_debug_context) { + _debug_context = mrb_debug_context_new(mrb); + } + return _debug_context; +} + +void +mrb_debug_context_set(mrb_debug_context *dbg) +{ + _debug_context = dbg; +} + +void +mrb_debug_context_free(mrb_state *mrb) +{ + if (_debug_context) { + mrb_debug_delete_break_all(mrb, _debug_context); + mrb_free(mrb, _debug_context); + _debug_context = NULL; + } +} + +static mrdb_state* +mrdb_state_new(mrb_state *mrb) +{ + mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrdb_state)); + + memset(mrdb, 0, sizeof(mrdb_state)); + + mrdb->dbg = mrb_debug_context_get(mrb); + mrdb->command = mrb_malloc(mrb, MAX_COMMAND_LINE+1); + mrdb->print_no = 1; + + return mrdb; +} + +mrdb_state* +mrdb_state_get(mrb_state *mrb) +{ + if (!_mrdb_state) { + _mrdb_state = mrdb_state_new(mrb); + } + return _mrdb_state; +} + +void +mrdb_state_set(mrdb_state *mrdb) +{ + _mrdb_state = mrdb; +} + +void +mrdb_state_free(mrb_state *mrb) +{ + mrb_debug_context_free(mrb); + if (_mrdb_state) { + mrb_free(mrb, _mrdb_state->command); + mrb_free(mrb, _mrdb_state); + _mrdb_state = NULL; + } +} + +static char* +get_command(mrb_state *mrb, mrdb_state *mrdb) +{ + int i; + int c; + + for (i=0; i<MAX_COMMAND_LINE; i++) { + if ((c=getchar()) == EOF || c == '\n') break; + mrdb->command[i] = c; + } + + if (i == 0 && feof(stdin)) { + clearerr(stdin); + strcpy(mrdb->command, "quit"); + i += sizeof("quit") - 1; + } + + if (i == MAX_COMMAND_LINE) { + for ( ; (c=getchar()) != EOF && c !='\n'; i++) ; + } + + if (i > MAX_COMMAND_LINE) { + printf("command line too long.\n"); + i = 0; /* discard command data */ + } + mrdb->command[i] = '\0'; + + return mrdb->command; +} + +static char* +pick_out_word(mrb_state *mrb, char **pp) +{ + char *ps; + + for (ps=*pp; ISBLANK(*ps); ps++) ; + if (*ps == '\0') { + return NULL; + } + + if (*ps == '\"' || *ps == '\'') { + *pp = strchr(ps+1, *ps); + if (*pp) (*pp)++; + } + else { + *pp = strpbrk(ps, " \t"); + } + + if (!*pp) { + *pp = ps + strlen(ps); + } + + if (**pp != '\0') { + **pp = '\0'; + (*pp)++; + } + + return ps; +} + +static debug_command* +parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf) +{ + debug_command *cmd = NULL; + char *p = buf; + size_t wlen; + + /* get word #1 */ + mrdb->words[0] = pick_out_word(mrb, &p); + if (!mrdb->words[0]) { + return NULL; + } + mrdb->wcnt = 1; + /* set remain parameter */ + for ( ; *p && ISBLANK(*p); p++) ; + if (*p) { + mrdb->words[mrdb->wcnt++] = p; + } + + /* check word #1 */ + for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) { + wlen = strlen(mrdb->words[0]); + if (wlen >= cmd->len1 && + strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) { + break; + } + } + + if (cmd->cmd2) { + if (mrdb->wcnt > 1) { + /* get word #2 */ + mrdb->words[1] = pick_out_word(mrb, &p); + if (mrdb->words[1]) { + /* update remain parameter */ + for ( ; *p && ISBLANK(*p); p++) ; + if (*p) { + mrdb->words[mrdb->wcnt++] = p; + } + } + } + + /* check word #1,#2 */ + for ( ; cmd->cmd1; cmd++) { + wlen = strlen(mrdb->words[0]); + if (wlen < cmd->len1 || + strncmp(mrdb->words[0], cmd->cmd1, wlen)) { + continue; + } + + if (!cmd->cmd2) break; /* word #1 only */ + + if (mrdb->wcnt == 1) continue; /* word #2 not specified */ + + wlen = strlen(mrdb->words[1]); + if (wlen >= cmd->len2 && + strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) { + break; /* word #1 and #2 */ + } + } + } + + /* divide remain parameters */ + if (cmd->cmd1 && cmd->div) { + p = mrdb->words[--mrdb->wcnt]; + for ( ; mrdb->wcnt<MAX_COMMAND_WORD; mrdb->wcnt++) { + mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p); + if (!mrdb->words[mrdb->wcnt]) { + break; + } + } + } + + return cmd->cmd1 ? cmd : NULL; +} + +static void +print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb) +{ + mrb_debug_breakpoint bp; + int32_t ret; + uint16_t lineno; + const char *file; + const char *method_name; + const char *class_name; + + ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp); + if (ret == 0) { + switch(bp.type) { + case MRB_DEBUG_BPTYPE_LINE: + file = bp.point.linepoint.file; + lineno = bp.point.linepoint.lineno; + printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno); + break; + case MRB_DEBUG_BPTYPE_METHOD: + method_name = bp.point.methodpoint.method_name; + class_name = bp.point.methodpoint.class_name; + if (class_name == NULL) { + printf("Breakpoint %d, %s\n", bp.bpno, method_name); + } + else { + printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name); + } + if (mrdb->dbg->isCfunc) { + printf("Stopped before calling the C function.\n"); + } + break; + default: + break; + } + } +} + +static void +print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb) +{ + const char* file = mrdb->dbg->prvfile; + uint16_t lineno = mrdb->dbg->prvline; + printf("%s:%d\n", file, lineno); +} + +static void +print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb) +{ + char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile); + uint16_t lineno = mrdb->dbg->prvline; + if (file != NULL) { + mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno); + mrb_free(mrb, file); + } +} + +static void +print_info_stopped(mrb_state *mrb, mrdb_state *mrdb) +{ + switch(mrdb->dbg->bm) { + case BRK_BREAK: + print_info_stopped_break(mrb, mrdb); + print_info_stopped_code(mrb, mrdb); + break; + case BRK_STEP: + case BRK_NEXT: + print_info_stopped_step_next(mrb, mrdb); + print_info_stopped_code(mrb, mrdb); + break; + default: + break; + } +} + +static debug_command* +get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb) +{ + debug_command *cmd = NULL; + char *p; + int i; + + while (!cmd) { + for (p=NULL; !p || *p=='\0'; ) { + printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline); + fflush(stdout); + p = get_command(mrb, mrdb); + } + + cmd = parse_command(mrb, mrdb, p); +#ifdef _DBG_MRDB_PARSER_ + for (i=0; i<mrdb->wcnt; i++) { + printf("%d: %s\n", i, mrdb->words[i]); + } +#endif + if (!cmd) { + printf("invalid command ("); + for (i=0; i<mrdb->wcnt; i++) { + if (i>0) { + printf(" "); + } + printf("%s", mrdb->words[i]); + } + puts(")"); + } + } + return cmd; +} + +static int32_t +check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs) +{ + struct RClass* c; + mrb_sym sym; + int32_t bpno; + mrb_bool isCfunc; + + mrb_debug_context *dbg = mrb_debug_context_get(mrb); + + isCfunc = FALSE; + bpno = dbg->method_bpno; + dbg->method_bpno = 0; + + switch(GET_OPCODE(*pc)) { + case OP_SEND: + case OP_SENDB: + c = mrb_class(mrb, regs[GETARG_A(*pc)]); + sym = irep->syms[GETARG_B(*pc)]; + break; + case OP_SUPER: + c = mrb->c->ci->target_class->super; + sym = mrb->c->ci->mid; + break; + default: + sym = 0; + break; + } + if (sym != 0) { + dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc); + if (isCfunc) { + bpno = dbg->method_bpno; + dbg->method_bpno = 0; + } + } + dbg->isCfunc = isCfunc; + return bpno; +} + +static void +mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs) +{ + const char *file; + int32_t line; + int32_t bpno; + + mrb_debug_context *dbg = mrb_debug_context_get(mrb); + + mrb_assert(dbg); + + dbg->irep = irep; + dbg->pc = pc; + dbg->regs = regs; + + if (dbg->xphase == DBG_PHASE_RESTART) { + dbg->root_irep = irep; + dbg->prvfile = NULL; + dbg->prvline = 0; + dbg->prvci = NULL; + dbg->xm = DBG_RUN; + dbg->xphase = DBG_PHASE_RUNNING; + } + + file = mrb_debug_get_filename(irep, pc - irep->iseq); + line = mrb_debug_get_line(irep, pc - irep->iseq); + + switch (dbg->xm) { + case DBG_STEP: + if (!file || (dbg->prvfile == file && dbg->prvline == line)) { + return; + } + dbg->method_bpno = 0; + dbg->bm = BRK_STEP; + break; + + case DBG_NEXT: + if (!file || (dbg->prvfile == file && dbg->prvline == line)) { + return; + } + if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) { + return; + } + dbg->prvci = NULL; + dbg->method_bpno = 0; + dbg->bm = BRK_NEXT; + break; + + case DBG_RUN: + bpno = check_method_breakpoint(mrb, irep, pc, regs); + if (bpno > 0) { + dbg->stopped_bpno = bpno; + dbg->bm = BRK_BREAK; + break; + } + if (dbg->prvfile != file || dbg->prvline != line) { + bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line); + if (bpno > 0) { + dbg->stopped_bpno = bpno; + dbg->bm = BRK_BREAK; + break; + } + } + dbg->prvfile = file; + dbg->prvline = line; + return; + case DBG_INIT: + dbg->root_irep = irep; + dbg->bm = BRK_INIT; + if (!file || line < 0) { + puts("Cannot get debugging information."); + } + break; + + default: + return; + } + + dbg->prvfile = file; + dbg->prvline = line; + + if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) { + return; + } + dbg->break_hook(mrb, dbg); + + dbg->xphase = DBG_PHASE_RUNNING; +} + +static mrdb_exemode +mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg) +{ + debug_command *cmd; + dbgcmd_state st = DBGST_CONTINUE; + mrdb_state *mrdb = mrdb_state_get(mrb); + + print_info_stopped(mrb, mrdb); + + while (1) { + cmd = get_and_parse_command(mrb, mrdb); + mrb_assert(cmd); + + st = cmd->func(mrb, mrdb); + + if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break; + } + return dbg->xm; +} + +int +main(int argc, char **argv) +{ + mrb_state *mrb = mrb_open(); + int n = -1; + struct _args args; + mrb_value v; + mrdb_state *mrdb; + mrdb_state *mrdb_backup; + mrb_debug_context* dbg_backup; + debug_command *cmd; + + l_restart: + + if (mrb == NULL) { + fputs("Invalid mrb_state, exiting mruby\n", stderr); + return EXIT_FAILURE; + } + + /* parse command parameters */ + n = parse_args(mrb, argc, argv, &args); + if (n == EXIT_FAILURE || args.rfp == NULL) { + cleanup(mrb, &args); + usage(argv[0]); + return n; + } + + /* initialize debugger information */ + mrdb = mrdb_state_get(mrb); + mrb_assert(mrdb && mrdb->dbg); + mrdb->srcpath = args.srcpath; + + if (mrdb->dbg->xm == DBG_QUIT) { + mrdb->dbg->xphase = DBG_PHASE_RESTART; + } + else { + mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN; + } + mrdb->dbg->xm = DBG_INIT; + mrdb->dbg->ccnt = 1; + + /* setup hook functions */ + mrb->code_fetch_hook = mrb_code_fetch_hook; + mrdb->dbg->break_hook = mrb_debug_break_hook; + + if (args.mrbfile) { /* .mrb */ + v = mrb_load_irep_file(mrb, args.rfp); + } + else { /* .rb */ + mrbc_context *cc = mrbc_context_new(mrb); + mrbc_filename(mrb, cc, args.fname); + v = mrb_load_file_cxt(mrb, args.rfp, cc); + mrbc_context_free(mrb, cc); + } + if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) { + const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc)); + if (!strcmp(classname, "DebuggerExit")) { + cleanup(mrb, &args); + return 0; + } + if (!strcmp(classname, "DebuggerRestart")) { + mrdb_backup = mrdb_state_get(mrb); + dbg_backup = mrb_debug_context_get(mrb); + + mrdb_state_set(NULL); + mrb_debug_context_set(NULL); + + cleanup(mrb, &args); + mrb = mrb_open(); + + mrdb_state_set(mrdb_backup); + mrb_debug_context_set(dbg_backup); + + goto l_restart; + } + } + puts("mruby application exited."); + mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN; + if (!mrb_undef_p(v)) { + if (mrb->exc) { + mrb_print_error(mrb); + } + else { + printf(" => "); + mrb_p(mrb, v); + } + } + + mrdb->dbg->prvfile = "-"; + mrdb->dbg->prvline = 0; + + while (1) { + cmd = get_and_parse_command(mrb, mrdb); + mrb_assert(cmd); + + if (cmd->id == DBGCMD_QUIT) { + break; + } + + if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart; + } + + cleanup(mrb, &args); + + return 0; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h new file mode 100644 index 00000000..5ac12c1c --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h @@ -0,0 +1,165 @@ +/* +** mrdb.h - mruby debugger +** +*/ + +#ifndef MRDB_H +#define MRDB_H + +#include <mruby.h> + +#include "mrdbconf.h" + +#ifdef _MSC_VER +# define __func__ __FUNCTION__ +#endif + +#define MAX_COMMAND_WORD (16) + +typedef enum debug_command_id { + DBGCMD_RUN, + DBGCMD_CONTINUE, + DBGCMD_NEXT, + DBGCMD_STEP, + DBGCMD_BREAK, + DBGCMD_INFO_BREAK, + DBGCMD_WATCH, + DBGCMD_INFO_WATCH, + DBGCMD_ENABLE, + DBGCMD_DISABLE, + DBGCMD_DELETE, + DBGCMD_PRINT, + DBGCMD_DISPLAY, + DBGCMD_INFO_DISPLAY, + DBGCMD_DELETE_DISPLAY, + DBGCMD_EVAL, + DBGCMD_BACKTRACE, + DBGCMD_LIST, + DBGCMD_HELP, + DBGCMD_QUIT, + DBGCMD_UNKNOWN +} debug_command_id; + +typedef enum dbgcmd_state { + DBGST_CONTINUE, + DBGST_PROMPT, + DBGST_COMMAND_ERROR, + DBGST_MAX, + DBGST_RESTART +} dbgcmd_state; + +typedef enum mrdb_exemode { + DBG_INIT, + DBG_RUN, + DBG_STEP, + DBG_NEXT, + DBG_QUIT, +} mrdb_exemode; + +typedef enum mrdb_exephase { + DBG_PHASE_BEFORE_RUN, + DBG_PHASE_RUNNING, + DBG_PHASE_AFTER_RUN, + DBG_PHASE_RESTART, +} mrdb_exephase; + +typedef enum mrdb_brkmode { + BRK_INIT, + BRK_BREAK, + BRK_STEP, + BRK_NEXT, + BRK_QUIT, +} mrdb_brkmode; + +typedef enum { + MRB_DEBUG_BPTYPE_NONE, + MRB_DEBUG_BPTYPE_LINE, + MRB_DEBUG_BPTYPE_METHOD, +} mrb_debug_bptype; + +struct mrb_irep; +struct mrbc_context; +struct mrb_debug_context; + +typedef struct mrb_debug_linepoint { + const char *file; + uint16_t lineno; +} mrb_debug_linepoint; + +typedef struct mrb_debug_methodpoint { + const char *class_name; + const char *method_name; +} mrb_debug_methodpoint; + +typedef struct mrb_debug_breakpoint { + uint32_t bpno; + uint8_t enable; + mrb_debug_bptype type; + union point { + mrb_debug_linepoint linepoint; + mrb_debug_methodpoint methodpoint; + } point; +} mrb_debug_breakpoint; + +typedef struct mrb_debug_context { + struct mrb_irep *root_irep; + struct mrb_irep *irep; + mrb_code *pc; + mrb_value *regs; + + const char *prvfile; + int32_t prvline; + mrb_callinfo *prvci; + + mrdb_exemode xm; + mrdb_exephase xphase; + mrdb_brkmode bm; + int16_t bmi; + + uint16_t ccnt; + uint16_t scnt; + + mrb_debug_breakpoint bp[MAX_BREAKPOINT]; + uint32_t bpnum; + int32_t next_bpno; + int32_t method_bpno; + int32_t stopped_bpno; + mrb_bool isCfunc; + + mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg); + +} mrb_debug_context; + +typedef struct mrdb_state { + char *command; + uint8_t wcnt; + uint8_t pi; + char *words[MAX_COMMAND_WORD]; + const char *srcpath; + uint32_t print_no; + + mrb_debug_context *dbg; +} mrdb_state; + +typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*); + +/* cmdrun.c */ +dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*); +/* cmdbreak.c */ +dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*); +/* cmdprint.c */ +dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*); +/* cmdmisc.c */ +dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*); +dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*); + +#endif diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h new file mode 100644 index 00000000..f17f9c57 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h @@ -0,0 +1,16 @@ +/* +** mrdbconf.h - mruby debugger configuration +** +*/ + +#ifndef MRDBCONF_H +#define MRDBCONF_H + +/* configuration options: */ +/* maximum size for command buffer */ +#define MAX_COMMAND_LINE 1024 + +/* maximum number of setable breakpoint */ +#define MAX_BREAKPOINT 5 + +#endif diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h new file mode 100644 index 00000000..c7812b0d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h @@ -0,0 +1,20 @@ +/* +** mrdberror.h - mruby debugger error code +** +*/ + +#ifndef MRDBERROR_H +#define MRDBERROR_H + +#define MRB_DEBUG_OK (0) +#define MRB_DEBUG_NOBUF (-1) +#define MRB_DEBUG_INVALID_ARGUMENT (-2) + +#define MRB_DEBUG_BREAK_INVALID_LINENO (-11) +#define MRB_DEBUG_BREAK_INVALID_FILE (-12) +#define MRB_DEBUG_BREAK_INVALID_NO (-13) +#define MRB_DEBUG_BREAK_NUM_OVER (-14) +#define MRB_DEBUG_BREAK_NO_OVER (-15) + +#endif + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb new file mode 100644 index 00000000..ed53321b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb @@ -0,0 +1,12 @@ +require 'open3' + +assert('mirb normal operations') do + o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1\nb=2\na+b\n") + assert_true o.include?('=> 3') + assert_true o.include?('=> 2') +end + +assert('regression for #1563') do + o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1;b=2;c=3\nb\nc") + assert_true o.include?('=> 3') +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake new file mode 100644 index 00000000..a74871d8 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake @@ -0,0 +1,33 @@ +MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mirb command' + + if spec.build.cc.search_header_path 'readline/readline.h' + spec.cc.defines << "ENABLE_READLINE" + if spec.build.cc.search_header_path 'termcap.h' + if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD + if spec.build.cc.search_header_path 'termcap.h' + if MRUBY_BUILD_HOST_IS_CYGWIN then + spec.linker.libraries << 'ncurses' + else + spec.linker.libraries << 'termcap' + end + end + end + end + if RUBY_PLATFORM.include?('netbsd') + spec.linker.libraries << 'edit' + else + spec.linker.libraries << 'readline' + if spec.build.cc.search_header_path 'curses.h' + spec.linker.libraries << 'ncurses' + end + end + elsif spec.build.cc.search_header_path 'linenoise.h' + spec.cc.defines << "ENABLE_LINENOISE" + end + + spec.bins = %w(mirb) + spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c new file mode 100644 index 00000000..fe311d83 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -0,0 +1,584 @@ +/* +** mirb - Embeddable Interactive Ruby Shell +** +** This program takes code from the user in +** an interactive way and executes it +** immediately. It's a REPL... +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include <signal.h> +#include <setjmp.h> + +#ifdef ENABLE_READLINE +#include <readline/readline.h> +#include <readline/history.h> +#define MIRB_ADD_HISTORY(line) add_history(line) +#define MIRB_READLINE(ch) readline(ch) +#define MIRB_WRITE_HISTORY(path) write_history(path) +#define MIRB_READ_HISTORY(path) read_history(path) +#define MIRB_USING_HISTORY() using_history() +#elif defined(ENABLE_LINENOISE) +#define ENABLE_READLINE +#include <linenoise.h> +#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line) +#define MIRB_READLINE(ch) linenoise(ch) +#define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path) +#define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path) +#define MIRB_USING_HISTORY() +#endif + +#ifndef _WIN32 +#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1) +#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val) +#define SIGJMP_BUF sigjmp_buf +#else +#define MIRB_SIGSETJMP(env) setjmp(env) +#define MIRB_SIGLONGJMP(env, val) longjmp(env, val) +#define SIGJMP_BUF jmp_buf +#endif + +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/proc.h> +#include <mruby/compile.h> +#include <mruby/string.h> + +#ifdef ENABLE_READLINE + +static const char history_file_name[] = ".mirb_history"; + +static char * +get_history_path(mrb_state *mrb) +{ + char *path = NULL; + const char *home = getenv("HOME"); + +#ifdef _WIN32 + if (home != NULL) { + home = getenv("USERPROFILE"); + } +#endif + + if (home != NULL) { + int len = snprintf(NULL, 0, "%s/%s", home, history_file_name); + if (len >= 0) { + size_t size = len + 1; + path = (char *)mrb_malloc_simple(mrb, size); + if (path != NULL) { + int n = snprintf(path, size, "%s/%s", home, history_file_name); + if (n != len) { + mrb_free(mrb, path); + path = NULL; + } + } + } + } + + return path; +} + +#endif + +static void +p(mrb_state *mrb, mrb_value obj, int prompt) +{ + mrb_value val; + + val = mrb_funcall(mrb, obj, "inspect", 0); + if (prompt) { + if (!mrb->exc) { + fputs(" => ", stdout); + } + else { + val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); + } + } + if (!mrb_string_p(val)) { + val = mrb_obj_as_string(mrb, obj); + } + fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); + putc('\n', stdout); +} + +/* Guess if the user might want to enter more + * or if he wants an evaluation of his code now */ +static mrb_bool +is_code_block_open(struct mrb_parser_state *parser) +{ + mrb_bool code_block_open = FALSE; + + /* check for heredoc */ + if (parser->parsing_heredoc != NULL) return TRUE; + if (parser->heredoc_end_now) { + parser->heredoc_end_now = FALSE; + return FALSE; + } + + /* check for unterminated string */ + if (parser->lex_strterm) return TRUE; + + /* check if parser error are available */ + if (0 < parser->nerr) { + const char unexpected_end[] = "syntax error, unexpected $end"; + const char *message = parser->error_buffer[0].message; + + /* a parser error occur, we have to check if */ + /* we need to read one more line or if there is */ + /* a different issue which we have to show to */ + /* the user */ + + if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) { + code_block_open = TRUE; + } + else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) { + code_block_open = FALSE; + } + else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) { + code_block_open = FALSE; + } + return code_block_open; + } + + switch (parser->lstate) { + + /* all states which need more code */ + + case EXPR_BEG: + /* beginning of a statement, */ + /* that means previous line ended */ + code_block_open = FALSE; + break; + case EXPR_DOT: + /* a message dot was the last token, */ + /* there has to come more */ + code_block_open = TRUE; + break; + case EXPR_CLASS: + /* a class keyword is not enough! */ + /* we need also a name of the class */ + code_block_open = TRUE; + break; + case EXPR_FNAME: + /* a method name is necessary */ + code_block_open = TRUE; + break; + case EXPR_VALUE: + /* if, elsif, etc. without condition */ + code_block_open = TRUE; + break; + + /* now all the states which are closed */ + + case EXPR_ARG: + /* an argument is the last token */ + code_block_open = FALSE; + break; + + /* all states which are unsure */ + + case EXPR_CMDARG: + break; + case EXPR_END: + /* an expression was ended */ + break; + case EXPR_ENDARG: + /* closing parenthese */ + break; + case EXPR_ENDFN: + /* definition end */ + break; + case EXPR_MID: + /* jump keyword like break, return, ... */ + break; + case EXPR_MAX_STATE: + /* don't know what to do with this token */ + break; + default: + /* this state is unexpected! */ + break; + } + + return code_block_open; +} + +struct _args { + FILE *rfp; + mrb_bool verbose : 1; + int argc; + char** argv; +}; + +static void +usage(const char *name) +{ + static const char *const usage_msg[] = { + "switches:", + "-v print version number, then run in verbose mode", + "--verbose run in verbose mode", + "--version print the version", + "--copyright print the copyright", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches]\n", name); + while (*p) + printf(" %s\n", *p++); +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) +{ + static const struct _args args_zero = { 0 }; + + *args = args_zero; + + for (argc--,argv++; argc > 0; argc--,argv++) { + char *item; + if (argv[0][0] != '-') break; + + item = argv[0] + 1; + switch (*item++) { + case 'v': + if (!args->verbose) mrb_show_version(mrb); + args->verbose = TRUE; + break; + case '-': + if (strcmp((*argv) + 2, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp((*argv) + 2, "verbose") == 0) { + args->verbose = TRUE; + break; + } + else if (strcmp((*argv) + 2, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + default: + return EXIT_FAILURE; + } + } + + if (args->rfp == NULL) { + if (*argv != NULL) { + args->rfp = fopen(argv[0], "r"); + if (args->rfp == NULL) { + printf("Cannot open program file. (%s)\n", *argv); + return EXIT_FAILURE; + } + argc--; argv++; + } + } + args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); + memcpy(args->argv, argv, (argc+1) * sizeof(char*)); + args->argc = argc; + + return EXIT_SUCCESS; +} + +static void +cleanup(mrb_state *mrb, struct _args *args) +{ + if (args->rfp) + fclose(args->rfp); + mrb_free(mrb, args->argv); + mrb_close(mrb); +} + +/* Print a short remark for the user */ +static void +print_hint(void) +{ + printf("mirb - Embeddable Interactive Ruby Shell\n\n"); +} + +#ifndef ENABLE_READLINE +/* Print the command line prompt of the REPL */ +static void +print_cmdline(int code_block_open) +{ + if (code_block_open) { + printf("* "); + } + else { + printf("> "); + } + fflush(stdout); +} +#endif + +void mrb_codedump_all(mrb_state*, struct RProc*); + +static int +check_keyword(const char *buf, const char *word) +{ + const char *p = buf; + size_t len = strlen(word); + + /* skip preceding spaces */ + while (*p && isspace((unsigned char)*p)) { + p++; + } + /* check keyword */ + if (strncmp(p, word, len) != 0) { + return 0; + } + p += len; + /* skip trailing spaces */ + while (*p) { + if (!isspace((unsigned char)*p)) return 0; + p++; + } + return 1; +} + + +#ifndef ENABLE_READLINE +volatile sig_atomic_t input_canceled = 0; +void +ctrl_c_handler(int signo) +{ + input_canceled = 1; +} +#else +SIGJMP_BUF ctrl_c_buf; +void +ctrl_c_handler(int signo) +{ + MIRB_SIGLONGJMP(ctrl_c_buf, 1); +} +#endif + +int +main(int argc, char **argv) +{ + char ruby_code[4096] = { 0 }; + char last_code_line[1024] = { 0 }; +#ifndef ENABLE_READLINE + int last_char; + size_t char_index; +#else + char *history_path; + char* line; +#endif + mrbc_context *cxt; + struct mrb_parser_state *parser; + mrb_state *mrb; + mrb_value result; + struct _args args; + mrb_value ARGV; + int n; + int i; + mrb_bool code_block_open = FALSE; + int ai; + unsigned int stack_keep = 0; + + /* new interpreter instance */ + mrb = mrb_open(); + if (mrb == NULL) { + fputs("Invalid mrb interpreter, exiting mirb\n", stderr); + return EXIT_FAILURE; + } + + n = parse_args(mrb, argc, argv, &args); + if (n == EXIT_FAILURE) { + cleanup(mrb, &args); + usage(argv[0]); + return n; + } + + ARGV = mrb_ary_new_capa(mrb, args.argc); + for (i = 0; i < args.argc; i++) { + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } + } + mrb_define_global_const(mrb, "ARGV", ARGV); + +#ifdef ENABLE_READLINE + history_path = get_history_path(mrb); + if (history_path == NULL) { + fputs("failed to get history path\n", stderr); + mrb_close(mrb); + return EXIT_FAILURE; + } + + MIRB_USING_HISTORY(); + MIRB_READ_HISTORY(history_path); +#endif + + print_hint(); + + cxt = mrbc_context_new(mrb); + cxt->capture_errors = TRUE; + cxt->lineno = 1; + mrbc_filename(mrb, cxt, "(mirb)"); + if (args.verbose) cxt->dump_result = TRUE; + + ai = mrb_gc_arena_save(mrb); + + while (TRUE) { + char *utf8; + + if (args.rfp) { + if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL) + goto done; + break; + } + +#ifndef ENABLE_READLINE + print_cmdline(code_block_open); + + signal(SIGINT, ctrl_c_handler); + char_index = 0; + while ((last_char = getchar()) != '\n') { + if (last_char == EOF) break; + if (char_index >= sizeof(last_code_line)-2) { + fputs("input string too long\n", stderr); + continue; + } + last_code_line[char_index++] = last_char; + } + signal(SIGINT, SIG_DFL); + if (input_canceled) { + ruby_code[0] = '\0'; + last_code_line[0] = '\0'; + code_block_open = FALSE; + puts("^C"); + input_canceled = 0; + continue; + } + if (last_char == EOF) { + fputs("\n", stdout); + break; + } + + last_code_line[char_index++] = '\n'; + last_code_line[char_index] = '\0'; +#else + if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) { + ; + } + else { + ruby_code[0] = '\0'; + last_code_line[0] = '\0'; + code_block_open = FALSE; + puts("^C"); + } + signal(SIGINT, ctrl_c_handler); + line = MIRB_READLINE(code_block_open ? "* " : "> "); + signal(SIGINT, SIG_DFL); + + if (line == NULL) { + printf("\n"); + break; + } + if (strlen(line) > sizeof(last_code_line)-2) { + fputs("input string too long\n", stderr); + continue; + } + strcpy(last_code_line, line); + strcat(last_code_line, "\n"); + MIRB_ADD_HISTORY(line); + free(line); +#endif + +done: + + if (code_block_open) { + if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) { + fputs("concatenated input string too long\n", stderr); + continue; + } + strcat(ruby_code, last_code_line); + } + else { + if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) { + break; + } + strcpy(ruby_code, last_code_line); + } + + utf8 = mrb_utf8_from_locale(ruby_code, -1); + if (!utf8) abort(); + + /* parse code */ + parser = mrb_parser_new(mrb); + if (parser == NULL) { + fputs("create parser state error\n", stderr); + break; + } + parser->s = utf8; + parser->send = utf8 + strlen(utf8); + parser->lineno = cxt->lineno; + mrb_parser_parse(parser, cxt); + code_block_open = is_code_block_open(parser); + mrb_utf8_free(utf8); + + if (code_block_open) { + /* no evaluation of code */ + } + else { + if (0 < parser->nerr) { + /* syntax error */ + printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message); + } + else { + /* generate bytecode */ + struct RProc *proc = mrb_generate_code(mrb, parser); + if (proc == NULL) { + fputs("codegen error\n", stderr); + mrb_parser_free(parser); + break; + } + + if (args.verbose) { + mrb_codedump_all(mrb, proc); + } + /* pass a proc for evaluation */ + /* evaluate the bytecode */ + result = mrb_vm_run(mrb, + proc, + mrb_top_self(mrb), + stack_keep); + stack_keep = proc->body.irep->nlocals; + /* did an exception occur? */ + if (mrb->exc) { + p(mrb, mrb_obj_value(mrb->exc), 0); + mrb->exc = 0; + } + else { + /* no */ + if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){ + result = mrb_any_to_s(mrb, result); + } + p(mrb, result, 1); + } + } + ruby_code[0] = '\0'; + last_code_line[0] = '\0'; + mrb_gc_arena_restore(mrb, ai); + } + mrb_parser_free(parser); + cxt->lineno++; + } + +#ifdef ENABLE_READLINE + MIRB_WRITE_HISTORY(history_path); + mrb_free(mrb, history_path); +#endif + + mrbc_context_free(mrb, cxt); + mrb_close(mrb); + + return 0; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake new file mode 100644 index 00000000..e710b5a4 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake @@ -0,0 +1,16 @@ +MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby compiler executable' + + spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler' + + exec = exefile("#{build.build_dir}/bin/mrbc") + mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten + + file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t| + build.linker.run t.name, t.prerequisites + end + + build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' } +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c new file mode 100644 index 00000000..580c2e25 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c @@ -0,0 +1,336 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <mruby.h> +#include <mruby/compile.h> +#include <mruby/dump.h> +#include <mruby/proc.h> + +#define RITEBIN_EXT ".mrb" +#define C_EXT ".c" + +struct mrbc_args { + int argc; + char **argv; + int idx; + const char *prog; + const char *outfile; + const char *initname; + mrb_bool check_syntax : 1; + mrb_bool verbose : 1; + unsigned int flags : 4; +}; + +static void +usage(const char *name) +{ + static const char *const usage_msg[] = { + "switches:", + "-c check syntax only", + "-o<outfile> place the output into <outfile>", + "-v print version number, then turn on verbose mode", + "-g produce debugging information", + "-B<symbol> binary <symbol> output in C language format", + "-e generate little endian iseq data", + "-E generate big endian iseq data", + "--verbose run at verbose mode", + "--version print the version", + "--copyright print the copyright", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches] programfile\n", name); + while (*p) + printf(" %s\n", *p++); +} + +static char * +get_outfilename(mrb_state *mrb, char *infile, const char *ext) +{ + size_t infilelen; + size_t extlen; + char *outfile; + char *p; + + infilelen = strlen(infile); + extlen = strlen(ext); + outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1); + memcpy(outfile, infile, infilelen + 1); + if (*ext) { + if ((p = strrchr(outfile, '.')) == NULL) + p = outfile + infilelen; + memcpy(p, ext, extlen + 1); + } + + return outfile; +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) +{ + char *outfile = NULL; + static const struct mrbc_args args_zero = { 0 }; + int i; + + *args = args_zero; + args->argc = argc; + args->argv = argv; + args->prog = argv[0]; + + for (i=1; i<argc; i++) { + if (argv[i][0] == '-') { + switch ((argv[i])[1]) { + case 'o': + if (args->outfile) { + fprintf(stderr, "%s: an output file is already specified. (%s)\n", + args->prog, outfile); + return -1; + } + if (argv[i][2] == '\0' && argv[i+1]) { + i++; + args->outfile = get_outfilename(mrb, argv[i], ""); + } + else { + args->outfile = get_outfilename(mrb, argv[i] + 2, ""); + } + break; + case 'B': + if (argv[i][2] == '\0' && argv[i+1]) { + i++; + args->initname = argv[i]; + } + else { + args->initname = argv[i]+2; + } + if (*args->initname == '\0') { + fprintf(stderr, "%s: function name is not specified.\n", args->prog); + return -1; + } + break; + case 'c': + args->check_syntax = TRUE; + break; + case 'v': + if (!args->verbose) mrb_show_version(mrb); + args->verbose = TRUE; + break; + case 'g': + args->flags |= DUMP_DEBUG_INFO; + break; + case 'E': + args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK); + break; + case 'e': + args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK); + break; + case 'h': + return -1; + case '-': + if (argv[i][1] == '\n') { + return i; + } + if (strcmp(argv[i] + 2, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp(argv[i] + 2, "verbose") == 0) { + args->verbose = TRUE; + break; + } + else if (strcmp(argv[i] + 2, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + return -1; + default: + return i; + } + } + else { + break; + } + } + if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) { + fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n", + args->prog, bigendian_p() ? "big" : "little"); + } + return i; +} + +static void +cleanup(mrb_state *mrb, struct mrbc_args *args) +{ + mrb_free(mrb, (void*)args->outfile); + mrb_close(mrb); +} + +static int +partial_hook(struct mrb_parser_state *p) +{ + mrbc_context *c = p->cxt; + struct mrbc_args *args = (struct mrbc_args *)c->partial_data; + const char *fn; + + if (p->f) fclose(p->f); + if (args->idx >= args->argc) { + p->f = NULL; + return -1; + } + fn = args->argv[args->idx++]; + p->f = fopen(fn, "r"); + if (p->f == NULL) { + fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn); + return -1; + } + mrb_parser_set_filename(p, fn); + return 0; +} + +static mrb_value +load_file(mrb_state *mrb, struct mrbc_args *args) +{ + mrbc_context *c; + mrb_value result; + char *input = args->argv[args->idx]; + FILE *infile; + mrb_bool need_close = FALSE; + + c = mrbc_context_new(mrb); + if (args->verbose) + c->dump_result = TRUE; + c->no_exec = TRUE; + if (input[0] == '-' && input[1] == '\0') { + infile = stdin; + } + else { + need_close = TRUE; + if ((infile = fopen(input, "r")) == NULL) { + fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input); + return mrb_nil_value(); + } + } + mrbc_filename(mrb, c, input); + args->idx++; + if (args->idx < args->argc) { + need_close = FALSE; + mrbc_partial_hook(mrb, c, partial_hook, (void*)args); + } + + result = mrb_load_file_cxt(mrb, infile, c); + if (need_close) fclose(infile); + mrbc_context_free(mrb, c); + if (mrb_undef_p(result)) { + return mrb_nil_value(); + } + return result; +} + +static int +dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args) +{ + int n = MRB_DUMP_OK; + mrb_irep *irep = proc->body.irep; + + if (args->initname) { + n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname); + if (n == MRB_DUMP_INVALID_ARGUMENT) { + fprintf(stderr, "%s: invalid C language symbol name\n", args->initname); + } + } + else { + n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp); + } + if (n != MRB_DUMP_OK) { + fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n); + } + return n; +} + +int +main(int argc, char **argv) +{ + mrb_state *mrb = mrb_open(); + int n, result; + struct mrbc_args args; + FILE *wfp; + mrb_value load; + + if (mrb == NULL) { + fputs("Invalid mrb_state, exiting mrbc\n", stderr); + return EXIT_FAILURE; + } + + n = parse_args(mrb, argc, argv, &args); + if (n < 0) { + cleanup(mrb, &args); + usage(argv[0]); + return EXIT_FAILURE; + } + if (n == argc) { + fprintf(stderr, "%s: no program file given\n", args.prog); + return EXIT_FAILURE; + } + if (args.outfile == NULL && !args.check_syntax) { + if (n + 1 == argc) { + args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); + } + else { + fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog); + return EXIT_FAILURE; + } + } + + args.idx = n; + load = load_file(mrb, &args); + if (mrb_nil_p(load)) { + cleanup(mrb, &args); + return EXIT_FAILURE; + } + if (args.check_syntax) { + printf("%s:%s:Syntax OK\n", args.prog, argv[n]); + } + + if (args.check_syntax) { + cleanup(mrb, &args); + return EXIT_SUCCESS; + } + + if (args.outfile) { + if (strcmp("-", args.outfile) == 0) { + wfp = stdout; + } + else if ((wfp = fopen(args.outfile, "wb")) == NULL) { + fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile); + return EXIT_FAILURE; + } + } + else { + fprintf(stderr, "Output file is required\n"); + return EXIT_FAILURE; + } + result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args); + fclose(wfp); + cleanup(mrb, &args); + if (result != MRB_DUMP_OK) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +void +mrb_init_mrblib(mrb_state *mrb) +{ +} + +#ifndef DISABLE_GEMS +void +mrb_init_mrbgems(mrb_state *mrb) +{ +} + +void +mrb_final_mrbgems(mrb_state *mrb) +{ +} +#endif diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake new file mode 100644 index 00000000..66d6ef80 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake @@ -0,0 +1,30 @@ +module MRuby + class Build + def exefile(name) + if name.is_a?(Array) + name.flatten.map { |n| exefile(n) } + elsif name !~ /\./ + "#{name}#{exts.executable}" + else + name + end + end + end +end + +MRuby.each_target do + next if kind_of? MRuby::CrossBuild + + mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '') + mruby_config_path = "#{build_dir}/bin/#{mruby_config}" + @bins << mruby_config + + file mruby_config_path => libfile("#{build_dir}/lib/libmruby") do |t| + FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name + config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}] + IO.write(t.name, File.open(t.name) {|f| + f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"} + }) + FileUtils.chmod(0755, t.name) + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config new file mode 100644 index 00000000..57346c03 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config @@ -0,0 +1,20 @@ +#!/bin/sh + +while [ $# -gt 0 ]; do + case $1 in + --cflags) echo MRUBY_CFLAGS;; + --ldflags) echo MRUBY_LDFLAGS;; + --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;; + --libs) echo MRUBY_LIBS;; + --libmruby-path) echo MRUBY_LIBMRUBY_PATH;; + --help) echo "Usage: mruby-config [switches]" + echo " switches:" + echo " --cflags print flags passed to compiler" + echo " --ldflags print flags passed to linker" + echo " --ldflags-before-libs print flags passed to linker before linked libraries" + echo " --libs print linked libraries" + echo " --libmruby-path print libmruby path" + exit 0;; + esac + shift +done diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat new file mode 100644 index 00000000..a1f7bfdd --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat @@ -0,0 +1,42 @@ +@echo off + +:top +shift +if "%0" equ "" goto :eof +if "%0" equ "--cflags" goto cflags +if "%0" equ "--ldflags" goto ldflags +if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs +if "%0" equ "--libs" goto libs +if "%0" equ "--libmruby-path" goto libmrubypath +if "%0" equ "--help" goto showhelp +echo Invalid Option +goto :eof + +:cflags +echo MRUBY_CFLAGS +goto top + +:libs +echo MRUBY_LIBS +goto top + +:ldflags +echo MRUBY_LDFLAGS +goto top + +:ldflagsbeforelibs +echo MRUBY_LDFLAGS_BEFORE_LIBS +goto top + +:libmrubypath +echo MRUBY_LIBMRUBY_PATH +goto top + +:showhelp +echo Usage: mruby-config [switches] +echo switches: +echo --cflags print flags passed to compiler +echo --ldflags print flags passed to linker +echo --ldflags-before-libs print flags passed to linker before linked libraries +echo --libs print linked libraries +echo --libmruby-path print libmruby path diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb new file mode 100644 index 00000000..b6b09018 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -0,0 +1,60 @@ +require 'tempfile' + +assert('regression for #1564') do + o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1` + assert_include o, "-e:1:2: syntax error" + o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1` + assert_include o, "-e:1:3: syntax error" +end + +assert('regression for #1572') do + script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') + File.write script.path, 'p "ok"' + system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}" + o = `#{cmd('mruby')} -b #{bin.path}`.strip + assert_equal o, '"ok"' +end + +assert '$0 value' do + script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') + + # .rb script + script.write "p $0\n" + script.flush + assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp + + # .mrb file + `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"` + assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp + + # one liner + assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp +end + +assert '__END__', '8.6' do + script = Tempfile.new('test.rb') + + script.write <<EOS +p 'test' + __END__ = 'fin' +p __END__ +__END__ +p 'legend' +EOS + script.flush + assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}` +end + +assert('garbage collecting built-in classes') do + script = Tempfile.new('test.rb') + + script.write <<RUBY +NilClass = nil +GC.start +Array.dup +print nil.class.to_s +RUBY + script.flush + assert_equal "NilClass", `#{cmd('mruby')} #{script.path}` + assert_equal 0, $?.exitstatus +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake new file mode 100644 index 00000000..fbec1384 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake @@ -0,0 +1,12 @@ +MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby command' + spec.bins = %w(mruby) + spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') + spec.add_dependency('mruby-error', :core => 'mruby-error') + + if build.cxx_exception_enabled? + build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx") + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c new file mode 100644 index 00000000..61d4cde9 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -0,0 +1,254 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/compile.h> +#include <mruby/dump.h> +#include <mruby/variable.h> + +#ifdef MRB_DISABLE_STDIO +static void +p(mrb_state *mrb, mrb_value obj) +{ + mrb_value val = mrb_inspect(mrb, obj); + + fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); + putc('\n', stdout); +} +#else +#define p(mrb,obj) mrb_p(mrb,obj) +#endif + +struct _args { + FILE *rfp; + char* cmdline; + mrb_bool fname : 1; + mrb_bool mrbfile : 1; + mrb_bool check_syntax : 1; + mrb_bool verbose : 1; + int argc; + char** argv; +}; + +static void +usage(const char *name) +{ + static const char *const usage_msg[] = { + "switches:", + "-b load and execute RiteBinary (mrb) file", + "-c check syntax only", + "-e 'command' one line of script", + "-v print version number, then run in verbose mode", + "--verbose run in verbose mode", + "--version print the version", + "--copyright print the copyright", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches] programfile\n", name); + while (*p) + printf(" %s\n", *p++); +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) +{ + char **origargv = argv; + static const struct _args args_zero = { 0 }; + + *args = args_zero; + + for (argc--,argv++; argc > 0; argc--,argv++) { + char *item; + if (argv[0][0] != '-') break; + + if (strlen(*argv) <= 1) { + argc--; argv++; + args->rfp = stdin; + break; + } + + item = argv[0] + 1; + switch (*item++) { + case 'b': + args->mrbfile = TRUE; + break; + case 'c': + args->check_syntax = TRUE; + break; + case 'e': + if (item[0]) { + goto append_cmdline; + } + else if (argc > 1) { + argc--; argv++; + item = argv[0]; +append_cmdline: + if (!args->cmdline) { + size_t buflen; + char *buf; + + buflen = strlen(item) + 1; + buf = (char *)mrb_malloc(mrb, buflen); + memcpy(buf, item, buflen); + args->cmdline = buf; + } + else { + size_t cmdlinelen; + size_t itemlen; + + cmdlinelen = strlen(args->cmdline); + itemlen = strlen(item); + args->cmdline = + (char *)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2); + args->cmdline[cmdlinelen] = '\n'; + memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1); + } + } + else { + printf("%s: No code specified for -e\n", *origargv); + return EXIT_SUCCESS; + } + break; + case 'v': + if (!args->verbose) mrb_show_version(mrb); + args->verbose = TRUE; + break; + case '-': + if (strcmp((*argv) + 2, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp((*argv) + 2, "verbose") == 0) { + args->verbose = TRUE; + break; + } + else if (strcmp((*argv) + 2, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + default: + return EXIT_FAILURE; + } + } + + if (args->rfp == NULL && args->cmdline == NULL) { + if (*argv == NULL) args->rfp = stdin; + else { + args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); + if (args->rfp == NULL) { + printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); + return EXIT_FAILURE; + } + args->fname = TRUE; + args->cmdline = argv[0]; + argc--; argv++; + } + } + args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); + memcpy(args->argv, argv, (argc+1) * sizeof(char*)); + args->argc = argc; + + return EXIT_SUCCESS; +} + +static void +cleanup(mrb_state *mrb, struct _args *args) +{ + if (args->rfp && args->rfp != stdin) + fclose(args->rfp); + if (!args->fname) + mrb_free(mrb, args->cmdline); + mrb_free(mrb, args->argv); + mrb_close(mrb); +} + +int +main(int argc, char **argv) +{ + mrb_state *mrb = mrb_open(); + int n = -1; + int i; + struct _args args; + mrb_value ARGV; + mrbc_context *c; + mrb_value v; + mrb_sym zero_sym; + + if (mrb == NULL) { + fputs("Invalid mrb_state, exiting mruby\n", stderr); + return EXIT_FAILURE; + } + + n = parse_args(mrb, argc, argv, &args); + if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) { + cleanup(mrb, &args); + usage(argv[0]); + return n; + } + else { + int ai = mrb_gc_arena_save(mrb); + ARGV = mrb_ary_new_capa(mrb, args.argc); + for (i = 0; i < args.argc; i++) { + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } + } + mrb_define_global_const(mrb, "ARGV", ARGV); + + c = mrbc_context_new(mrb); + if (args.verbose) + c->dump_result = TRUE; + if (args.check_syntax) + c->no_exec = TRUE; + + /* Set $0 */ + zero_sym = mrb_intern_lit(mrb, "$0"); + if (args.rfp) { + const char *cmdline; + cmdline = args.cmdline ? args.cmdline : "-"; + mrbc_filename(mrb, c, cmdline); + mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); + } + else { + mrbc_filename(mrb, c, "-e"); + mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e")); + } + + /* Load program */ + if (args.mrbfile) { + v = mrb_load_irep_file_cxt(mrb, args.rfp, c); + } + else if (args.rfp) { + v = mrb_load_file_cxt(mrb, args.rfp, c); + } + else { + char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); + if (!utf8) abort(); + v = mrb_load_string_cxt(mrb, utf8, c); + mrb_utf8_free(utf8); + } + + mrb_gc_arena_restore(mrb, ai); + mrbc_context_free(mrb, c); + if (mrb->exc) { + if (mrb_undef_p(v)) { + mrb_p(mrb, mrb_obj_value(mrb->exc)); + } + else { + mrb_print_error(mrb); + } + n = -1; + } + else if (args.check_syntax) { + printf("Syntax OK\n"); + } + } + cleanup(mrb, &args); + + return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb new file mode 100644 index 00000000..bb664a2b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb @@ -0,0 +1,73 @@ +require 'tempfile' + +assert('no files') do + o = `#{cmd('mruby-strip')} 2>&1` + assert_equal 1, $?.exitstatus + assert_equal "no files to strip", o.split("\n")[0] +end + +assert('file not found') do + o = `#{cmd('mruby-strip')} not_found.mrb 2>&1` + assert_equal 1, $?.exitstatus + assert_equal "can't open file for reading not_found.mrb\n", o +end + +assert('not irep file') do + t = Tempfile.new('script.rb') + t.write 'p test\n' + t.flush + o = `#{cmd('mruby-strip')} #{t.path} 2>&1` + assert_equal 1, $?.exitstatus + assert_equal "can't read irep file #{t.path}\n", o +end + +assert('success') do + script_file, compiled1, compiled2 = + Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') + script_file.write "p 'test'\n" + script_file.flush + `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}` + `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}` + + o = `#{cmd('mruby-strip')} #{compiled1.path}` + assert_equal 0, $?.exitstatus + assert_equal "", o + assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}` + + o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}` + assert_equal 0, $?.exitstatus + assert_equal "", o +end + +assert('check debug section') do + script_file, with_debug, without_debug = + Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') + script_file.write "p 'test'\n" + script_file.flush + `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}` + `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}` + + assert_true with_debug.size >= without_debug.size + + `#{cmd('mruby-strip')} #{with_debug.path}` + assert_equal without_debug.size, with_debug.size +end + +assert('check lv section') do + script_file, with_lv, without_lv = + Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') + script_file.write <<EOS +a, b = 0, 1 +a += b +p Kernel.local_variables +EOS + script_file.flush + `#{cmd('mrbc')} -o #{with_lv.path} #{script_file.path}` + `#{cmd('mrbc')} -o #{without_lv.path} #{script_file.path}` + + `#{cmd('mruby-strip')} -l #{without_lv.path}` + assert_true without_lv.size < with_lv.size + + assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp + assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake new file mode 100644 index 00000000..2abd25ee --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake @@ -0,0 +1,6 @@ +MRuby::Gem::Specification.new('mruby-bin-strip') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'irep dump debug section remover command' + spec.bins = %w(mruby-strip) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c new file mode 100644 index 00000000..deb66d54 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c @@ -0,0 +1,155 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/dump.h> + +struct strip_args { + int argc_start; + int argc; + char **argv; + mrb_bool lvar; +}; + + +static void +irep_remove_lv(mrb_state *mrb, mrb_irep *irep) +{ + int i; + + if (irep->lv) { + mrb_free(mrb, irep->lv); + irep->lv = NULL; + } + + for (i = 0; i < irep->rlen; ++i) { + irep_remove_lv(mrb, irep->reps[i]); + } +} + +static void +print_usage(const char *f) +{ + printf("Usage: %s [switches] irepfiles\n", f); + printf("switches:\n"); + printf(" -l, --lvar remove LVAR section too.\n"); +} + +static int +parse_args(int argc, char **argv, struct strip_args *args) +{ + int i; + + args->argc_start = 0; + args->argc = argc; + args->argv = argv; + args->lvar = FALSE; + + for (i = 1; i < argc; ++i) { + const size_t len = strlen(argv[i]); + if (len >= 2 && argv[i][0] == '-') { + switch (argv[i][1]) { + case 'l': + args->lvar = TRUE; + break; + case '-': + if (strncmp((*argv) + 2, "lvar", len) == 0) { + args->lvar = TRUE; + break; + } + default: + return -1; + } + } + else { + break; + } + } + + args->argc_start = i; + return i; +} + +static int +strip(mrb_state *mrb, struct strip_args *args) +{ + int i; + + for (i = args->argc_start; i < args->argc; ++i) { + char *filename; + FILE *rfile; + mrb_irep *irep; + FILE *wfile; + int dump_result; + + filename = args->argv[i]; + rfile = fopen(filename, "rb"); + if (rfile == NULL) { + fprintf(stderr, "can't open file for reading %s\n", filename); + return EXIT_FAILURE; + } + + irep = mrb_read_irep_file(mrb, rfile); + fclose(rfile); + if (irep == NULL) { + fprintf(stderr, "can't read irep file %s\n", filename); + return EXIT_FAILURE; + } + + /* clear lv if --lvar is enabled */ + if (args->lvar) { + irep_remove_lv(mrb, irep); + } + + wfile = fopen(filename, "wb"); + if (wfile == NULL) { + fprintf(stderr, "can't open file for writing %s\n", filename); + mrb_irep_decref(mrb, irep); + return EXIT_FAILURE; + } + + /* debug flag must always be false */ + dump_result = mrb_dump_irep_binary(mrb, irep, FALSE, wfile); + + fclose(wfile); + mrb_irep_decref(mrb, irep); + + if (dump_result != MRB_DUMP_OK) { + fprintf(stderr, "error occurred during dumping %s\n", filename); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +int +main(int argc, char **argv) +{ + struct strip_args args; + int args_result; + mrb_state *mrb; + int ret; + + if (argc <= 1) { + printf("no files to strip\n"); + print_usage(argv[0]); + return EXIT_FAILURE; + } + + args_result = parse_args(argc, argv, &args); + if (args_result < 0) { + print_usage(argv[0]); + return EXIT_FAILURE; + } + mrb = mrb_open_core(mrb_default_allocf, NULL); + if (mrb == NULL) { + fputs("Invalid mrb_state, exiting mruby-strip\n", stderr); + return EXIT_FAILURE; + } + + ret = strip(mrb, &args); + + mrb_close(mrb); + return ret; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake new file mode 100644 index 00000000..a384b1ee --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-class-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'class/module extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c new file mode 100644 index 00000000..5506c482 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c @@ -0,0 +1,30 @@ +#include "mruby.h" +#include "mruby/class.h" +#include "mruby/string.h" + +static mrb_value +mrb_mod_name(mrb_state *mrb, mrb_value self) +{ + mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self)); + return mrb_nil_p(name)? name : mrb_str_dup(mrb, name); +} + +static mrb_value +mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self) +{ + return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS); +} + +void +mrb_mruby_class_ext_gem_init(mrb_state *mrb) +{ + struct RClass *mod = mrb->module_class; + + mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE()); + mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE()); +} + +void +mrb_mruby_class_ext_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb new file mode 100644 index 00000000..65abde10 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb @@ -0,0 +1,34 @@ +assert 'Module#name' do + module Outer + class Inner; end + const_set :SetInner, Class.new + end + + assert_equal 'Outer', Outer.name + assert_equal 'Outer::Inner', Outer::Inner.name + assert_equal 'Outer::SetInner', Outer::SetInner.name + + outer = Module.new do + const_set :SetInner, Class.new + end + Object.const_set :SetOuter, outer + + assert_equal 'SetOuter', SetOuter.name + assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name + + mod = Module.new + cls = Class.new + + assert_nil mod.name + assert_nil cls.name +end + +assert 'Module#singleton_class?' do + mod = Module.new + cls = Class.new + scl = cls.singleton_class + + assert_false mod.singleton_class? + assert_false cls.singleton_class? + assert_true scl.singleton_class? +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb new file mode 100644 index 00000000..f4d9208b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb @@ -0,0 +1,30 @@ +require 'tempfile' + +assert('Compiling multiple files without new line in last line. #2361') do + a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') + a.write('module A; end') + a.flush + b.write('module B; end') + b.flush + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1` + assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end + +assert('parsing function with void argument') do + a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') + a.write('f ()') + a.flush + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` + assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end + +assert('embedded document with invalid terminator') do + a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') + a.write("=begin\n=endx\n") + a.flush + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` + assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp + assert_equal 1, $?.exitstatus +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c new file mode 100644 index 00000000..8f15a9b1 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c @@ -0,0 +1,3026 @@ +/* +** codegen.c - mruby code generator +** +** See Copyright Notice in mruby.h +*/ + +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <mruby.h> +#include <mruby/compile.h> +#include <mruby/proc.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include <mruby/debug.h> +#include "node.h" +#include <mruby/opcode.h> +#include <mruby/re.h> +#include <mruby/throw.h> + +#ifndef MRB_CODEGEN_LEVEL_MAX +#define MRB_CODEGEN_LEVEL_MAX 1024 +#endif + +typedef mrb_ast_node node; +typedef struct mrb_parser_state parser_state; + +enum looptype { + LOOP_NORMAL, + LOOP_BLOCK, + LOOP_FOR, + LOOP_BEGIN, + LOOP_RESCUE, +}; + +struct loopinfo { + enum looptype type; + int pc1, pc2, pc3, acc; + int ensure_level; + struct loopinfo *prev; +}; + +typedef struct scope { + mrb_state *mrb; + mrb_pool *mpool; + struct mrb_jmpbuf jmp; + + struct scope *prev; + + node *lv; + + int sp; + int pc; + int lastlabel; + int ainfo:15; + mrb_bool mscope:1; + + struct loopinfo *loop; + int ensure_level; + char const *filename; + uint16_t lineno; + + mrb_code *iseq; + uint16_t *lines; + int icapa; + + mrb_irep *irep; + int pcapa, scapa, rcapa; + + uint16_t nlocals; + uint16_t nregs; + int ai; + + int debug_start_pos; + uint16_t filename_index; + parser_state* parser; + + int rlev; /* recursion levels */ +} codegen_scope; + +static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); +static void scope_finish(codegen_scope *s); +static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); +static void loop_break(codegen_scope *s, node *tree); +static void loop_pop(codegen_scope *s, int val); + +static void gen_assignment(codegen_scope *s, node *tree, int sp, int val); +static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val); + +static void codegen(codegen_scope *s, node *tree, int val); +static void raise_error(codegen_scope *s, const char *msg); + +static void +codegen_error(codegen_scope *s, const char *message) +{ + if (!s) return; + while (s->prev) { + codegen_scope *tmp = s->prev; + mrb_free(s->mrb, s->iseq); + mrb_pool_close(s->mpool); + s = tmp; + } +#ifndef MRB_DISABLE_STDIO + if (s->filename && s->lineno) { + fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message); + } + else { + fprintf(stderr, "codegen error: %s\n", message); + } +#endif + MRB_THROW(&s->jmp); +} + +static void* +codegen_palloc(codegen_scope *s, size_t len) +{ + void *p = mrb_pool_alloc(s->mpool, len); + + if (!p) codegen_error(s, "pool memory allocation"); + return p; +} + +static void* +codegen_malloc(codegen_scope *s, size_t len) +{ + void *p = mrb_malloc_simple(s->mrb, len); + + if (!p) codegen_error(s, "mrb_malloc"); + return p; +} + +static void* +codegen_realloc(codegen_scope *s, void *p, size_t len) +{ + p = mrb_realloc_simple(s->mrb, p, len); + + if (!p && len > 0) codegen_error(s, "mrb_realloc"); + return p; +} + +static int +new_label(codegen_scope *s) +{ + s->lastlabel = s->pc; + return s->pc; +} + +static inline int +genop(codegen_scope *s, mrb_code i) +{ + if (s->pc == s->icapa) { + s->icapa *= 2; + s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa); + if (s->lines) { + s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa); + s->irep->lines = s->lines; + } + } + s->iseq[s->pc] = i; + if (s->lines) { + s->lines[s->pc] = s->lineno; + } + return s->pc++; +} + +#define NOVAL 0 +#define VAL 1 + +static mrb_bool +no_optimize(codegen_scope *s) +{ + if (s && s->parser && s->parser->no_optimize) + return TRUE; + return FALSE; +} + +static int +genop_peep(codegen_scope *s, mrb_code i, int val) +{ + /* peephole optimization */ + if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) { + mrb_code i0 = s->iseq[s->pc-1]; + int c1 = GET_OPCODE(i); + int c0 = GET_OPCODE(i0); + + switch (c1) { + case OP_MOVE: + if (GETARG_A(i) == GETARG_B(i)) { + /* skip useless OP_MOVE */ + return 0; + } + if (val) break; + switch (c0) { + case OP_MOVE: + if (GETARG_A(i) == GETARG_A(i0)) { + /* skip overriden OP_MOVE */ + s->pc--; + s->iseq[s->pc] = i; + } + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) { + /* skip swapping OP_MOVE */ + return 0; + } + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->pc--; + return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val); + } + break; + case OP_LOADI: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0)); + return 0; + } + break; + case OP_ARRAY: + case OP_HASH: + case OP_RANGE: + case OP_AREF: + case OP_GETUPVAR: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0)); + return 0; + } + break; + case OP_LOADSYM: + case OP_GETGLOBAL: + case OP_GETIV: + case OP_GETCV: + case OP_GETCONST: + case OP_GETSPECIAL: + case OP_LOADL: + case OP_STRING: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0)); + return 0; + } + break; + case OP_SCLASS: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0)); + return 0; + } + break; + case OP_LOADNIL: + case OP_LOADSELF: + case OP_LOADT: + case OP_LOADF: + case OP_OCLASS: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i)); + return 0; + } + break; + default: + break; + } + break; + case OP_SETIV: + case OP_SETCV: + case OP_SETCONST: + case OP_SETMCNST: + case OP_SETGLOBAL: + if (val) break; + if (c0 == OP_MOVE) { + if (GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i)); + return 0; + } + } + break; + case OP_SETUPVAR: + if (val) break; + if (c0 == OP_MOVE) { + if (GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i)); + return 0; + } + } + break; + case OP_EPOP: + if (c0 == OP_EPOP) { + s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); + return 0; + } + break; + case OP_POPERR: + if (c0 == OP_POPERR) { + s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); + return 0; + } + break; + case OP_RETURN: + switch (c0) { + case OP_RETURN: + return 0; + case OP_MOVE: + if (GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); + return 0; + } + break; + case OP_SETIV: + case OP_SETCV: + case OP_SETCONST: + case OP_SETMCNST: + case OP_SETUPVAR: + case OP_SETGLOBAL: + s->pc--; + genop_peep(s, i0, NOVAL); + i0 = s->iseq[s->pc-1]; + return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); +#if 0 + case OP_SEND: + if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0)); + return; + } + break; +#endif + default: + break; + } + break; + case OP_ADD: + case OP_SUB: + if (c0 == OP_LOADI) { + int c = GETARG_sBx(i0); + + if (c1 == OP_SUB) c = -c; + if (c > 127 || c < -127) break; + if (0 <= c) + s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c); + else + s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c); + return 0; + } + case OP_STRCAT: + if (c0 == OP_STRING) { + mrb_value v = s->irep->pool[GETARG_Bx(i0)]; + + if (mrb_string_p(v) && RSTRING_LEN(v) == 0) { + s->pc--; + return 0; + } + } + if (c0 == OP_LOADNIL) { + if (GETARG_B(i) == GETARG_A(i0)) { + s->pc--; + return 0; + } + } + break; + case OP_JMPIF: + case OP_JMPNOT: + if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i)); + return s->pc-1; + } + break; + default: + break; + } + } + return genop(s, i); +} + +static void +scope_error(codegen_scope *s) +{ + exit(EXIT_FAILURE); +} + +static inline void +dispatch(codegen_scope *s, int pc) +{ + int diff = s->pc - pc; + mrb_code i = s->iseq[pc]; + int c = GET_OPCODE(i); + + s->lastlabel = s->pc; + switch (c) { + case OP_JMP: + case OP_JMPIF: + case OP_JMPNOT: + case OP_ONERR: + break; + default: +#ifndef MRB_DISABLE_STDIO + fprintf(stderr, "bug: dispatch on non JMP op\n"); +#endif + scope_error(s); + break; + } + if (diff > MAXARG_sBx) { + codegen_error(s, "too distant jump address"); + } + s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); +} + +static void +dispatch_linked(codegen_scope *s, int pc) +{ + mrb_code i; + int pos; + + if (!pc) return; + for (;;) { + i = s->iseq[pc]; + pos = GETARG_sBx(i); + dispatch(s, pc); + if (!pos) break; + pc = pos; + } +} + +#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) +static void +push_(codegen_scope *s) +{ + if (s->sp > 511) { + codegen_error(s, "too complex expression"); + } + s->sp++; + nregs_update; +} + +static void +push_n_(codegen_scope *s, int n) +{ + if (s->sp+n > 511) { + codegen_error(s, "too complex expression"); + } + s->sp+=n; + nregs_update; +} + +#define push() push_(s) +#define push_n(n) push_n_(s,n) +#define pop_(s) ((s)->sp--) +#define pop() pop_(s) +#define pop_n(n) (s->sp-=(n)) +#define cursp() (s->sp) + +static inline int +new_lit(codegen_scope *s, mrb_value val) +{ + int i; + mrb_value *pv; + + switch (mrb_type(val)) { + case MRB_TT_STRING: + for (i=0; i<s->irep->plen; i++) { + mrb_int len; + pv = &s->irep->pool[i]; + + if (mrb_type(*pv) != MRB_TT_STRING) continue; + if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue; + if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0) + return i; + } + break; + case MRB_TT_FLOAT: + for (i=0; i<s->irep->plen; i++) { + pv = &s->irep->pool[i]; + if (mrb_type(*pv) != MRB_TT_FLOAT) continue; + if (mrb_float(*pv) == mrb_float(val)) return i; + } + break; + case MRB_TT_FIXNUM: + for (i=0; i<s->irep->plen; i++) { + pv = &s->irep->pool[i]; + if (!mrb_fixnum_p(*pv)) continue; + if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i; + } + break; + default: + /* should not happen */ + return 0; + } + + if (s->irep->plen == s->pcapa) { + s->pcapa *= 2; + s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa); + } + + pv = &s->irep->pool[s->irep->plen]; + i = s->irep->plen++; + + switch (mrb_type(val)) { + case MRB_TT_STRING: + *pv = mrb_str_pool(s->mrb, val); + break; + + case MRB_TT_FLOAT: +#ifdef MRB_WORD_BOXING + *pv = mrb_float_pool(s->mrb, mrb_float(val)); + break; +#endif + case MRB_TT_FIXNUM: + *pv = val; + break; + + default: + /* should not happen */ + break; + } + return i; +} + +/* method symbols should be fit in 9 bits */ +#define MAXMSYMLEN 512 +/* maximum symbol numbers */ +#define MAXSYMLEN 65536 + +static int +new_msym(codegen_scope *s, mrb_sym sym) +{ + int i, len; + + mrb_assert(s->irep); + + len = s->irep->slen; + if (len > MAXMSYMLEN) len = MAXMSYMLEN; + for (i=0; i<len; i++) { + if (s->irep->syms[i] == sym) return i; + if (s->irep->syms[i] == 0) break; + } + if (i == MAXMSYMLEN) { + codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")"); + } + s->irep->syms[i] = sym; + if (i == s->irep->slen) s->irep->slen++; + return i; +} + +static int +new_sym(codegen_scope *s, mrb_sym sym) +{ + int i; + + for (i=0; i<s->irep->slen; i++) { + if (s->irep->syms[i] == sym) return i; + } + if (s->irep->slen == MAXSYMLEN) { + codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")"); + } + + if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) { + s->scapa = MAXSYMLEN; + s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN); + for (i = s->irep->slen; i < MAXMSYMLEN; i++) { + static const mrb_sym mrb_sym_zero = { 0 }; + s->irep->syms[i] = mrb_sym_zero; + } + s->irep->slen = MAXMSYMLEN; + } + s->irep->syms[s->irep->slen] = sym; + return s->irep->slen++; +} + +static int +node_len(node *tree) +{ + int n = 0; + + while (tree) { + n++; + tree = tree->cdr; + } + return n; +} + +#define nsym(x) ((mrb_sym)(intptr_t)(x)) +#define lv_name(lv) nsym((lv)->car) +static int +lv_idx(codegen_scope *s, mrb_sym id) +{ + node *lv = s->lv; + int n = 1; + + while (lv) { + if (lv_name(lv) == id) return n; + n++; + lv = lv->cdr; + } + return 0; +} + +static void +for_body(codegen_scope *s, node *tree) +{ + codegen_scope *prev = s; + int idx; + struct loopinfo *lp; + node *n2; + mrb_code c; + + /* generate receiver */ + codegen(s, tree->cdr->car, VAL); + /* generate loop-block */ + s = scope_new(s->mrb, s, NULL); + if (s == NULL) { + raise_error(prev, "unexpected scope"); + } + + push(); /* push for a block parameter */ + + /* generate loop variable */ + n2 = tree->car; + genop(s, MKOP_Ax(OP_ENTER, 0x40000)); + if (n2->car && !n2->car->cdr && !n2->cdr) { + gen_assignment(s, n2->car->car, 1, NOVAL); + } + else { + gen_vmassignment(s, n2, 1, VAL); + } + /* construct loop */ + lp = loop_push(s, LOOP_FOR); + lp->pc2 = new_label(s); + + /* loop body */ + codegen(s, tree->cdr->cdr->car, VAL); + pop(); + if (s->pc > 0) { + c = s->iseq[s->pc-1]; + if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + loop_pop(s, NOVAL); + scope_finish(s); + s = prev; + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK)); + push();pop(); /* space for a block */ + pop(); + idx = new_msym(s, mrb_intern_lit(s->mrb, "each")); + genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0)); +} + +static int +lambda_body(codegen_scope *s, node *tree, int blk) +{ + mrb_code c; + codegen_scope *parent = s; + s = scope_new(s->mrb, s, tree->car); + if (s == NULL) { + raise_error(parent, "unexpected scope"); + } + + s->mscope = !blk; + + if (blk) { + struct loopinfo *lp = loop_push(s, LOOP_BLOCK); + lp->pc1 = new_label(s); + } + tree = tree->cdr; + if (tree->car) { + mrb_aspec a; + int ma, oa, ra, pa, ka, kd, ba; + int pos, i; + node *n, *opt; + + ma = node_len(tree->car->car); + n = tree->car->car; + while (n) { + n = n->cdr; + } + oa = node_len(tree->car->cdr->car); + ra = tree->car->cdr->cdr->car ? 1 : 0; + pa = node_len(tree->car->cdr->cdr->cdr->car); + ka = kd = 0; + ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; + + if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) { + codegen_error(s, "too many formal arguments"); + } + a = ((mrb_aspec)(ma & 0x1f) << 18) + | ((mrb_aspec)(oa & 0x1f) << 13) + | ((ra & 1) << 12) + | ((pa & 0x1f) << 7) + | ((ka & 0x1f) << 2) + | ((kd & 1)<< 1) + | (ba & 1); + s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */ + | ((ra & 1) << 5) + | (pa & 0x1f); + genop(s, MKOP_Ax(OP_ENTER, a)); + pos = new_label(s); + for (i=0; i<oa; i++) { + new_label(s); + genop(s, MKOP_sBx(OP_JMP, 0)); + } + if (oa > 0) { + genop(s, MKOP_sBx(OP_JMP, 0)); + } + opt = tree->car->cdr->car; + i = 0; + while (opt) { + int idx; + + dispatch(s, pos+i); + codegen(s, opt->car->cdr, VAL); + idx = lv_idx(s, nsym(opt->car->car)); + pop(); + genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL); + i++; + opt = opt->cdr; + } + if (oa > 0) { + dispatch(s, pos+i); + } + } + codegen(s, tree->cdr->car, VAL); + pop(); + if (s->pc > 0) { + c = s->iseq[s->pc-1]; + if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) { + if (s->nregs == 0) { + genop(s, MKOP_A(OP_LOADNIL, 0)); + genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + } + } + if (blk) { + loop_pop(s, NOVAL); + } + scope_finish(s); + return parent->irep->rlen - 1; +} + +static int +scope_body(codegen_scope *s, node *tree, int val) +{ + codegen_scope *scope = scope_new(s->mrb, s, tree->car); + if (scope == NULL) { + raise_error(s, "unexpected scope"); + } + + codegen(scope, tree->cdr, VAL); + if (!s->iseq) { + genop(scope, MKOP_A(OP_STOP, 0)); + } + else if (!val) { + genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + if (scope->nregs == 0) { + genop(scope, MKOP_A(OP_LOADNIL, 0)); + genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL); + } + } + scope_finish(scope); + if (!s->irep) { + /* should not happen */ + return 0; + } + return s->irep->rlen - 1; +} + +#define nint(x) ((int)(intptr_t)(x)) +#define nchar(x) ((char)(intptr_t)(x)) + +static mrb_bool +nosplat(node *t) +{ + while (t) { + if (nint(t->car->car) == NODE_SPLAT) return FALSE; + t = t->cdr; + } + return TRUE; +} + +static mrb_sym +attrsym(codegen_scope *s, mrb_sym a) +{ + const char *name; + mrb_int len; + char *name2; + + name = mrb_sym2name_len(s->mrb, a, &len); + name2 = (char *)codegen_palloc(s, + (size_t)len + + 1 /* '=' */ + + 1 /* '\0' */ + ); + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + memcpy(name2, name, (size_t)len); + name2[len] = '='; + name2[len+1] = '\0'; + + return mrb_intern(s->mrb, name2, len+1); +} + +#define CALL_MAXARGS 127 + +static int +gen_values(codegen_scope *s, node *t, int val, int extra) +{ + int n = 0; + int is_splat; + + while (t) { + is_splat = nint(t->car->car) == NODE_SPLAT; /* splat mode */ + if ( + n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */ + || is_splat) { + if (val) { + if (is_splat && n == 0 && nint(t->car->cdr->car) == NODE_ARRAY) { + codegen(s, t->car->cdr, VAL); + pop(); + } + else { + pop_n(n); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); + push(); + codegen(s, t->car, VAL); + pop(); pop(); + if (is_splat) { + genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + } + else { + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + } + t = t->cdr; + while (t) { + push(); + codegen(s, t->car, VAL); + pop(); pop(); + if (nint(t->car->car) == NODE_SPLAT) { + genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + } + else { + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + t = t->cdr; + } + } + else { + while (t) { + codegen(s, t->car, NOVAL); + t = t->cdr; + } + } + return -1; + } + /* normal (no splat) mode */ + codegen(s, t->car, val); + n++; + t = t->cdr; + } + return n; +} + +static void +gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe) +{ + mrb_sym sym = name ? name : nsym(tree->cdr->car); + int idx, skip = 0; + int n = 0, noop = 0, sendv = 0, blk = 0; + + codegen(s, tree->car, VAL); /* receiver */ + if (safe) { + int recv = cursp()-1; + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + genop(s, MKOP_AB(OP_MOVE, cursp(), recv)); + push_n(2); pop_n(2); /* space for one arg and a block */ + pop(); + idx = new_msym(s, mrb_intern_lit(s->mrb, "==")); + genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1)); + skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); + } + idx = new_msym(s, sym); + tree = tree->cdr->cdr->car; + if (tree) { + n = gen_values(s, tree->car, VAL, sp?1:0); + if (n < 0) { + n = noop = sendv = 1; + push(); + } + } + if (sp) { + if (sendv) { + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp)); + push(); + } + else { + genop(s, MKOP_AB(OP_MOVE, cursp(), sp)); + push(); + n++; + } + } + if (tree && tree->cdr) { + noop = 1; + codegen(s, tree->cdr, VAL); + pop(); + } + else { + blk = cursp(); + } + push();pop(); + pop_n(n+1); + { + mrb_int symlen; + const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen); + + if (!noop && symlen == 1 && symname[0] == '+' && n == 1) { + genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); + } + else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) { + genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); + } + else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) { + genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) { + genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) { + genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) { + genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) { + genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) { + genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) { + genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n)); + } + else { + if (sendv) n = CALL_MAXARGS; + if (blk > 0) { /* no block */ + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n)); + } + else { + genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n)); + } + } + } + if (safe) { + dispatch(s, skip); + } + if (val) { + push(); + } +} + +static void +gen_assignment(codegen_scope *s, node *tree, int sp, int val) +{ + int idx; + int type = nint(tree->car); + + tree = tree->cdr; + switch (type) { + case NODE_GVAR: + idx = new_sym(s, nsym(tree)); + genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); + break; + case NODE_LVAR: + idx = lv_idx(s, nsym(tree)); + if (idx > 0) { + if (idx != sp) { + genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val); + } + break; + } + else { /* upvar */ + int lv = 0; + codegen_scope *up = s->prev; + + while (up) { + idx = lv_idx(up, nsym(tree)); + if (idx > 0) { + genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val); + break; + } + lv++; + up = up->prev; + } + } + break; + case NODE_IVAR: + idx = new_sym(s, nsym(tree)); + genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); + break; + case NODE_CVAR: + idx = new_sym(s, nsym(tree)); + genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); + break; + case NODE_CONST: + idx = new_sym(s, nsym(tree)); + genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); + break; + case NODE_COLON2: + idx = new_sym(s, nsym(tree->cdr)); + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); + push(); + codegen(s, tree->car, VAL); + pop_n(2); + genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); + break; + + case NODE_CALL: + case NODE_SCALL: + push(); + gen_call(s, tree, attrsym(s, nsym(tree->cdr->car)), sp, NOVAL, + type == NODE_SCALL); + pop(); + if (val) { + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); + } + break; + + case NODE_MASGN: + gen_vmassignment(s, tree->car, sp, val); + break; + + /* splat without assignment */ + case NODE_NIL: + break; + + default: +#ifndef MRB_DISABLE_STDIO + fprintf(stderr, "unknown lhs %d\n", type); +#endif + break; + } + if (val) push(); +} + +static void +gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) +{ + int n = 0, post = 0; + node *t, *p; + + if (tree->car) { /* pre */ + t = tree->car; + n = 0; + while (t) { + genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n)); + gen_assignment(s, t->car, cursp(), NOVAL); + n++; + t = t->cdr; + } + } + t = tree->cdr; + if (t) { + if (t->cdr) { /* post count */ + p = t->cdr->car; + while (p) { + post++; + p = p->cdr; + } + } + if (val) { + genop(s, MKOP_AB(OP_MOVE, cursp(), rhs)); + } + else { + pop(); + } + push_n(post); + pop_n(post); + genop(s, MKOP_ABC(OP_APOST, cursp(), n, post)); + n = 1; + if (t->car) { /* rest */ + gen_assignment(s, t->car, cursp(), NOVAL); + } + if (t->cdr && t->cdr->car) { + t = t->cdr->car; + while (t) { + gen_assignment(s, t->car, cursp()+n, NOVAL); + t = t->cdr; + n++; + } + } + if (!val) { + push(); + } + } +} + +static void +gen_send_intern(codegen_scope *s) +{ + push();pop(); /* space for a block */ + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0)); + push(); +} +static void +gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) +{ + if (val) { + int i = 0, j = 0; + + while (tree) { + switch (nint(tree->car->car)) { + case NODE_STR: + if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0)) + break; + /* fall through */ + case NODE_BEGIN: + codegen(s, tree->car, VAL); + ++j; + break; + + case NODE_LITERAL_DELIM: + if (j > 0) { + j = 0; + ++i; + if (sym) + gen_send_intern(s); + } + break; + } + if (j >= 2) { + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + j = 1; + } + tree = tree->cdr; + } + if (j > 0) { + ++i; + if (sym) + gen_send_intern(s); + } + pop_n(i); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i)); + push(); + } + else { + while (tree) { + switch (nint(tree->car->car)) { + case NODE_BEGIN: case NODE_BLOCK: + codegen(s, tree->car, NOVAL); + } + tree = tree->cdr; + } + } +} + +static void +raise_error(codegen_scope *s, const char *msg) +{ + int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg)); + + genop(s, MKOP_ABx(OP_ERR, 1, idx)); +} + +static double +readint_float(codegen_scope *s, const char *p, int base) +{ + const char *e = p + strlen(p); + double f = 0; + int n; + + if (*p == '+') p++; + while (p < e) { + char c = *p; + c = tolower((unsigned char)c); + for (n=0; n<base; n++) { + if (mrb_digitmap[n] == c) { + f *= base; + f += n; + break; + } + } + if (n == base) { + codegen_error(s, "malformed readint input"); + } + p++; + } + return f; +} + +static mrb_int +readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow) +{ + const char *e = p + strlen(p); + mrb_int result = 0; + int n; + + mrb_assert(base >= 2 && base <= 36); + if (*p == '+') p++; + while (p < e) { + char c = *p; + c = tolower((unsigned char)c); + for (n=0; n<base; n++) { + if (mrb_digitmap[n] == c) { + break; + } + } + if (n == base) { + codegen_error(s, "malformed readint input"); + } + + if (neg) { + if ((MRB_INT_MIN + n)/base > result) { + *overflow = TRUE; + return 0; + } + result *= base; + result -= n; + } + else { + if ((MRB_INT_MAX - n)/base < result) { + *overflow = TRUE; + return 0; + } + result *= base; + result += n; + } + p++; + } + *overflow = FALSE; + return result; +} + +static void +gen_retval(codegen_scope *s, node *tree) +{ + if (nint(tree->car) == NODE_SPLAT) { + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0)); + push(); + codegen(s, tree, VAL); + pop(); pop(); + genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + } + else { + codegen(s, tree, VAL); + pop(); + } +} + +static void +codegen(codegen_scope *s, node *tree, int val) +{ + int nt; + int rlev = s->rlev; + + if (!tree) { + if (val) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + return; + } + + s->rlev++; + if (s->rlev > MRB_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } + if (s->irep && s->filename_index != tree->filename_index) { + s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); + mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); + s->debug_start_pos = s->pc; + s->filename_index = tree->filename_index; + s->filename = mrb_parser_get_filename(s->parser, tree->filename_index); + } + + nt = nint(tree->car); + s->lineno = tree->lineno; + tree = tree->cdr; + switch (nt) { + case NODE_BEGIN: + if (val && !tree) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + while (tree) { + codegen(s, tree->car, tree->cdr ? NOVAL : val); + tree = tree->cdr; + } + break; + + case NODE_RESCUE: + { + int onerr, noexc, exend, pos1, pos2, tmp; + struct loopinfo *lp; + + if (tree->car == NULL) goto exit; + onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); + lp = loop_push(s, LOOP_BEGIN); + lp->pc1 = onerr; + codegen(s, tree->car, VAL); + pop(); + lp->type = LOOP_RESCUE; + noexc = genop(s, MKOP_Bx(OP_JMP, 0)); + dispatch(s, onerr); + tree = tree->cdr; + exend = 0; + pos1 = 0; + if (tree->car) { + node *n2 = tree->car; + int exc = cursp(); + + genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0)); + push(); + while (n2) { + node *n3 = n2->car; + node *n4 = n3->car; + + if (pos1) dispatch(s, pos1); + pos2 = 0; + do { + if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) { + codegen(s, n4->car, VAL); + genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); + push_n(2); pop_n(2); /* space for one arg and a block */ + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); + } + else { + if (n4) { + codegen(s, n4->car, VAL); + } + else { + genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError")))); + push(); + } + pop(); + genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1)); + } + tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); + pos2 = tmp; + if (n4) { + n4 = n4->cdr; + } + } while (n4); + pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch_linked(s, pos2); + + pop(); + if (n3->cdr->car) { + gen_assignment(s, n3->cdr->car, exc, NOVAL); + } + if (n3->cdr->cdr->car) { + codegen(s, n3->cdr->cdr->car, val); + if (val) pop(); + } + tmp = genop(s, MKOP_sBx(OP_JMP, exend)); + exend = tmp; + n2 = n2->cdr; + push(); + } + if (pos1) { + dispatch(s, pos1); + genop(s, MKOP_A(OP_RAISE, exc)); + } + } + pop(); + tree = tree->cdr; + dispatch(s, noexc); + genop(s, MKOP_A(OP_POPERR, 1)); + if (tree->car) { + codegen(s, tree->car, val); + } + else if (val) { + push(); + } + dispatch_linked(s, exend); + loop_pop(s, NOVAL); + } + break; + + case NODE_ENSURE: + if (!tree->cdr || !tree->cdr->cdr || + (nint(tree->cdr->cdr->car) == NODE_BEGIN && + tree->cdr->cdr->cdr)) { + int idx; + int epush = s->pc; + + genop(s, MKOP_Bx(OP_EPUSH, 0)); + s->ensure_level++; + codegen(s, tree->car, val); + idx = scope_body(s, tree->cdr, NOVAL); + s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx); + s->ensure_level--; + genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL); + } + else { /* empty ensure ignored */ + codegen(s, tree->car, val); + } + break; + + case NODE_LAMBDA: + if (val) { + int idx = lambda_body(s, tree, 1); + + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); + push(); + } + break; + + case NODE_BLOCK: + if (val) { + int idx = lambda_body(s, tree, 1); + + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); + push(); + } + break; + + case NODE_IF: + { + int pos1, pos2; + node *e = tree->cdr->cdr->car; + + if (!tree->car) { + codegen(s, e, val); + goto exit; + } + switch (nint(tree->car->car)) { + case NODE_TRUE: + case NODE_INT: + case NODE_STR: + codegen(s, tree->cdr->car, val); + goto exit; + case NODE_FALSE: + case NODE_NIL: + codegen(s, e, val); + goto exit; + } + codegen(s, tree->car, VAL); + pop(); + pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); + + codegen(s, tree->cdr->car, val); + if (e) { + if (val) pop(); + pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch(s, pos1); + codegen(s, e, val); + dispatch(s, pos2); + } + else { + if (val) { + pop(); + pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch(s, pos1); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + dispatch(s, pos2); + push(); + } + else { + dispatch(s, pos1); + } + } + } + break; + + case NODE_AND: + { + int pos; + + codegen(s, tree->car, VAL); + pop(); + pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); + codegen(s, tree->cdr, val); + dispatch(s, pos); + } + break; + + case NODE_OR: + { + int pos; + + codegen(s, tree->car, VAL); + pop(); + pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); + codegen(s, tree->cdr, val); + dispatch(s, pos); + } + break; + + case NODE_WHILE: + { + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + + lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); + lp->pc2 = new_label(s); + codegen(s, tree->cdr, NOVAL); + dispatch(s, lp->pc1); + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc)); + + loop_pop(s, val); + } + break; + + case NODE_UNTIL: + { + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + + lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); + lp->pc2 = new_label(s); + codegen(s, tree->cdr, NOVAL); + dispatch(s, lp->pc1); + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc)); + + loop_pop(s, val); + } + break; + + case NODE_FOR: + for_body(s, tree); + if (val) push(); + break; + + case NODE_CASE: + { + int head = 0; + int pos1, pos2, pos3, tmp; + node *n; + + pos3 = 0; + if (tree->car) { + head = cursp(); + codegen(s, tree->car, VAL); + } + tree = tree->cdr; + while (tree) { + n = tree->car->car; + pos1 = pos2 = 0; + while (n) { + codegen(s, n->car, VAL); + if (head) { + genop(s, MKOP_AB(OP_MOVE, cursp(), head)); + push_n(2); pop_n(2); /* space for one arg and a block */ + pop(); + if (nint(n->car->car) == NODE_SPLAT) { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); + } + else { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); + } + } + else { + pop(); + } + tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); + pos2 = tmp; + n = n->cdr; + } + if (tree->car->car) { + pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch_linked(s, pos2); + } + codegen(s, tree->car->cdr, val); + if (val) pop(); + tmp = genop(s, MKOP_sBx(OP_JMP, pos3)); + pos3 = tmp; + if (pos1) dispatch(s, pos1); + tree = tree->cdr; + } + if (val) { + int pos = cursp(); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + if (pos3) dispatch_linked(s, pos3); + if (head) pop(); + if (cursp() != pos) { + genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); + } + push(); + } + else { + if (pos3) { + dispatch_linked(s, pos3); + } + if (head) { + pop(); + } + } + } + break; + + case NODE_SCOPE: + scope_body(s, tree, NOVAL); + break; + + case NODE_FCALL: + case NODE_CALL: + gen_call(s, tree, 0, 0, val, 0); + break; + case NODE_SCALL: + gen_call(s, tree, 0, 0, val, 1); + break; + + case NODE_DOT2: + codegen(s, tree->car, val); + codegen(s, tree->cdr, val); + if (val) { + pop(); pop(); + genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE)); + push(); + } + break; + + case NODE_DOT3: + codegen(s, tree->car, val); + codegen(s, tree->cdr, val); + if (val) { + pop(); pop(); + genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE)); + push(); + } + break; + + case NODE_COLON2: + { + int sym = new_sym(s, nsym(tree->cdr)); + + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + if (val) push(); + } + break; + + case NODE_COLON3: + { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + if (val) push(); + } + break; + + case NODE_ARRAY: + { + int n; + + n = gen_values(s, tree, val, 0); + if (n >= 0) { + if (val) { + pop_n(n); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); + push(); + } + } + else if (val) { + push(); + } + } + break; + + case NODE_HASH: + { + int len = 0; + mrb_bool update = FALSE; + + while (tree) { + codegen(s, tree->car->car, val); + codegen(s, tree->car->cdr, val); + len++; + tree = tree->cdr; + if (val && len == 126) { + pop_n(len*2); + genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); + if (update) { + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); + } + push(); + update = TRUE; + len = 0; + } + } + if (val) { + pop_n(len*2); + genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); + if (update) { + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); + } + push(); + } + } + break; + + case NODE_SPLAT: + codegen(s, tree, val); + break; + + case NODE_ASGN: + codegen(s, tree->cdr, VAL); + pop(); + gen_assignment(s, tree->car, cursp(), val); + break; + + case NODE_MASGN: + { + int len = 0, n = 0, post = 0; + node *t = tree->cdr, *p; + int rhs = cursp(); + + if (nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) { + /* fixed rhs */ + t = t->cdr; + while (t) { + codegen(s, t->car, VAL); + len++; + t = t->cdr; + } + tree = tree->car; + if (tree->car) { /* pre */ + t = tree->car; + n = 0; + while (t) { + if (n < len) { + gen_assignment(s, t->car, rhs+n, NOVAL); + n++; + } + else { + genop(s, MKOP_A(OP_LOADNIL, rhs+n)); + gen_assignment(s, t->car, rhs+n, NOVAL); + } + t = t->cdr; + } + } + t = tree->cdr; + if (t) { + if (t->cdr) { /* post count */ + p = t->cdr->car; + while (p) { + post++; + p = p->cdr; + } + } + if (t->car) { /* rest (len - pre - post) */ + int rn; + + if (len < post + n) { + rn = 0; + } + else { + rn = len - post - n; + } + genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); + gen_assignment(s, t->car, cursp(), NOVAL); + n += rn; + } + if (t->cdr && t->cdr->car) { + t = t->cdr->car; + while (n<len) { + gen_assignment(s, t->car, rhs+n, NOVAL); + t = t->cdr; + n++; + } + } + } + pop_n(len); + if (val) { + genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len)); + push(); + } + } + else { + /* variable rhs */ + codegen(s, t, VAL); + gen_vmassignment(s, tree->car, rhs, val); + if (!val) { + pop(); + } + } + } + break; + + case NODE_OP_ASGN: + { + mrb_sym sym = nsym(tree->cdr->car); + mrb_int len; + const char *name = mrb_sym2name_len(s->mrb, sym, &len); + int idx, callargs = -1, vsp = -1; + + if ((len == 2 && name[0] == '|' && name[1] == '|') && + (nint(tree->car->car) == NODE_CONST || + nint(tree->car->car) == NODE_CVAR)) { + int onerr, noexc, exc; + struct loopinfo *lp; + + onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); + lp = loop_push(s, LOOP_BEGIN); + lp->pc1 = onerr; + exc = cursp(); + codegen(s, tree->car, VAL); + lp->type = LOOP_RESCUE; + genop(s, MKOP_A(OP_POPERR, 1)); + noexc = genop(s, MKOP_Bx(OP_JMP, 0)); + dispatch(s, onerr); + genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0)); + genop(s, MKOP_A(OP_LOADF, exc)); + dispatch(s, noexc); + loop_pop(s, NOVAL); + } + else if (nint(tree->car->car) == NODE_CALL) { + node *n = tree->car->cdr; + int base, i, nargs = 0; + callargs = 0; + + if (val) { + vsp = cursp(); + push(); + } + codegen(s, n->car, VAL); /* receiver */ + idx = new_msym(s, nsym(n->cdr->car)); + base = cursp()-1; + if (n->cdr->cdr->car) { + nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1); + if (nargs >= 0) { + callargs = nargs; + } + else { /* varargs */ + push(); + nargs = 1; + callargs = CALL_MAXARGS; + } + } + /* copy receiver and arguments */ + genop(s, MKOP_AB(OP_MOVE, cursp(), base)); + for (i=0; i<nargs; i++) { + genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1)); + } + push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */ + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs)); + push(); + } + else { + codegen(s, tree->car, VAL); + } + if (len == 2 && + ((name[0] == '|' && name[1] == '|') || + (name[0] == '&' && name[1] == '&'))) { + int pos; + + pop(); + if (val) { + if (vsp >= 0) { + genop(s, MKOP_AB(OP_MOVE, vsp, cursp())); + } + pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0)); + } + else { + pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL); + } + codegen(s, tree->cdr->cdr->car, VAL); + pop(); + if (val && vsp >= 0) { + genop(s, MKOP_AB(OP_MOVE, vsp, cursp())); + } + if (nint(tree->car->car) == NODE_CALL) { + if (callargs == CALL_MAXARGS) { + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + else { + pop_n(callargs); + callargs++; + } + pop(); + idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car))); + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs)); + } + else { + gen_assignment(s, tree->car, cursp(), val); + } + dispatch(s, pos); + goto exit; + } + codegen(s, tree->cdr->cdr->car, VAL); + push(); pop(); + pop(); pop(); + + idx = new_msym(s, sym); + if (len == 1 && name[0] == '+') { + genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val); + } + else if (len == 1 && name[0] == '-') { + genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val); + } + else if (len == 1 && name[0] == '*') { + genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '/') { + genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '<') { + genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1)); + } + else if (len == 2 && name[0] == '<' && name[1] == '=') { + genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '>') { + genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1)); + } + else if (len == 2 && name[0] == '>' && name[1] == '=') { + genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1)); + } + else { + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1)); + } + if (callargs < 0) { + gen_assignment(s, tree->car, cursp(), val); + } + else { + if (val && vsp >= 0) { + genop(s, MKOP_AB(OP_MOVE, vsp, cursp())); + } + if (callargs == CALL_MAXARGS) { + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + else { + pop_n(callargs); + callargs++; + } + pop(); + idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car))); + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs)); + } + } + break; + + case NODE_SUPER: + { + codegen_scope *s2 = s; + int lv = 0; + int n = 0, noop = 0, sendv = 0; + + push(); /* room for receiver */ + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf))); + push(); push(); /* ARGARY pushes two values */ + pop(); pop(); + if (tree) { + node *args = tree->car; + if (args) { + n = gen_values(s, args, VAL, 0); + if (n < 0) { + n = noop = sendv = 1; + push(); + } + } + } + if (tree && tree->cdr) { + codegen(s, tree->cdr, VAL); + pop(); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); pop(); + } + pop_n(n+1); + if (sendv) n = CALL_MAXARGS; + genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n)); + if (val) push(); + } + break; + + case NODE_ZSUPER: + { + codegen_scope *s2 = s; + int lv = 0, ainfo = 0; + + push(); /* room for receiver */ + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2) ainfo = s2->ainfo; + genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf))); + push(); push(); pop(); /* ARGARY pushes two values */ + if (tree && tree->cdr) { + codegen(s, tree->cdr, VAL); + pop(); + } + pop(); pop(); + genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS)); + if (val) push(); + } + break; + + case NODE_RETURN: + if (tree) { + gen_retval(s, tree); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + if (s->loop) { + genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN)); + } + else { + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + if (val) push(); + break; + + case NODE_YIELD: + { + codegen_scope *s2 = s; + int lv = 0, ainfo = 0; + int n = 0, sendv = 0; + + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2) ainfo = s2->ainfo; + push(); + if (tree) { + n = gen_values(s, tree, VAL, 0); + if (n < 0) { + n = sendv = 1; + push(); + } + } + push();pop(); /* space for a block */ + pop_n(n+1); + genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); + if (sendv) n = CALL_MAXARGS; + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); + if (val) push(); + } + break; + + case NODE_BREAK: + loop_break(s, tree); + if (val) push(); + break; + + case NODE_NEXT: + if (!s->loop) { + raise_error(s, "unexpected next"); + } + else if (s->loop->type == LOOP_NORMAL) { + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + codegen(s, tree, NOVAL); + genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc)); + } + else { + if (tree) { + codegen(s, tree, VAL); + pop(); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + if (val) push(); + break; + + case NODE_REDO: + if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) { + raise_error(s, "unexpected redo"); + } + else { + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); + } + if (val) push(); + break; + + case NODE_RETRY: + { + const char *msg = "unexpected retry"; + + if (!s->loop) { + raise_error(s, msg); + } + else { + struct loopinfo *lp = s->loop; + int n = 0; + + while (lp && lp->type != LOOP_RESCUE) { + if (lp->type == LOOP_BEGIN) { + n++; + } + lp = lp->prev; + } + if (!lp) { + raise_error(s, msg); + } + else { + if (n > 0) { + genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL); + } + if (s->ensure_level > lp->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL); + } + genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); + } + } + if (val) push(); + } + break; + + case NODE_LVAR: + if (val) { + int idx = lv_idx(s, nsym(tree)); + + if (idx > 0) { + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL); + } + else { + int lv = 0; + codegen_scope *up = s->prev; + + while (up) { + idx = lv_idx(up, nsym(tree)); + if (idx > 0) { + genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); + break; + } + lv++; + up = up->prev; + } + } + push(); + } + break; + + case NODE_GVAR: + if (val) { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_IVAR: + if (val) { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); + push(); + } + break; + + case NODE_CVAR: + if (val) { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); + push(); + } + break; + + case NODE_CONST: + { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym)); + if (val) { + push(); + } + } + break; + + case NODE_DEFINED: + codegen(s, tree, VAL); + break; + + case NODE_BACK_REF: + if (val) { + char buf[3]; + int sym; + + buf[0] = '$'; + buf[1] = nchar(tree); + buf[2] = 0; + sym = new_sym(s, mrb_intern_cstr(s->mrb, buf)); + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_NTH_REF: + if (val) { + mrb_state *mrb = s->mrb; + mrb_value str; + int sym; + + str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree))); + sym = new_sym(s, mrb_intern_str(mrb, str)); + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_ARG: + /* should not happen */ + break; + + case NODE_BLOCK_ARG: + codegen(s, tree, VAL); + break; + + case NODE_INT: + if (val) { + char *p = (char*)tree->car; + int base = nint(tree->cdr->car); + mrb_int i; + mrb_code co; + mrb_bool overflow; + + i = readint_mrb_int(s, p, base, FALSE, &overflow); + if (overflow) { + double f = readint_float(s, p, base); + int off = new_lit(s, mrb_float_value(s->mrb, f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + } + else { + if (i < MAXARG_sBx && i > -MAXARG_sBx) { + co = MKOP_AsBx(OP_LOADI, cursp(), i); + } + else { + int off = new_lit(s, mrb_fixnum_value(i)); + co = MKOP_ABx(OP_LOADL, cursp(), off); + } + genop(s, co); + } + push(); + } + break; + + case NODE_FLOAT: + if (val) { + char *p = (char*)tree; + mrb_float f = mrb_float_read(p, NULL); + int off = new_lit(s, mrb_float_value(s->mrb, f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + push(); + } + break; + + case NODE_NEGATE: + { + nt = nint(tree->car); + tree = tree->cdr; + switch (nt) { + case NODE_FLOAT: + if (val) { + char *p = (char*)tree; + mrb_float f = mrb_float_read(p, NULL); + int off = new_lit(s, mrb_float_value(s->mrb, -f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + push(); + } + break; + + case NODE_INT: + if (val) { + char *p = (char*)tree->car; + int base = nint(tree->cdr->car); + mrb_int i; + mrb_code co; + mrb_bool overflow; + + i = readint_mrb_int(s, p, base, TRUE, &overflow); + if (overflow) { + double f = readint_float(s, p, base); + int off = new_lit(s, mrb_float_value(s->mrb, -f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + } + else { + if (i < MAXARG_sBx && i > -MAXARG_sBx) { + co = MKOP_AsBx(OP_LOADI, cursp(), i); + } + else { + int off = new_lit(s, mrb_fixnum_value(i)); + co = MKOP_ABx(OP_LOADL, cursp(), off); + } + genop(s, co); + } + push(); + } + break; + + default: + if (val) { + int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); + + genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); + push(); + codegen(s, tree, VAL); + pop(); pop(); + genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); + } + else { + codegen(s, tree, NOVAL); + } + break; + } + } + break; + + case NODE_STR: + if (val) { + char *p = (char*)tree->car; + size_t len = (intptr_t)tree->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int off = new_lit(s, mrb_str_new(s->mrb, p, len)); + + mrb_gc_arena_restore(s->mrb, ai); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + } + break; + + case NODE_HEREDOC: + tree = ((struct mrb_parser_heredoc_info *)tree)->doc; + /* fall through */ + case NODE_DSTR: + if (val) { + node *n = tree; + + if (!n) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + break; + } + codegen(s, n->car, VAL); + n = n->cdr; + while (n) { + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + } + else { + node *n = tree; + + while (n) { + if (nint(n->car->car) != NODE_STR) { + codegen(s, n->car, NOVAL); + } + n = n->cdr; + } + } + break; + + case NODE_WORDS: + gen_literal_array(s, tree, FALSE, val); + break; + + case NODE_SYMBOLS: + gen_literal_array(s, tree, TRUE, val); + break; + + case NODE_DXSTR: + { + node *n; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); + + genop(s, MKOP_A(OP_LOADSELF, cursp())); + push(); + codegen(s, tree->car, VAL); + n = tree->cdr; + while (n) { + if (nint(n->car->car) == NODE_XSTR) { + n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; + mrb_assert(!n->cdr); /* must be the end */ + } + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + push(); /* for block */ + pop_n(3); + sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); + if (val) push(); + mrb_gc_arena_restore(s->mrb, ai); + } + break; + + case NODE_XSTR: + { + char *p = (char*)tree->car; + size_t len = (intptr_t)tree->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int off = new_lit(s, mrb_str_new(s->mrb, p, len)); + int sym; + + genop(s, MKOP_A(OP_LOADSELF, cursp())); + push(); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); push(); + pop_n(3); + sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); + if (val) push(); + mrb_gc_arena_restore(s->mrb, ai); + } + break; + + case NODE_REGX: + if (val) { + char *p1 = (char*)tree->car; + char *p2 = (char*)tree->cdr->car; + char *p3 = (char*)tree->cdr->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); + int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); + int argc = 1; + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + if (p2 || p3) { + if (p2) { /* opt */ + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + push(); + argc++; + if (p3) { /* enc */ + off = new_lit(s, mrb_str_new(s->mrb, p3, 1)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + argc++; + } + } + push(); /* space for a block */ + pop_n(argc+2); + sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); + mrb_gc_arena_restore(s->mrb, ai); + push(); + } + break; + + case NODE_DREGX: + if (val) { + node *n = tree->car; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); + int argc = 1; + int off; + char *p; + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + codegen(s, n->car, VAL); + n = n->cdr; + while (n) { + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + n = tree->cdr->cdr; + if (n->car) { /* tail */ + p = (char*)n->car; + off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); + codegen(s, tree->car, VAL); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + } + if (n->cdr->car) { /* opt */ + char *p2 = (char*)n->cdr->car; + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + argc++; + } + if (n->cdr->cdr) { /* enc */ + char *p2 = (char*)n->cdr->cdr; + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + argc++; + } + push(); /* space for a block */ + pop_n(argc+2); + sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); + mrb_gc_arena_restore(s->mrb, ai); + push(); + } + else { + node *n = tree->car; + + while (n) { + if (nint(n->car->car) != NODE_STR) { + codegen(s, n->car, NOVAL); + } + n = n->cdr; + } + } + break; + + case NODE_SYM: + if (val) { + int sym = new_sym(s, nsym(tree)); + + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + break; + + case NODE_DSYM: + codegen(s, tree, val); + if (val) { + gen_send_intern(s); + } + break; + + case NODE_SELF: + if (val) { + genop(s, MKOP_A(OP_LOADSELF, cursp())); + push(); + } + break; + + case NODE_NIL: + if (val) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + break; + + case NODE_TRUE: + if (val) { + genop(s, MKOP_A(OP_LOADT, cursp())); + push(); + } + break; + + case NODE_FALSE: + if (val) { + genop(s, MKOP_A(OP_LOADF, cursp())); + push(); + } + break; + + case NODE_ALIAS: + { + int a = new_msym(s, nsym(tree->car)); + int b = new_msym(s, nsym(tree->cdr)); + int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method")); + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a)); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); + push(); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); /* space for a block */ + pop_n(4); + genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); + if (val) { + push(); + } + } + break; + + case NODE_UNDEF: + { + int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method")); + int num = 0; + node *t = tree; + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + while (t) { + int symbol; + if (num >= CALL_MAXARGS - 1) { + pop_n(num); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num)); + while (t) { + symbol = new_msym(s, nsym(t->car)); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + t = t->cdr; + } + num = CALL_MAXARGS; + break; + } + symbol = new_msym(s, nsym(t->car)); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); + push(); + t = t->cdr; + num++; + } + push();pop(); /* space for a block */ + pop(); + if (num < CALL_MAXARGS) { + pop_n(num); + } + genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); + if (val) { + push(); + } + } + break; + + case NODE_CLASS: + { + int idx; + + if (tree->car->car == (node*)0) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + else if (tree->car->car == (node*)1) { + genop(s, MKOP_A(OP_OCLASS, cursp())); + push(); + } + else { + codegen(s, tree->car->car, VAL); + } + if (tree->cdr->car) { + codegen(s, tree->cdr->car, VAL); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + pop(); pop(); + idx = new_msym(s, nsym(tree->car->cdr)); + genop(s, MKOP_AB(OP_CLASS, cursp(), idx)); + idx = scope_body(s, tree->cdr->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_MODULE: + { + int idx; + + if (tree->car->car == (node*)0) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + else if (tree->car->car == (node*)1) { + genop(s, MKOP_A(OP_OCLASS, cursp())); + push(); + } + else { + codegen(s, tree->car->car, VAL); + } + pop(); + idx = new_msym(s, nsym(tree->car->cdr)); + genop(s, MKOP_AB(OP_MODULE, cursp(), idx)); + idx = scope_body(s, tree->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_SCLASS: + { + int idx; + + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); + idx = scope_body(s, tree->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_DEF: + { + int sym = new_msym(s, nsym(tree->car)); + int idx = lambda_body(s, tree->cdr, 0); + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); + push(); pop(); + pop(); + genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); + if (val) { + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + } + break; + + case NODE_SDEF: + { + node *recv = tree->car; + int sym = new_msym(s, nsym(tree->cdr->car)); + int idx = lambda_body(s, tree->cdr->cdr, 0); + + codegen(s, recv, VAL); + pop(); + genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); + push(); + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); + pop(); + genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); + if (val) { + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + } + break; + + case NODE_POSTEXE: + codegen(s, tree, NOVAL); + break; + + default: + break; + } + exit: + s->rlev = rlev; +} + +static void +scope_add_irep(codegen_scope *s, mrb_irep *irep) +{ + if (s->irep == NULL) { + s->irep = irep; + return; + } + if (s->irep->rlen == s->rcapa) { + s->rcapa *= 2; + s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa); + } + s->irep->reps[s->irep->rlen] = irep; + s->irep->rlen++; +} + +static codegen_scope* +scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) +{ + static const codegen_scope codegen_scope_zero = { 0 }; + mrb_pool *pool = mrb_pool_open(mrb); + codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope)); + + if (!p) return NULL; + *p = codegen_scope_zero; + p->mrb = mrb; + p->mpool = pool; + if (!prev) return p; + p->prev = prev; + p->ainfo = -1; + p->mscope = 0; + + p->irep = mrb_add_irep(mrb); + scope_add_irep(prev, p->irep); + + p->rcapa = 8; + p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa); + + p->icapa = 1024; + p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); + p->irep->iseq = NULL; + + p->pcapa = 32; + p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); + p->irep->plen = 0; + + p->scapa = MAXMSYMLEN; + p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); + p->irep->slen = 0; + + p->lv = lv; + p->sp += node_len(lv)+1; /* add self */ + p->nlocals = p->sp; + if (lv) { + node *n = lv; + size_t i = 0; + + p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1)); + for (i=0, n=lv; n; i++,n=n->cdr) { + p->irep->lv[i].name = lv_name(n); + if (lv_name(n)) { + p->irep->lv[i].r = lv_idx(p, lv_name(n)); + } + else { + p->irep->lv[i].r = 0; + } + } + mrb_assert(i + 1 == p->nlocals); + } + p->ai = mrb_gc_arena_save(mrb); + + p->filename = prev->filename; + if (p->filename) { + p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa); + } + p->lineno = prev->lineno; + + /* debug setting */ + p->debug_start_pos = 0; + if (p->filename) { + mrb_debug_info_alloc(mrb, p->irep); + p->irep->filename = p->filename; + p->irep->lines = p->lines; + } + else { + p->irep->debug_info = NULL; + } + p->parser = prev->parser; + p->filename_index = prev->filename_index; + + p->rlev = prev->rlev+1; + + return p; +} + +static void +scope_finish(codegen_scope *s) +{ + mrb_state *mrb = s->mrb; + mrb_irep *irep = s->irep; + size_t fname_len; + char *fname; + + irep->flags = 0; + if (s->iseq) { + irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); + irep->ilen = s->pc; + if (s->lines) { + irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc); + } + else { + irep->lines = 0; + } + } + irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen); + irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen); + irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen); + if (s->filename) { + irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); + mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc); + + fname_len = strlen(s->filename); + fname = (char*)codegen_malloc(s, fname_len + 1); + memcpy(fname, s->filename, fname_len); + fname[fname_len] = '\0'; + irep->filename = fname; + irep->own_filename = TRUE; + } + + irep->nlocals = s->nlocals; + irep->nregs = s->nregs; + + mrb_gc_arena_restore(mrb, s->ai); + mrb_pool_close(s->mpool); +} + +static struct loopinfo* +loop_push(codegen_scope *s, enum looptype t) +{ + struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo)); + + p->type = t; + p->pc1 = p->pc2 = p->pc3 = 0; + p->prev = s->loop; + p->ensure_level = s->ensure_level; + p->acc = cursp(); + s->loop = p; + + return p; +} + +static void +loop_break(codegen_scope *s, node *tree) +{ + if (!s->loop) { + codegen(s, tree, NOVAL); + raise_error(s, "unexpected break"); + } + else { + struct loopinfo *loop; + int n = 0; + + if (tree) { + gen_retval(s, tree); + } + + loop = s->loop; + while (loop) { + if (loop->type == LOOP_BEGIN) { + n++; + loop = loop->prev; + } + else if (loop->type == LOOP_RESCUE) { + loop = loop->prev; + } + else{ + break; + } + } + if (!loop) { + raise_error(s, "unexpected break"); + return; + } + if (n > 0) { + genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL); + } + + if (loop->type == LOOP_NORMAL) { + int tmp; + + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + if (tree) { + genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL); + } + tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3)); + loop->pc3 = tmp; + } + else { + if (!tree) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK)); + } + } +} + +static void +loop_pop(codegen_scope *s, int val) +{ + if (val) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + dispatch_linked(s, s->loop->pc3); + s->loop = s->loop->prev; + if (val) push(); +} + +MRB_API struct RProc* +mrb_generate_code(mrb_state *mrb, parser_state *p) +{ + codegen_scope *scope = scope_new(mrb, 0, 0); + struct RProc *proc; + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + + if (!scope) { + return NULL; + } + scope->mrb = mrb; + scope->parser = p; + scope->filename = p->filename; + scope->filename_index = p->current_filename_index; + + MRB_TRY(&scope->jmp) { + mrb->jmp = &scope->jmp; + /* prepare irep */ + codegen(scope, p->tree, NOVAL); + proc = mrb_proc_new(mrb, scope->irep); + mrb_irep_decref(mrb, scope->irep); + mrb_pool_close(scope->mpool); + proc->c = NULL; + mrb->jmp = prev_jmp; + return proc; + } + MRB_CATCH(&scope->jmp) { + mrb_irep_decref(mrb, scope->irep); + mrb_pool_close(scope->mpool); + mrb->jmp = prev_jmp; + return NULL; + } + MRB_END_EXC(&scope->jmp); +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords new file mode 100644 index 00000000..9cb86608 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords @@ -0,0 +1,50 @@ +%{ +struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; +const struct kwtable *mrb_reserved_word(const char *, unsigned int); +static const struct kwtable *reserved_word(const char *, unsigned int); +#define mrb_reserved_word(str, len) reserved_word(str, len) +%} + +struct kwtable; +%% +__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END +__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END +__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END +BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END +END, {keyword_END, keyword_END}, EXPR_END +alias, {keyword_alias, keyword_alias}, EXPR_FNAME +and, {keyword_and, keyword_and}, EXPR_VALUE +begin, {keyword_begin, keyword_begin}, EXPR_BEG +break, {keyword_break, keyword_break}, EXPR_MID +case, {keyword_case, keyword_case}, EXPR_VALUE +class, {keyword_class, keyword_class}, EXPR_CLASS +def, {keyword_def, keyword_def}, EXPR_FNAME +do, {keyword_do, keyword_do}, EXPR_BEG +else, {keyword_else, keyword_else}, EXPR_BEG +elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE +end, {keyword_end, keyword_end}, EXPR_END +ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG +false, {keyword_false, keyword_false}, EXPR_END +for, {keyword_for, keyword_for}, EXPR_VALUE +if, {keyword_if, modifier_if}, EXPR_VALUE +in, {keyword_in, keyword_in}, EXPR_VALUE +module, {keyword_module, keyword_module}, EXPR_VALUE +next, {keyword_next, keyword_next}, EXPR_MID +nil, {keyword_nil, keyword_nil}, EXPR_END +not, {keyword_not, keyword_not}, EXPR_ARG +or, {keyword_or, keyword_or}, EXPR_VALUE +redo, {keyword_redo, keyword_redo}, EXPR_END +rescue, {keyword_rescue, modifier_rescue}, EXPR_MID +retry, {keyword_retry, keyword_retry}, EXPR_END +return, {keyword_return, keyword_return}, EXPR_MID +self, {keyword_self, keyword_self}, EXPR_END +super, {keyword_super, keyword_super}, EXPR_ARG +then, {keyword_then, keyword_then}, EXPR_BEG +true, {keyword_true, keyword_true}, EXPR_END +undef, {keyword_undef, keyword_undef}, EXPR_FNAME +unless, {keyword_unless, modifier_unless}, EXPR_VALUE +until, {keyword_until, modifier_until}, EXPR_VALUE +when, {keyword_when, keyword_when}, EXPR_VALUE +while, {keyword_while, modifier_while}, EXPR_VALUE +yield, {keyword_yield, keyword_yield}, EXPR_ARG +%% diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def new file mode 100644 index 00000000..58e30296 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def @@ -0,0 +1,212 @@ +/* ANSI-C code produced by gperf version 3.0.4 */ +/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' /home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#endif + +#line 1 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + +struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; +const struct kwtable *mrb_reserved_word(const char *, unsigned int); +static const struct kwtable *reserved_word(const char *, unsigned int); +#define mrb_reserved_word(str, len) reserved_word(str, len) +#line 8 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" +struct kwtable; + +#define TOTAL_KEYWORDS 40 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 12 +#define MIN_HASH_VALUE 8 +#define MAX_HASH_VALUE 50 +/* maximum key range = 43, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, + 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, + 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, + 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, + 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; +} + +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct kwtable * +mrb_reserved_word (register const char *str, register unsigned int len) +{ + static const struct kwtable wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"break", {keyword_break, keyword_break}, EXPR_MID}, +#line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"else", {keyword_else, keyword_else}, EXPR_BEG}, +#line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"nil", {keyword_nil, keyword_nil}, EXPR_END}, +#line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, +#line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"end", {keyword_end, keyword_end}, EXPR_END}, +#line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"then", {keyword_then, keyword_then}, EXPR_BEG}, +#line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"not", {keyword_not, keyword_not}, EXPR_ARG}, +#line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"false", {keyword_false, keyword_false}, EXPR_END}, +#line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"self", {keyword_self, keyword_self}, EXPR_END}, +#line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, +#line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, +#line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"true", {keyword_true, keyword_true}, EXPR_END}, +#line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"until", {keyword_until, modifier_until}, EXPR_VALUE}, +#line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, +#line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"return", {keyword_return, keyword_return}, EXPR_MID}, +#line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"def", {keyword_def, keyword_def}, EXPR_FNAME}, +#line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"and", {keyword_and, keyword_and}, EXPR_VALUE}, +#line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"do", {keyword_do, keyword_do}, EXPR_BEG}, +#line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, +#line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"for", {keyword_for, keyword_for}, EXPR_VALUE}, +#line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, +#line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"or", {keyword_or, keyword_or}, EXPR_VALUE}, +#line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"in", {keyword_in, keyword_in}, EXPR_VALUE}, +#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"when", {keyword_when, keyword_when}, EXPR_VALUE}, +#line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"retry", {keyword_retry, keyword_retry}, EXPR_END}, +#line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"if", {keyword_if, modifier_if}, EXPR_VALUE}, +#line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"case", {keyword_case, keyword_case}, EXPR_VALUE}, +#line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"redo", {keyword_redo, keyword_redo}, EXPR_END}, +#line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"next", {keyword_next, keyword_next}, EXPR_MID}, +#line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"super", {keyword_super, keyword_super}, EXPR_ARG}, +#line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"module", {keyword_module, keyword_module}, EXPR_VALUE}, +#line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, +#line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, +#line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, +#line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, +#line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"END", {keyword_END, keyword_END}, EXPR_END}, +#line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, +#line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, + {""}, +#line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"class", {keyword_class, keyword_class}, EXPR_CLASS}, + {""}, {""}, +#line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + {"while", {keyword_while, modifier_while}, EXPR_VALUE} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} +#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h new file mode 100644 index 00000000..9636dd75 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h @@ -0,0 +1,118 @@ +/* +** node.h - nodes of abstract syntax tree +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_COMPILER_NODE_H +#define MRUBY_COMPILER_NODE_H + +enum node_type { + NODE_METHOD, + NODE_FBODY, + NODE_CFUNC, + NODE_SCOPE, + NODE_BLOCK, + NODE_IF, + NODE_CASE, + NODE_WHEN, + NODE_OPT_N, + NODE_WHILE, + NODE_UNTIL, + NODE_ITER, + NODE_FOR, + NODE_BREAK, + NODE_NEXT, + NODE_REDO, + NODE_RETRY, + NODE_BEGIN, + NODE_RESCUE, + NODE_ENSURE, + NODE_AND, + NODE_OR, + NODE_NOT, + NODE_MASGN, + NODE_ASGN, + NODE_CDECL, + NODE_CVASGN, + NODE_CVDECL, + NODE_OP_ASGN, + NODE_CALL, + NODE_SCALL, + NODE_FCALL, + NODE_VCALL, + NODE_SUPER, + NODE_ZSUPER, + NODE_ARRAY, + NODE_ZARRAY, + NODE_HASH, + NODE_RETURN, + NODE_YIELD, + NODE_LVAR, + NODE_DVAR, + NODE_GVAR, + NODE_IVAR, + NODE_CONST, + NODE_CVAR, + NODE_NTH_REF, + NODE_BACK_REF, + NODE_MATCH, + NODE_MATCH2, + NODE_MATCH3, + NODE_INT, + NODE_FLOAT, + NODE_NEGATE, + NODE_LAMBDA, + NODE_SYM, + NODE_STR, + NODE_DSTR, + NODE_XSTR, + NODE_DXSTR, + NODE_REGX, + NODE_DREGX, + NODE_DREGX_ONCE, + NODE_LIST, + NODE_ARG, + NODE_ARGSCAT, + NODE_ARGSPUSH, + NODE_SPLAT, + NODE_TO_ARY, + NODE_SVALUE, + NODE_BLOCK_ARG, + NODE_DEF, + NODE_SDEF, + NODE_ALIAS, + NODE_UNDEF, + NODE_CLASS, + NODE_MODULE, + NODE_SCLASS, + NODE_COLON2, + NODE_COLON3, + NODE_CREF, + NODE_DOT2, + NODE_DOT3, + NODE_FLIP2, + NODE_FLIP3, + NODE_ATTRSET, + NODE_SELF, + NODE_NIL, + NODE_TRUE, + NODE_FALSE, + NODE_DEFINED, + NODE_NEWLINE, + NODE_POSTEXE, + NODE_ALLOCA, + NODE_DMETHOD, + NODE_BMETHOD, + NODE_MEMO, + NODE_IFUNC, + NODE_DSYM, + NODE_ATTRASGN, + NODE_HEREDOC, + NODE_LITERAL_DELIM, + NODE_WORDS, + NODE_SYMBOLS, + NODE_LAST +}; + +#endif /* MRUBY_COMPILER_NODE_H */ diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y new file mode 100644 index 00000000..0e425b18 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y @@ -0,0 +1,6652 @@ +/* +** parse.y - mruby parser +** +** See Copyright Notice in mruby.h +*/ + +%{ +#undef PARSER_DEBUG +#ifdef PARSER_DEBUG +# define YYDEBUG 1 +#endif +#define YYERROR_VERBOSE 1 +/* + * Force yacc to use our memory management. This is a little evil because + * the macros assume that "parser_state *p" is in scope + */ +#define YYMALLOC(n) mrb_malloc(p->mrb, (n)) +#define YYFREE(o) mrb_free(p->mrb, (o)) +#define YYSTACK_USE_ALLOCA 0 + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <mruby.h> +#include <mruby/compile.h> +#include <mruby/proc.h> +#include <mruby/error.h> +#include <mruby/throw.h> +#include "node.h" + +#define YYLEX_PARAM p + +typedef mrb_ast_node node; +typedef struct mrb_parser_state parser_state; +typedef struct mrb_parser_heredoc_info parser_heredoc_info; + +static int yyparse(parser_state *p); +static int yylex(void *lval, parser_state *p); +static void yyerror(parser_state *p, const char *s); +static void yywarn(parser_state *p, const char *s); +static void yywarning(parser_state *p, const char *s); +static void backref_error(parser_state *p, node *n); +static void void_expr_error(parser_state *p, node *n); +static void tokadd(parser_state *p, int32_t c); + +#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) + +typedef unsigned int stack_type; + +#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) +#define BITSTACK_POP(stack) ((stack) = (stack) >> 1) +#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) +#define BITSTACK_SET_P(stack) ((stack)&1) + +#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) +#define COND_POP() BITSTACK_POP(p->cond_stack) +#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) +#define COND_P() BITSTACK_SET_P(p->cond_stack) + +#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) +#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) +#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) +#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) + +#define SET_LINENO(c,n) ((c)->lineno = (n)) +#define NODE_LINENO(c,n) do {\ + if (n) {\ + (c)->filename_index = (n)->filename_index;\ + (c)->lineno = (n)->lineno;\ + }\ +} while (0) + +#define sym(x) ((mrb_sym)(intptr_t)(x)) +#define nsym(x) ((node*)(intptr_t)(x)) +#define nint(x) ((node*)(intptr_t)(x)) +#define intn(x) ((int)(intptr_t)(x)) + +static inline mrb_sym +intern_cstr_gen(parser_state *p, const char *s) +{ + return mrb_intern_cstr(p->mrb, s); +} +#define intern_cstr(s) intern_cstr_gen(p,(s)) + +static inline mrb_sym +intern_gen(parser_state *p, const char *s, size_t len) +{ + return mrb_intern(p->mrb, s, len); +} +#define intern(s,len) intern_gen(p,(s),(len)) + +static inline mrb_sym +intern_gen_c(parser_state *p, const char c) +{ + return mrb_intern(p->mrb, &c, 1); +} +#define intern_c(c) intern_gen_c(p,(c)) + +static void +cons_free_gen(parser_state *p, node *cons) +{ + cons->cdr = p->cells; + p->cells = cons; +} +#define cons_free(c) cons_free_gen(p, (c)) + +static void* +parser_palloc(parser_state *p, size_t size) +{ + void *m = mrb_pool_alloc(p->pool, size); + + if (!m) { + MRB_THROW(p->jmp); + } + return m; +} + +static node* +cons_gen(parser_state *p, node *car, node *cdr) +{ + node *c; + + if (p->cells) { + c = p->cells; + p->cells = p->cells->cdr; + } + else { + c = (node *)parser_palloc(p, sizeof(mrb_ast_node)); + } + + c->car = car; + c->cdr = cdr; + c->lineno = p->lineno; + c->filename_index = p->current_filename_index; + return c; +} +#define cons(a,b) cons_gen(p,(a),(b)) + +static node* +list1_gen(parser_state *p, node *a) +{ + return cons(a, 0); +} +#define list1(a) list1_gen(p, (a)) + +static node* +list2_gen(parser_state *p, node *a, node *b) +{ + return cons(a, cons(b,0)); +} +#define list2(a,b) list2_gen(p, (a),(b)) + +static node* +list3_gen(parser_state *p, node *a, node *b, node *c) +{ + return cons(a, cons(b, cons(c,0))); +} +#define list3(a,b,c) list3_gen(p, (a),(b),(c)) + +static node* +list4_gen(parser_state *p, node *a, node *b, node *c, node *d) +{ + return cons(a, cons(b, cons(c, cons(d, 0)))); +} +#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) + +static node* +list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) +{ + return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); +} +#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) + +static node* +list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) +{ + return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); +} +#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) + +static node* +append_gen(parser_state *p, node *a, node *b) +{ + node *c = a; + + if (!a) return b; + while (c->cdr) { + c = c->cdr; + } + if (b) { + c->cdr = b; + } + return a; +} +#define append(a,b) append_gen(p,(a),(b)) +#define push(a,b) append_gen(p,(a),list1(b)) + +static char* +parser_strndup(parser_state *p, const char *s, size_t len) +{ + char *b = (char *)parser_palloc(p, len+1); + + memcpy(b, s, len); + b[len] = '\0'; + return b; +} +#undef strndup +#define strndup(s,len) parser_strndup(p, s, len) + +static char* +parser_strdup(parser_state *p, const char *s) +{ + return parser_strndup(p, s, strlen(s)); +} +#undef strdup +#define strdup(s) parser_strdup(p, s) + +/* xxx ----------------------------- */ + +static node* +local_switch(parser_state *p) +{ + node *prev = p->locals; + + p->locals = cons(0, 0); + return prev; +} + +static void +local_resume(parser_state *p, node *prev) +{ + p->locals = prev; +} + +static void +local_nest(parser_state *p) +{ + p->locals = cons(0, p->locals); +} + +static void +local_unnest(parser_state *p) +{ + if (p->locals) { + p->locals = p->locals->cdr; + } +} + +static mrb_bool +local_var_p(parser_state *p, mrb_sym sym) +{ + node *l = p->locals; + + while (l) { + node *n = l->car; + while (n) { + if (sym(n->car) == sym) return TRUE; + n = n->cdr; + } + l = l->cdr; + } + return FALSE; +} + +static void +local_add_f(parser_state *p, mrb_sym sym) +{ + if (p->locals) { + p->locals->car = push(p->locals->car, nsym(sym)); + } +} + +static void +local_add(parser_state *p, mrb_sym sym) +{ + if (!local_var_p(p, sym)) { + local_add_f(p, sym); + } +} + +static node* +locals_node(parser_state *p) +{ + return p->locals ? p->locals->car : NULL; +} + +/* (:scope (vars..) (prog...)) */ +static node* +new_scope(parser_state *p, node *body) +{ + return cons((node*)NODE_SCOPE, cons(locals_node(p), body)); +} + +/* (:begin prog...) */ +static node* +new_begin(parser_state *p, node *body) +{ + if (body) { + return list2((node*)NODE_BEGIN, body); + } + return cons((node*)NODE_BEGIN, 0); +} + +#define newline_node(n) (n) + +/* (:rescue body rescue else) */ +static node* +new_rescue(parser_state *p, node *body, node *resq, node *els) +{ + return list4((node*)NODE_RESCUE, body, resq, els); +} + +static node* +new_mod_rescue(parser_state *p, node *body, node *resq) +{ + return new_rescue(p, body, list1(list3(0, 0, resq)), 0); +} + +/* (:ensure body ensure) */ +static node* +new_ensure(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); +} + +/* (:nil) */ +static node* +new_nil(parser_state *p) +{ + return list1((node*)NODE_NIL); +} + +/* (:true) */ +static node* +new_true(parser_state *p) +{ + return list1((node*)NODE_TRUE); +} + +/* (:false) */ +static node* +new_false(parser_state *p) +{ + return list1((node*)NODE_FALSE); +} + +/* (:alias new old) */ +static node* +new_alias(parser_state *p, mrb_sym a, mrb_sym b) +{ + return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); +} + +/* (:if cond then else) */ +static node* +new_if(parser_state *p, node *a, node *b, node *c) +{ + void_expr_error(p, a); + return list4((node*)NODE_IF, a, b, c); +} + +/* (:unless cond then else) */ +static node* +new_unless(parser_state *p, node *a, node *b, node *c) +{ + void_expr_error(p, a); + return list4((node*)NODE_IF, a, c, b); +} + +/* (:while cond body) */ +static node* +new_while(parser_state *p, node *a, node *b) +{ + void_expr_error(p, a); + return cons((node*)NODE_WHILE, cons(a, b)); +} + +/* (:until cond body) */ +static node* +new_until(parser_state *p, node *a, node *b) +{ + void_expr_error(p, a); + return cons((node*)NODE_UNTIL, cons(a, b)); +} + +/* (:for var obj body) */ +static node* +new_for(parser_state *p, node *v, node *o, node *b) +{ + void_expr_error(p, o); + return list4((node*)NODE_FOR, v, o, b); +} + +/* (:case a ((when ...) body) ((when...) body)) */ +static node* +new_case(parser_state *p, node *a, node *b) +{ + node *n = list2((node*)NODE_CASE, a); + node *n2 = n; + + void_expr_error(p, a); + while (n2->cdr) { + n2 = n2->cdr; + } + n2->cdr = b; + return n; +} + +/* (:postexe a) */ +static node* +new_postexe(parser_state *p, node *a) +{ + return cons((node*)NODE_POSTEXE, a); +} + +/* (:self) */ +static node* +new_self(parser_state *p) +{ + return list1((node*)NODE_SELF); +} + +/* (:call a b c) */ +static node* +new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass) +{ + node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); + void_expr_error(p, a); + NODE_LINENO(n, a); + return n; +} + +/* (:fcall self mid args) */ +static node* +new_fcall(parser_state *p, mrb_sym b, node *c) +{ + node *n = new_self(p); + NODE_LINENO(n, c); + n = list4((node*)NODE_FCALL, n, nsym(b), c); + NODE_LINENO(n, c); + return n; +} + +/* (:super . c) */ +static node* +new_super(parser_state *p, node *c) +{ + return cons((node*)NODE_SUPER, c); +} + +/* (:zsuper) */ +static node* +new_zsuper(parser_state *p) +{ + return list1((node*)NODE_ZSUPER); +} + +/* (:yield . c) */ +static node* +new_yield(parser_state *p, node *c) +{ + if (c) { + if (c->cdr) { + yyerror(p, "both block arg and actual block given"); + } + return cons((node*)NODE_YIELD, c->car); + } + return cons((node*)NODE_YIELD, 0); +} + +/* (:return . c) */ +static node* +new_return(parser_state *p, node *c) +{ + return cons((node*)NODE_RETURN, c); +} + +/* (:break . c) */ +static node* +new_break(parser_state *p, node *c) +{ + return cons((node*)NODE_BREAK, c); +} + +/* (:next . c) */ +static node* +new_next(parser_state *p, node *c) +{ + return cons((node*)NODE_NEXT, c); +} + +/* (:redo) */ +static node* +new_redo(parser_state *p) +{ + return list1((node*)NODE_REDO); +} + +/* (:retry) */ +static node* +new_retry(parser_state *p) +{ + return list1((node*)NODE_RETRY); +} + +/* (:dot2 a b) */ +static node* +new_dot2(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DOT2, cons(a, b)); +} + +/* (:dot3 a b) */ +static node* +new_dot3(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DOT3, cons(a, b)); +} + +/* (:colon2 b c) */ +static node* +new_colon2(parser_state *p, node *b, mrb_sym c) +{ + void_expr_error(p, b); + return cons((node*)NODE_COLON2, cons(b, nsym(c))); +} + +/* (:colon3 . c) */ +static node* +new_colon3(parser_state *p, mrb_sym c) +{ + return cons((node*)NODE_COLON3, nsym(c)); +} + +/* (:and a b) */ +static node* +new_and(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_AND, cons(a, b)); +} + +/* (:or a b) */ +static node* +new_or(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_OR, cons(a, b)); +} + +/* (:array a...) */ +static node* +new_array(parser_state *p, node *a) +{ + return cons((node*)NODE_ARRAY, a); +} + +/* (:splat . a) */ +static node* +new_splat(parser_state *p, node *a) +{ + return cons((node*)NODE_SPLAT, a); +} + +/* (:hash (k . v) (k . v)...) */ +static node* +new_hash(parser_state *p, node *a) +{ + return cons((node*)NODE_HASH, a); +} + +/* (:sym . a) */ +static node* +new_sym(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_SYM, nsym(sym)); +} + +static mrb_sym +new_strsym(parser_state *p, node* str) +{ + const char *s = (const char*)str->cdr->car; + size_t len = (size_t)str->cdr->cdr; + + return mrb_intern(p->mrb, s, len); +} + +/* (:lvar . a) */ +static node* +new_lvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_LVAR, nsym(sym)); +} + +/* (:gvar . a) */ +static node* +new_gvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_GVAR, nsym(sym)); +} + +/* (:ivar . a) */ +static node* +new_ivar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_IVAR, nsym(sym)); +} + +/* (:cvar . a) */ +static node* +new_cvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_CVAR, nsym(sym)); +} + +/* (:const . a) */ +static node* +new_const(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_CONST, nsym(sym)); +} + +/* (:undef a...) */ +static node* +new_undef(parser_state *p, mrb_sym sym) +{ + return list2((node*)NODE_UNDEF, nsym(sym)); +} + +/* (:class class super body) */ +static node* +new_class(parser_state *p, node *c, node *s, node *b) +{ + void_expr_error(p, s); + return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); +} + +/* (:sclass obj body) */ +static node* +new_sclass(parser_state *p, node *o, node *b) +{ + void_expr_error(p, o); + return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); +} + +/* (:module module body) */ +static node* +new_module(parser_state *p, node *m, node *b) +{ + return list3((node*)NODE_MODULE, m, cons(locals_node(p), b)); +} + +/* (:def m lv (arg . body)) */ +static node* +new_def(parser_state *p, mrb_sym m, node *a, node *b) +{ + return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b); +} + +/* (:sdef obj m lv (arg . body)) */ +static node* +new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) +{ + void_expr_error(p, o); + return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b); +} + +/* (:arg . sym) */ +static node* +new_arg(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_ARG, nsym(sym)); +} + +/* (m o r m2 b) */ +/* m: (a b c) */ +/* o: ((a . e1) (b . e2)) */ +/* r: a */ +/* m2: (a b c) */ +/* b: a */ +static node* +new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk) +{ + node *n; + + n = cons(m2, nsym(blk)); + n = cons(nsym(rest), n); + n = cons(opt, n); + return cons(m, n); +} + +/* (:block_arg . a) */ +static node* +new_block_arg(parser_state *p, node *a) +{ + return cons((node*)NODE_BLOCK_ARG, a); +} + +/* (:block arg body) */ +static node* +new_block(parser_state *p, node *a, node *b) +{ + return list4((node*)NODE_BLOCK, locals_node(p), a, b); +} + +/* (:lambda arg body) */ +static node* +new_lambda(parser_state *p, node *a, node *b) +{ + return list4((node*)NODE_LAMBDA, locals_node(p), a, b); +} + +/* (:asgn lhs rhs) */ +static node* +new_asgn(parser_state *p, node *a, node *b) +{ + void_expr_error(p, b); + return cons((node*)NODE_ASGN, cons(a, b)); +} + +/* (:masgn mlhs=(pre rest post) mrhs) */ +static node* +new_masgn(parser_state *p, node *a, node *b) +{ + void_expr_error(p, b); + return cons((node*)NODE_MASGN, cons(a, b)); +} + +/* (:asgn lhs rhs) */ +static node* +new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) +{ + void_expr_error(p, b); + return list4((node*)NODE_OP_ASGN, a, nsym(op), b); +} + +/* (:int . i) */ +static node* +new_int(parser_state *p, const char *s, int base) +{ + return list3((node*)NODE_INT, (node*)strdup(s), nint(base)); +} + +/* (:float . i) */ +static node* +new_float(parser_state *p, const char *s) +{ + return cons((node*)NODE_FLOAT, (node*)strdup(s)); +} + +/* (:str . (s . len)) */ +static node* +new_str(parser_state *p, const char *s, size_t len) +{ + return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len))); +} + +/* (:dstr . a) */ +static node* +new_dstr(parser_state *p, node *a) +{ + return cons((node*)NODE_DSTR, a); +} + +/* (:str . (s . len)) */ +static node* +new_xstr(parser_state *p, const char *s, int len) +{ + return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len))); +} + +/* (:xstr . a) */ +static node* +new_dxstr(parser_state *p, node *a) +{ + return cons((node*)NODE_DXSTR, a); +} + +/* (:dsym . a) */ +static node* +new_dsym(parser_state *p, node *a) +{ + return cons((node*)NODE_DSYM, new_dstr(p, a)); +} + +/* (:regx . (s . (opt . enc))) */ +static node* +new_regx(parser_state *p, const char *p1, const char* p2, const char* p3) +{ + return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3))); +} + +/* (:dregx . (a . b)) */ +static node* +new_dregx(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DREGX, cons(a, b)); +} + +/* (:backref . n) */ +static node* +new_back_ref(parser_state *p, int n) +{ + return cons((node*)NODE_BACK_REF, nint(n)); +} + +/* (:nthref . n) */ +static node* +new_nth_ref(parser_state *p, int n) +{ + return cons((node*)NODE_NTH_REF, nint(n)); +} + +/* (:heredoc . a) */ +static node* +new_heredoc(parser_state *p) +{ + parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info)); + return cons((node*)NODE_HEREDOC, (node*)inf); +} + +static void +new_bv(parser_state *p, mrb_sym id) +{ +} + +static node* +new_literal_delim(parser_state *p) +{ + return cons((node*)NODE_LITERAL_DELIM, 0); +} + +/* (:words . a) */ +static node* +new_words(parser_state *p, node *a) +{ + return cons((node*)NODE_WORDS, a); +} + +/* (:symbols . a) */ +static node* +new_symbols(parser_state *p, node *a) +{ + return cons((node*)NODE_SYMBOLS, a); +} + +/* xxx ----------------------------- */ + +/* (:call a op) */ +static node* +call_uni_op(parser_state *p, node *recv, const char *m) +{ + void_expr_error(p, recv); + return new_call(p, recv, intern_cstr(m), 0, 1); +} + +/* (:call a op b) */ +static node* +call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) +{ + return new_call(p, recv, intern_cstr(m), list1(list1(arg1)), 1); +} + +static void +args_with_block(parser_state *p, node *a, node *b) +{ + if (b) { + if (a->cdr) { + yyerror(p, "both block arg and actual block given"); + } + a->cdr = b; + } +} + +static void +call_with_block(parser_state *p, node *a, node *b) +{ + node *n; + + switch ((enum node_type)intn(a->car)) { + case NODE_SUPER: + case NODE_ZSUPER: + if (!a->cdr) a->cdr = cons(0, b); + else { + args_with_block(p, a->cdr, b); + } + break; + case NODE_CALL: + case NODE_FCALL: + case NODE_SCALL: + n = a->cdr->cdr->cdr; + if (!n->car) n->car = cons(0, b); + else { + args_with_block(p, n->car, b); + } + break; + default: + break; + } +} + +static node* +negate_lit(parser_state *p, node *n) +{ + return cons((node*)NODE_NEGATE, n); +} + +static node* +cond(node *n) +{ + return n; +} + +static node* +ret_args(parser_state *p, node *n) +{ + if (n->cdr) { + yyerror(p, "block argument should not be given"); + return NULL; + } + if (!n->car->cdr) return n->car->car; + return new_array(p, n->car); +} + +static void +assignable(parser_state *p, node *lhs) +{ + if (intn(lhs->car) == NODE_LVAR) { + local_add(p, sym(lhs->cdr)); + } +} + +static node* +var_reference(parser_state *p, node *lhs) +{ + node *n; + + if (intn(lhs->car) == NODE_LVAR) { + if (!local_var_p(p, sym(lhs->cdr))) { + n = new_fcall(p, sym(lhs->cdr), 0); + cons_free(lhs); + return n; + } + } + + return lhs; +} + +typedef enum mrb_string_type string_type; + +static node* +new_strterm(parser_state *p, string_type type, int term, int paren) +{ + return cons(nint(type), cons((node*)0, cons(nint(paren), nint(term)))); +} + +static void +end_strterm(parser_state *p) +{ + cons_free(p->lex_strterm->cdr->cdr); + cons_free(p->lex_strterm->cdr); + cons_free(p->lex_strterm); + p->lex_strterm = NULL; +} + +static parser_heredoc_info * +parsing_heredoc_inf(parser_state *p) +{ + node *nd = p->parsing_heredoc; + if (nd == NULL) + return NULL; + /* mrb_assert(nd->car->car == NODE_HEREDOC); */ + return (parser_heredoc_info*)nd->car->cdr; +} + +static void +heredoc_treat_nextline(parser_state *p) +{ + if (p->heredocs_from_nextline == NULL) + return; + if (p->parsing_heredoc == NULL) { + node *n; + p->parsing_heredoc = p->heredocs_from_nextline; + p->lex_strterm_before_heredoc = p->lex_strterm; + p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0); + n = p->all_heredocs; + if (n) { + while (n->cdr) + n = n->cdr; + n->cdr = p->parsing_heredoc; + } + else { + p->all_heredocs = p->parsing_heredoc; + } + } + else { + node *n, *m; + m = p->heredocs_from_nextline; + while (m->cdr) + m = m->cdr; + n = p->all_heredocs; + mrb_assert(n != NULL); + if (n == p->parsing_heredoc) { + m->cdr = n; + p->all_heredocs = p->heredocs_from_nextline; + p->parsing_heredoc = p->heredocs_from_nextline; + } + else { + while (n->cdr != p->parsing_heredoc) { + n = n->cdr; + mrb_assert(n != NULL); + } + m->cdr = n->cdr; + n->cdr = p->heredocs_from_nextline; + p->parsing_heredoc = p->heredocs_from_nextline; + } + } + p->heredocs_from_nextline = NULL; +} + +static void +heredoc_end(parser_state *p) +{ + p->parsing_heredoc = p->parsing_heredoc->cdr; + if (p->parsing_heredoc == NULL) { + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + end_strterm(p); + p->lex_strterm = p->lex_strterm_before_heredoc; + p->lex_strterm_before_heredoc = NULL; + p->heredoc_end_now = TRUE; + } + else { + /* next heredoc */ + p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type); + } +} +#define is_strterm_type(p,str_func) (intn((p)->lex_strterm->car) & (str_func)) + +/* xxx ----------------------------- */ + +%} + +%pure-parser +%parse-param {parser_state *p} +%lex-param {parser_state *p} + +%union { + node *nd; + mrb_sym id; + int num; + stack_type stack; + const struct vtable *vars; +} + +%token <num> + keyword_class + keyword_module + keyword_def + keyword_begin + keyword_if + keyword_unless + keyword_while + keyword_until + keyword_for + +%token + keyword_undef + keyword_rescue + keyword_ensure + keyword_end + keyword_then + keyword_elsif + keyword_else + keyword_case + keyword_when + keyword_break + keyword_next + keyword_redo + keyword_retry + keyword_in + keyword_do + keyword_do_cond + keyword_do_block + keyword_do_LAMBDA + keyword_return + keyword_yield + keyword_super + keyword_self + keyword_nil + keyword_true + keyword_false + keyword_and + keyword_or + keyword_not + modifier_if + modifier_unless + modifier_while + modifier_until + modifier_rescue + keyword_alias + keyword_BEGIN + keyword_END + keyword__LINE__ + keyword__FILE__ + keyword__ENCODING__ + +%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL +%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP +%token <nd> tSTRING tSTRING_PART tSTRING_MID tLABEL_END +%token <nd> tNTH_REF tBACK_REF +%token <num> tREGEXP_END + +%type <nd> singleton string string_rep string_interp xstring regexp +%type <nd> literal numeric cpath symbol +%type <nd> top_compstmt top_stmts top_stmt +%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call +%type <nd> expr_value arg_rhs primary_value +%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure +%type <nd> args call_args opt_call_args +%type <nd> paren_args opt_paren_args variable +%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs +%type <nd> command_asgn command_rhs mrhs superclass block_call block_command +%type <nd> f_block_optarg f_block_opt +%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs +%type <nd> assoc_list assocs assoc undef_list backref for_var +%type <nd> block_param opt_block_param block_param_def f_opt +%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body +%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg +%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner +%type <id> fsym sym basic_symbol operation operation2 operation3 +%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn +%type <nd> heredoc words symbols +%type <num> call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */ + +%token tUPLUS /* unary+ */ +%token tUMINUS /* unary- */ +%token tPOW /* ** */ +%token tCMP /* <=> */ +%token tEQ /* == */ +%token tEQQ /* === */ +%token tNEQ /* != */ +%token tGEQ /* >= */ +%token tLEQ /* <= */ +%token tANDOP tOROP /* && and || */ +%token tMATCH tNMATCH /* =~ and !~ */ +%token tDOT2 tDOT3 /* .. and ... */ +%token tAREF tASET /* [] and []= */ +%token tLSHFT tRSHFT /* << and >> */ +%token tCOLON2 /* :: */ +%token tCOLON3 /* :: at EXPR_BEG */ +%token <id> tOP_ASGN /* +=, -= etc. */ +%token tASSOC /* => */ +%token tLPAREN /* ( */ +%token tLPAREN_ARG /* ( */ +%token tRPAREN /* ) */ +%token tLBRACK /* [ */ +%token tLBRACE /* { */ +%token tLBRACE_ARG /* { */ +%token tSTAR /* * */ +%token tAMPER /* & */ +%token tLAMBDA /* -> */ +%token tANDDOT /* &. */ +%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG +%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG +%token <nd> tHEREDOC_BEG /* <<, <<- */ +%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM +%token <nd> tHD_STRING_PART tHD_STRING_MID + +/* + * precedence table + */ + +%nonassoc tLOWEST +%nonassoc tLBRACE_ARG + +%nonassoc modifier_if modifier_unless modifier_while modifier_until +%left keyword_or keyword_and +%right keyword_not +%right '=' tOP_ASGN +%left modifier_rescue +%right '?' ':' +%nonassoc tDOT2 tDOT3 +%left tOROP +%left tANDOP +%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH +%left '>' tGEQ '<' tLEQ +%left '|' '^' +%left '&' +%left tLSHFT tRSHFT +%left '+' '-' +%left '*' '/' '%' +%right tUMINUS_NUM tUMINUS +%right tPOW +%right '!' '~' tUPLUS + +%token tLAST_TOKEN + +%% +program : { + p->lstate = EXPR_BEG; + if (!p->locals) p->locals = cons(0,0); + } + top_compstmt + { + p->tree = new_scope(p, $2); + NODE_LINENO(p->tree, $2); + } + ; + +top_compstmt : top_stmts opt_terms + { + $$ = $1; + } + ; + +top_stmts : none + { + $$ = new_begin(p, 0); + } + | top_stmt + { + $$ = new_begin(p, $1); + NODE_LINENO($$, $1); + } + | top_stmts terms top_stmt + { + $$ = push($1, newline_node($3)); + } + | error top_stmt + { + $$ = new_begin(p, 0); + } + ; + +top_stmt : stmt + | keyword_BEGIN + { + $<nd>$ = local_switch(p); + } + '{' top_compstmt '}' + { + yyerror(p, "BEGIN not supported"); + local_resume(p, $<nd>2); + $$ = 0; + } + ; + +bodystmt : compstmt + opt_rescue + opt_else + opt_ensure + { + if ($2) { + $$ = new_rescue(p, $1, $2, $3); + NODE_LINENO($$, $1); + } + else if ($3) { + yywarn(p, "else without rescue is useless"); + $$ = push($1, $3); + } + else { + $$ = $1; + } + if ($4) { + if ($$) { + $$ = new_ensure(p, $$, $4); + } + else { + $$ = push($4, new_nil(p)); + } + } + } + ; + +compstmt : stmts opt_terms + { + $$ = $1; + } + ; + +stmts : none + { + $$ = new_begin(p, 0); + } + | stmt + { + $$ = new_begin(p, $1); + NODE_LINENO($$, $1); + } + | stmts terms stmt + { + $$ = push($1, newline_node($3)); + } + | error stmt + { + $$ = new_begin(p, $2); + } + ; + +stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym + { + $$ = new_alias(p, $2, $4); + } + | keyword_undef undef_list + { + $$ = $2; + } + | stmt modifier_if expr_value + { + $$ = new_if(p, cond($3), $1, 0); + } + | stmt modifier_unless expr_value + { + $$ = new_unless(p, cond($3), $1, 0); + } + | stmt modifier_while expr_value + { + $$ = new_while(p, cond($3), $1); + } + | stmt modifier_until expr_value + { + $$ = new_until(p, cond($3), $1); + } + | stmt modifier_rescue stmt + { + $$ = new_mod_rescue(p, $1, $3); + } + | keyword_END '{' compstmt '}' + { + yyerror(p, "END not supported"); + $$ = new_postexe(p, $3); + } + | command_asgn + | mlhs '=' command_call + { + $$ = new_masgn(p, $1, $3); + } + | lhs '=' mrhs + { + $$ = new_asgn(p, $1, new_array(p, $3)); + } + | mlhs '=' arg + { + $$ = new_masgn(p, $1, $3); + } + | mlhs '=' mrhs + { + $$ = new_masgn(p, $1, new_array(p, $3)); + } + | expr + ; + +command_asgn : lhs '=' command_rhs + { + $$ = new_asgn(p, $1, $3); + } + | var_lhs tOP_ASGN command_rhs + { + $$ = new_op_asgn(p, $1, $2, $3); + } + | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); + } + | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); + } + | primary_value call_op tCONSTANT tOP_ASGN command_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); + } + | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call + { + yyerror(p, "constant re-assignment"); + $$ = 0; + } + | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); + } + | backref tOP_ASGN command_rhs + { + backref_error(p, $1); + $$ = new_begin(p, 0); + } + ; + +command_rhs : command_call %prec tOP_ASGN + | command_call modifier_rescue stmt + { + $$ = new_mod_rescue(p, $1, $3); + } + | command_asgn + ; + + +expr : command_call + | expr keyword_and expr + { + $$ = new_and(p, $1, $3); + } + | expr keyword_or expr + { + $$ = new_or(p, $1, $3); + } + | keyword_not opt_nl expr + { + $$ = call_uni_op(p, cond($3), "!"); + } + | '!' command_call + { + $$ = call_uni_op(p, cond($2), "!"); + } + | arg + ; + +expr_value : expr + { + if (!$1) $$ = new_nil(p); + else { + $$ = $1; + } + } + ; + +command_call : command + | block_command + ; + +block_command : block_call + | block_call call_op2 operation2 command_args + { + $$ = new_call(p, $1, $3, $4, $2); + } + ; + +cmd_brace_block : tLBRACE_ARG + { + local_nest(p); + } + opt_block_param + compstmt + '}' + { + $$ = new_block(p, $3, $4); + local_unnest(p); + } + ; + +command : operation command_args %prec tLOWEST + { + $$ = new_fcall(p, $1, $2); + } + | operation command_args cmd_brace_block + { + args_with_block(p, $2, $3); + $$ = new_fcall(p, $1, $2); + } + | primary_value call_op operation2 command_args %prec tLOWEST + { + $$ = new_call(p, $1, $3, $4, $2); + } + | primary_value call_op operation2 command_args cmd_brace_block + { + args_with_block(p, $4, $5); + $$ = new_call(p, $1, $3, $4, $2); + } + | primary_value tCOLON2 operation2 command_args %prec tLOWEST + { + $$ = new_call(p, $1, $3, $4, tCOLON2); + } + | primary_value tCOLON2 operation2 command_args cmd_brace_block + { + args_with_block(p, $4, $5); + $$ = new_call(p, $1, $3, $4, tCOLON2); + } + | keyword_super command_args + { + $$ = new_super(p, $2); + } + | keyword_yield command_args + { + $$ = new_yield(p, $2); + } + | keyword_return call_args + { + $$ = new_return(p, ret_args(p, $2)); + } + | keyword_break call_args + { + $$ = new_break(p, ret_args(p, $2)); + } + | keyword_next call_args + { + $$ = new_next(p, ret_args(p, $2)); + } + ; + +mlhs : mlhs_basic + { + $$ = $1; + } + | tLPAREN mlhs_inner rparen + { + $$ = $2; + } + ; + +mlhs_inner : mlhs_basic + | tLPAREN mlhs_inner rparen + { + $$ = $2; + } + ; + +mlhs_basic : mlhs_list + { + $$ = list1($1); + } + | mlhs_list mlhs_item + { + $$ = list1(push($1,$2)); + } + | mlhs_list tSTAR mlhs_node + { + $$ = list2($1, $3); + } + | mlhs_list tSTAR mlhs_node ',' mlhs_post + { + $$ = list3($1, $3, $5); + } + | mlhs_list tSTAR + { + $$ = list2($1, new_nil(p)); + } + | mlhs_list tSTAR ',' mlhs_post + { + $$ = list3($1, new_nil(p), $4); + } + | tSTAR mlhs_node + { + $$ = list2(0, $2); + } + | tSTAR mlhs_node ',' mlhs_post + { + $$ = list3(0, $2, $4); + } + | tSTAR + { + $$ = list2(0, new_nil(p)); + } + | tSTAR ',' mlhs_post + { + $$ = list3(0, new_nil(p), $3); + } + ; + +mlhs_item : mlhs_node + | tLPAREN mlhs_inner rparen + { + $$ = new_masgn(p, $2, NULL); + } + ; + +mlhs_list : mlhs_item ',' + { + $$ = list1($1); + } + | mlhs_list mlhs_item ',' + { + $$ = push($1, $2); + } + ; + +mlhs_post : mlhs_item + { + $$ = list1($1); + } + | mlhs_list mlhs_item + { + $$ = push($1, $2); + } + ; + +mlhs_node : variable + { + assignable(p, $1); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3, '.'); + } + | primary_value call_op tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0, $2); + } + | primary_value tCOLON2 tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0, tCOLON2); + } + | primary_value call_op tCONSTANT + { + $$ = new_call(p, $1, $3, 0, $2); + } + | primary_value tCOLON2 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon3(p, $2); + } + | backref + { + backref_error(p, $1); + $$ = 0; + } + ; + +lhs : variable + { + assignable(p, $1); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3, '.'); + } + | primary_value call_op tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0, $2); + } + | primary_value tCOLON2 tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0, tCOLON2); + } + | primary_value call_op tCONSTANT + { + $$ = new_call(p, $1, $3, 0, $2); + } + | primary_value tCOLON2 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon3(p, $2); + } + | backref + { + backref_error(p, $1); + $$ = 0; + } + ; + +cname : tIDENTIFIER + { + yyerror(p, "class/module name must be CONSTANT"); + } + | tCONSTANT + ; + +cpath : tCOLON3 cname + { + $$ = cons((node*)1, nsym($2)); + } + | cname + { + $$ = cons((node*)0, nsym($1)); + } + | primary_value tCOLON2 cname + { + void_expr_error(p, $1); + $$ = cons($1, nsym($3)); + } + ; + +fname : tIDENTIFIER + | tCONSTANT + | tFID + | op + { + p->lstate = EXPR_ENDFN; + $$ = $1; + } + | reswords + { + p->lstate = EXPR_ENDFN; + $$ = $<id>1; + } + ; + +fsym : fname + | basic_symbol + ; + +undef_list : fsym + { + $$ = new_undef(p, $1); + } + | undef_list ',' {p->lstate = EXPR_FNAME;} fsym + { + $$ = push($1, nsym($4)); + } + ; + +op : '|' { $$ = intern_c('|'); } + | '^' { $$ = intern_c('^'); } + | '&' { $$ = intern_c('&'); } + | tCMP { $$ = intern("<=>",3); } + | tEQ { $$ = intern("==",2); } + | tEQQ { $$ = intern("===",3); } + | tMATCH { $$ = intern("=~",2); } + | tNMATCH { $$ = intern("!~",2); } + | '>' { $$ = intern_c('>'); } + | tGEQ { $$ = intern(">=",2); } + | '<' { $$ = intern_c('<'); } + | tLEQ { $$ = intern("<=",2); } + | tNEQ { $$ = intern("!=",2); } + | tLSHFT { $$ = intern("<<",2); } + | tRSHFT { $$ = intern(">>",2); } + | '+' { $$ = intern_c('+'); } + | '-' { $$ = intern_c('-'); } + | '*' { $$ = intern_c('*'); } + | tSTAR { $$ = intern_c('*'); } + | '/' { $$ = intern_c('/'); } + | '%' { $$ = intern_c('%'); } + | tPOW { $$ = intern("**",2); } + | '!' { $$ = intern_c('!'); } + | '~' { $$ = intern_c('~'); } + | tUPLUS { $$ = intern("+@",2); } + | tUMINUS { $$ = intern("-@",2); } + | tAREF { $$ = intern("[]",2); } + | tASET { $$ = intern("[]=",3); } + | '`' { $$ = intern_c('`'); } + ; + +reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ + | keyword_BEGIN | keyword_END + | keyword_alias | keyword_and | keyword_begin + | keyword_break | keyword_case | keyword_class | keyword_def + | keyword_do | keyword_else | keyword_elsif + | keyword_end | keyword_ensure | keyword_false + | keyword_for | keyword_in | keyword_module | keyword_next + | keyword_nil | keyword_not | keyword_or | keyword_redo + | keyword_rescue | keyword_retry | keyword_return | keyword_self + | keyword_super | keyword_then | keyword_true | keyword_undef + | keyword_when | keyword_yield | keyword_if | keyword_unless + | keyword_while | keyword_until + ; + +arg : lhs '=' arg_rhs + { + $$ = new_asgn(p, $1, $3); + } + | var_lhs tOP_ASGN arg_rhs + { + $$ = new_op_asgn(p, $1, $2, $3); + } + | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); + } + | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); + } + | primary_value call_op tCONSTANT tOP_ASGN arg_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); + } + | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); + } + | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs + { + yyerror(p, "constant re-assignment"); + $$ = new_begin(p, 0); + } + | tCOLON3 tCONSTANT tOP_ASGN arg_rhs + { + yyerror(p, "constant re-assignment"); + $$ = new_begin(p, 0); + } + | backref tOP_ASGN arg_rhs + { + backref_error(p, $1); + $$ = new_begin(p, 0); + } + | arg tDOT2 arg + { + $$ = new_dot2(p, $1, $3); + } + | arg tDOT3 arg + { + $$ = new_dot3(p, $1, $3); + } + | arg '+' arg + { + $$ = call_bin_op(p, $1, "+", $3); + } + | arg '-' arg + { + $$ = call_bin_op(p, $1, "-", $3); + } + | arg '*' arg + { + $$ = call_bin_op(p, $1, "*", $3); + } + | arg '/' arg + { + $$ = call_bin_op(p, $1, "/", $3); + } + | arg '%' arg + { + $$ = call_bin_op(p, $1, "%", $3); + } + | arg tPOW arg + { + $$ = call_bin_op(p, $1, "**", $3); + } + | tUMINUS_NUM tINTEGER tPOW arg + { + $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); + } + | tUMINUS_NUM tFLOAT tPOW arg + { + $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); + } + | tUPLUS arg + { + $$ = call_uni_op(p, $2, "+@"); + } + | tUMINUS arg + { + $$ = call_uni_op(p, $2, "-@"); + } + | arg '|' arg + { + $$ = call_bin_op(p, $1, "|", $3); + } + | arg '^' arg + { + $$ = call_bin_op(p, $1, "^", $3); + } + | arg '&' arg + { + $$ = call_bin_op(p, $1, "&", $3); + } + | arg tCMP arg + { + $$ = call_bin_op(p, $1, "<=>", $3); + } + | arg '>' arg + { + $$ = call_bin_op(p, $1, ">", $3); + } + | arg tGEQ arg + { + $$ = call_bin_op(p, $1, ">=", $3); + } + | arg '<' arg + { + $$ = call_bin_op(p, $1, "<", $3); + } + | arg tLEQ arg + { + $$ = call_bin_op(p, $1, "<=", $3); + } + | arg tEQ arg + { + $$ = call_bin_op(p, $1, "==", $3); + } + | arg tEQQ arg + { + $$ = call_bin_op(p, $1, "===", $3); + } + | arg tNEQ arg + { + $$ = call_bin_op(p, $1, "!=", $3); + } + | arg tMATCH arg + { + $$ = call_bin_op(p, $1, "=~", $3); + } + | arg tNMATCH arg + { + $$ = call_bin_op(p, $1, "!~", $3); + } + | '!' arg + { + $$ = call_uni_op(p, cond($2), "!"); + } + | '~' arg + { + $$ = call_uni_op(p, cond($2), "~"); + } + | arg tLSHFT arg + { + $$ = call_bin_op(p, $1, "<<", $3); + } + | arg tRSHFT arg + { + $$ = call_bin_op(p, $1, ">>", $3); + } + | arg tANDOP arg + { + $$ = new_and(p, $1, $3); + } + | arg tOROP arg + { + $$ = new_or(p, $1, $3); + } + | arg '?' arg opt_nl ':' arg + { + $$ = new_if(p, cond($1), $3, $6); + } + | primary + { + $$ = $1; + } + ; + +aref_args : none + | args trailer + { + $$ = $1; + NODE_LINENO($$, $1); + } + | args comma assocs trailer + { + $$ = push($1, new_hash(p, $3)); + } + | assocs trailer + { + $$ = cons(new_hash(p, $1), 0); + NODE_LINENO($$, $1); + } + ; + +arg_rhs : arg %prec tOP_ASGN + { + $$ = $1; + } + | arg modifier_rescue arg + { + void_expr_error(p, $1); + void_expr_error(p, $3); + $$ = new_mod_rescue(p, $1, $3); + } + ; + +paren_args : '(' opt_call_args rparen + { + $$ = $2; + } + ; + +opt_paren_args : none + | paren_args + ; + +opt_call_args : none + | call_args + | args ',' + { + $$ = cons($1,0); + NODE_LINENO($$, $1); + } + | args comma assocs ',' + { + $$ = cons(push($1, new_hash(p, $3)), 0); + NODE_LINENO($$, $1); + } + | assocs ',' + { + $$ = cons(list1(new_hash(p, $1)), 0); + NODE_LINENO($$, $1); + } + ; + +call_args : command + { + void_expr_error(p, $1); + $$ = cons(list1($1), 0); + NODE_LINENO($$, $1); + } + | args opt_block_arg + { + $$ = cons($1, $2); + NODE_LINENO($$, $1); + } + | assocs opt_block_arg + { + $$ = cons(list1(new_hash(p, $1)), $2); + NODE_LINENO($$, $1); + } + | args comma assocs opt_block_arg + { + $$ = cons(push($1, new_hash(p, $3)), $4); + NODE_LINENO($$, $1); + } + | block_arg + { + $$ = cons(0, $1); + NODE_LINENO($$, $1); + } + ; + +command_args : { + $<stack>$ = p->cmdarg_stack; + CMDARG_PUSH(1); + } + call_args + { + p->cmdarg_stack = $<stack>1; + $$ = $2; + } + ; + +block_arg : tAMPER arg + { + $$ = new_block_arg(p, $2); + } + ; + +opt_block_arg : comma block_arg + { + $$ = $2; + } + | none + { + $$ = 0; + } + ; + +comma : ',' + | ',' heredoc_bodies + ; + +args : arg + { + void_expr_error(p, $1); + $$ = cons($1, 0); + NODE_LINENO($$, $1); + } + | tSTAR arg + { + void_expr_error(p, $2); + $$ = cons(new_splat(p, $2), 0); + NODE_LINENO($$, $2); + } + | args comma arg + { + void_expr_error(p, $3); + $$ = push($1, $3); + } + | args comma tSTAR arg + { + void_expr_error(p, $4); + $$ = push($1, new_splat(p, $4)); + } + ; + +mrhs : args comma arg + { + void_expr_error(p, $3); + $$ = push($1, $3); + } + | args comma tSTAR arg + { + void_expr_error(p, $4); + $$ = push($1, new_splat(p, $4)); + } + | tSTAR arg + { + void_expr_error(p, $2); + $$ = list1(new_splat(p, $2)); + } + ; + +primary : literal + | string + | xstring + | regexp + | heredoc + | var_ref + | backref + | tFID + { + $$ = new_fcall(p, $1, 0); + } + | keyword_begin + { + $<stack>$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + bodystmt + keyword_end + { + p->cmdarg_stack = $<stack>2; + $$ = $3; + } + | tLPAREN_ARG + { + $<stack>$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + stmt {p->lstate = EXPR_ENDARG;} rparen + { + p->cmdarg_stack = $<stack>2; + $$ = $3; + } + | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen + { + $$ = new_nil(p); + } + | tLPAREN compstmt ')' + { + $$ = $2; + } + | primary_value tCOLON2 tCONSTANT + { + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + $$ = new_colon3(p, $2); + } + | tLBRACK aref_args ']' + { + $$ = new_array(p, $2); + NODE_LINENO($$, $2); + } + | tLBRACE assoc_list '}' + { + $$ = new_hash(p, $2); + NODE_LINENO($$, $2); + } + | keyword_return + { + $$ = new_return(p, 0); + } + | keyword_yield opt_paren_args + { + $$ = new_yield(p, $2); + } + | keyword_not '(' expr rparen + { + $$ = call_uni_op(p, cond($3), "!"); + } + | keyword_not '(' rparen + { + $$ = call_uni_op(p, new_nil(p), "!"); + } + | operation brace_block + { + $$ = new_fcall(p, $1, cons(0, $2)); + } + | method_call + | method_call brace_block + { + call_with_block(p, $1, $2); + $$ = $1; + } + | tLAMBDA + { + local_nest(p); + $<num>$ = p->lpar_beg; + p->lpar_beg = ++p->paren_nest; + } + f_larglist + { + $<stack>$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + lambda_body + { + p->lpar_beg = $<num>2; + $$ = new_lambda(p, $3, $5); + local_unnest(p); + p->cmdarg_stack = $<stack>4; + CMDARG_LEXPOP(); + } + | keyword_if expr_value then + compstmt + if_tail + keyword_end + { + $$ = new_if(p, cond($2), $4, $5); + SET_LINENO($$, $1); + } + | keyword_unless expr_value then + compstmt + opt_else + keyword_end + { + $$ = new_unless(p, cond($2), $4, $5); + SET_LINENO($$, $1); + } + | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} + compstmt + keyword_end + { + $$ = new_while(p, cond($3), $6); + SET_LINENO($$, $1); + } + | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} + compstmt + keyword_end + { + $$ = new_until(p, cond($3), $6); + SET_LINENO($$, $1); + } + | keyword_case expr_value opt_terms + case_body + keyword_end + { + $$ = new_case(p, $2, $4); + } + | keyword_case opt_terms case_body keyword_end + { + $$ = new_case(p, 0, $3); + } + | keyword_for for_var keyword_in + {COND_PUSH(1);} + expr_value do + {COND_POP();} + compstmt + keyword_end + { + $$ = new_for(p, $2, $5, $8); + SET_LINENO($$, $1); + } + | keyword_class + cpath superclass + { + if (p->in_def || p->in_single) + yyerror(p, "class definition in method body"); + $<nd>$ = local_switch(p); + } + bodystmt + keyword_end + { + $$ = new_class(p, $2, $3, $5); + SET_LINENO($$, $1); + local_resume(p, $<nd>4); + } + | keyword_class + tLSHFT expr + { + $<num>$ = p->in_def; + p->in_def = 0; + } + term + { + $<nd>$ = cons(local_switch(p), nint(p->in_single)); + p->in_single = 0; + } + bodystmt + keyword_end + { + $$ = new_sclass(p, $3, $7); + SET_LINENO($$, $1); + local_resume(p, $<nd>6->car); + p->in_def = $<num>4; + p->in_single = intn($<nd>6->cdr); + } + | keyword_module + cpath + { + if (p->in_def || p->in_single) + yyerror(p, "module definition in method body"); + $<nd>$ = local_switch(p); + } + bodystmt + keyword_end + { + $$ = new_module(p, $2, $4); + SET_LINENO($$, $1); + local_resume(p, $<nd>3); + } + | keyword_def fname + { + $<stack>$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + { + p->in_def++; + $<nd>$ = local_switch(p); + } + f_arglist + bodystmt + keyword_end + { + $$ = new_def(p, $2, $5, $6); + SET_LINENO($$, $1); + local_resume(p, $<nd>4); + p->in_def--; + p->cmdarg_stack = $<stack>3; + } + | keyword_def singleton dot_or_colon + { + p->lstate = EXPR_FNAME; + $<stack>$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + fname + { + p->in_single++; + p->lstate = EXPR_ENDFN; /* force for args */ + $<nd>$ = local_switch(p); + } + f_arglist + bodystmt + keyword_end + { + $$ = new_sdef(p, $2, $5, $7, $8); + SET_LINENO($$, $1); + local_resume(p, $<nd>6); + p->in_single--; + p->cmdarg_stack = $<stack>4; + } + | keyword_break + { + $$ = new_break(p, 0); + } + | keyword_next + { + $$ = new_next(p, 0); + } + | keyword_redo + { + $$ = new_redo(p); + } + | keyword_retry + { + $$ = new_retry(p); + } + ; + +primary_value : primary + { + $$ = $1; + if (!$$) $$ = new_nil(p); + } + ; + +then : term + | keyword_then + | term keyword_then + ; + +do : term + | keyword_do_cond + ; + +if_tail : opt_else + | keyword_elsif expr_value then + compstmt + if_tail + { + $$ = new_if(p, cond($2), $4, $5); + } + ; + +opt_else : none + | keyword_else compstmt + { + $$ = $2; + } + ; + +for_var : lhs + { + $$ = list1(list1($1)); + } + | mlhs + ; + +f_marg : f_norm_arg + { + $$ = new_arg(p, $1); + } + | tLPAREN f_margs rparen + { + $$ = new_masgn(p, $2, 0); + } + ; + +f_marg_list : f_marg + { + $$ = list1($1); + } + | f_marg_list ',' f_marg + { + $$ = push($1, $3); + } + ; + +f_margs : f_marg_list + { + $$ = list3($1,0,0); + } + | f_marg_list ',' tSTAR f_norm_arg + { + $$ = list3($1, new_arg(p, $4), 0); + } + | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list + { + $$ = list3($1, new_arg(p, $4), $6); + } + | f_marg_list ',' tSTAR + { + $$ = list3($1, (node*)-1, 0); + } + | f_marg_list ',' tSTAR ',' f_marg_list + { + $$ = list3($1, (node*)-1, $5); + } + | tSTAR f_norm_arg + { + $$ = list3(0, new_arg(p, $2), 0); + } + | tSTAR f_norm_arg ',' f_marg_list + { + $$ = list3(0, new_arg(p, $2), $4); + } + | tSTAR + { + $$ = list3(0, (node*)-1, 0); + } + | tSTAR ',' f_marg_list + { + $$ = list3(0, (node*)-1, $3); + } + ; + +block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, 0, $6); + } + | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, $7, $8); + } + | f_arg ',' f_block_optarg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, 0, $4); + } + | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, $5, $6); + } + | f_arg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, 0, $4); + } + | f_arg ',' + { + $$ = new_args(p, $1, 0, 0, 0, 0); + } + | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, $5, $6); + } + | f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, 0, 0, $2); + } + | f_block_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, 0, $4); + } + | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, $5, $6); + } + | f_block_optarg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, 0, $2); + } + | f_block_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, $3, $4); + } + | f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, 0, $2); + } + | f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, $3, $4); + } + | f_block_arg + { + $$ = new_args(p, 0, 0, 0, 0, $1); + } + ; + +opt_block_param : none + | block_param_def + { + p->cmd_start = TRUE; + $$ = $1; + } + ; + +block_param_def : '|' opt_bv_decl '|' + { + $$ = 0; + } + | tOROP + { + $$ = 0; + } + | '|' block_param opt_bv_decl '|' + { + $$ = $2; + } + ; + + +opt_bv_decl : opt_nl + { + $$ = 0; + } + | opt_nl ';' bv_decls opt_nl + { + $$ = 0; + } + ; + +bv_decls : bvar + | bv_decls ',' bvar + ; + +bvar : tIDENTIFIER + { + local_add_f(p, $1); + new_bv(p, $1); + } + | f_bad_arg + ; + +f_larglist : '(' f_args opt_bv_decl ')' + { + $$ = $2; + } + | f_args + { + $$ = $1; + } + ; + +lambda_body : tLAMBEG compstmt '}' + { + $$ = $2; + } + | keyword_do_LAMBDA compstmt keyword_end + { + $$ = $2; + } + ; + +do_block : keyword_do_block + { + local_nest(p); + } + opt_block_param + compstmt + keyword_end + { + $$ = new_block(p,$3,$4); + local_unnest(p); + } + ; + +block_call : command do_block + { + if ($1->car == (node*)NODE_YIELD) { + yyerror(p, "block given to yield"); + } + else { + call_with_block(p, $1, $2); + } + $$ = $1; + } + | block_call call_op2 operation2 opt_paren_args + { + $$ = new_call(p, $1, $3, $4, $2); + } + | block_call call_op2 operation2 opt_paren_args brace_block + { + $$ = new_call(p, $1, $3, $4, $2); + call_with_block(p, $$, $5); + } + | block_call call_op2 operation2 command_args do_block + { + $$ = new_call(p, $1, $3, $4, $2); + call_with_block(p, $$, $5); + } + ; + +method_call : operation paren_args + { + $$ = new_fcall(p, $1, $2); + } + | primary_value call_op operation2 opt_paren_args + { + $$ = new_call(p, $1, $3, $4, $2); + } + | primary_value tCOLON2 operation2 paren_args + { + $$ = new_call(p, $1, $3, $4, tCOLON2); + } + | primary_value tCOLON2 operation3 + { + $$ = new_call(p, $1, $3, 0, tCOLON2); + } + | primary_value call_op paren_args + { + $$ = new_call(p, $1, intern("call",4), $3, $2); + } + | primary_value tCOLON2 paren_args + { + $$ = new_call(p, $1, intern("call",4), $3, tCOLON2); + } + | keyword_super paren_args + { + $$ = new_super(p, $2); + } + | keyword_super + { + $$ = new_zsuper(p); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3, '.'); + } + ; + +brace_block : '{' + { + local_nest(p); + $<num>$ = p->lineno; + } + opt_block_param + compstmt '}' + { + $$ = new_block(p,$3,$4); + SET_LINENO($$, $<num>2); + local_unnest(p); + } + | keyword_do + { + local_nest(p); + $<num>$ = p->lineno; + } + opt_block_param + compstmt keyword_end + { + $$ = new_block(p,$3,$4); + SET_LINENO($$, $<num>2); + local_unnest(p); + } + ; + +case_body : keyword_when args then + compstmt + cases + { + $$ = cons(cons($2, $4), $5); + } + ; + +cases : opt_else + { + if ($1) { + $$ = cons(cons(0, $1), 0); + } + else { + $$ = 0; + } + } + | case_body + ; + +opt_rescue : keyword_rescue exc_list exc_var then + compstmt + opt_rescue + { + $$ = list1(list3($2, $3, $5)); + if ($6) $$ = append($$, $6); + } + | none + ; + +exc_list : arg + { + $$ = list1($1); + } + | mrhs + | none + ; + +exc_var : tASSOC lhs + { + $$ = $2; + } + | none + ; + +opt_ensure : keyword_ensure compstmt + { + $$ = $2; + } + | none + ; + +literal : numeric + | symbol + | words + | symbols + ; + +string : tCHAR + | tSTRING + | tSTRING_BEG tSTRING + { + $$ = $2; + } + | tSTRING_BEG string_rep tSTRING + { + $$ = new_dstr(p, push($2, $3)); + } + ; + +string_rep : string_interp + | string_rep string_interp + { + $$ = append($1, $2); + } + ; + +string_interp : tSTRING_MID + { + $$ = list1($1); + } + | tSTRING_PART + { + $<nd>$ = p->lex_strterm; + p->lex_strterm = NULL; + } + compstmt + '}' + { + p->lex_strterm = $<nd>2; + $$ = list2($1, $3); + } + | tLITERAL_DELIM + { + $$ = list1(new_literal_delim(p)); + } + | tHD_LITERAL_DELIM heredoc_bodies + { + $$ = list1(new_literal_delim(p)); + } + ; + +xstring : tXSTRING_BEG tXSTRING + { + $$ = $2; + } + | tXSTRING_BEG string_rep tXSTRING + { + $$ = new_dxstr(p, push($2, $3)); + } + ; + +regexp : tREGEXP_BEG tREGEXP + { + $$ = $2; + } + | tREGEXP_BEG string_rep tREGEXP + { + $$ = new_dregx(p, $2, $3); + } + ; + +heredoc : tHEREDOC_BEG + ; + +heredoc_bodies : heredoc_body + | heredoc_bodies heredoc_body + ; + +heredoc_body : tHEREDOC_END + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + inf->doc = push(inf->doc, new_str(p, "", 0)); + heredoc_end(p); + } + | heredoc_string_rep tHEREDOC_END + { + heredoc_end(p); + } + ; + +heredoc_string_rep : heredoc_string_interp + | heredoc_string_rep heredoc_string_interp + ; + +heredoc_string_interp : tHD_STRING_MID + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + inf->doc = push(inf->doc, $1); + heredoc_treat_nextline(p); + } + | tHD_STRING_PART + { + $<nd>$ = p->lex_strterm; + p->lex_strterm = NULL; + } + compstmt + '}' + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + p->lex_strterm = $<nd>2; + inf->doc = push(push(inf->doc, $1), $3); + } + ; + +words : tWORDS_BEG tSTRING + { + $$ = new_words(p, list1($2)); + } + | tWORDS_BEG string_rep tSTRING + { + $$ = new_words(p, push($2, $3)); + } + ; + + +symbol : basic_symbol + { + $$ = new_sym(p, $1); + } + | tSYMBEG tSTRING_BEG string_rep tSTRING + { + p->lstate = EXPR_END; + $$ = new_dsym(p, push($3, $4)); + } + ; + +basic_symbol : tSYMBEG sym + { + p->lstate = EXPR_END; + $$ = $2; + } + ; + +sym : fname + | tIVAR + | tGVAR + | tCVAR + | tSTRING + { + $$ = new_strsym(p, $1); + } + | tSTRING_BEG tSTRING + { + $$ = new_strsym(p, $2); + } + ; + +symbols : tSYMBOLS_BEG tSTRING + { + $$ = new_symbols(p, list1($2)); + } + | tSYMBOLS_BEG string_rep tSTRING + { + $$ = new_symbols(p, push($2, $3)); + } + ; + +numeric : tINTEGER + | tFLOAT + | tUMINUS_NUM tINTEGER %prec tLOWEST + { + $$ = negate_lit(p, $2); + } + | tUMINUS_NUM tFLOAT %prec tLOWEST + { + $$ = negate_lit(p, $2); + } + ; + +variable : tIDENTIFIER + { + $$ = new_lvar(p, $1); + } + | tIVAR + { + $$ = new_ivar(p, $1); + } + | tGVAR + { + $$ = new_gvar(p, $1); + } + | tCVAR + { + $$ = new_cvar(p, $1); + } + | tCONSTANT + { + $$ = new_const(p, $1); + } + ; + +var_lhs : variable + { + assignable(p, $1); + } + ; + +var_ref : variable + { + $$ = var_reference(p, $1); + } + | keyword_nil + { + $$ = new_nil(p); + } + | keyword_self + { + $$ = new_self(p); + } + | keyword_true + { + $$ = new_true(p); + } + | keyword_false + { + $$ = new_false(p); + } + | keyword__FILE__ + { + const char *fn = p->filename; + if (!fn) { + fn = "(null)"; + } + $$ = new_str(p, fn, strlen(fn)); + } + | keyword__LINE__ + { + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", p->lineno); + $$ = new_int(p, buf, 10); + } + | keyword__ENCODING__ + { +#ifdef MRB_UTF8_STRING + const char *enc = "UTF-8"; +#else + const char *enc = "ASCII-8BIT"; +#endif + $$ = new_str(p, enc, strlen(enc)); + } + ; + +backref : tNTH_REF + | tBACK_REF + ; + +superclass : /* term */ + { + $$ = 0; + } + | '<' + { + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + } + expr_value term + { + $$ = $3; + } /* + | error term + { + yyerrok; + $$ = 0; + } */ + ; + +f_arglist : '(' f_args rparen + { + $$ = $2; + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + } + | f_args term + { + $$ = $1; + } + ; + +f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, 0, $6); + } + | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, $7, $8); + } + | f_arg ',' f_optarg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, 0, $4); + } + | f_arg ',' f_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, $5, $6); + } + | f_arg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, 0, $4); + } + | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, $5, $6); + } + | f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, 0, 0, $2); + } + | f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, 0, $4); + } + | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, $5, $6); + } + | f_optarg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, 0, $2); + } + | f_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, $3, $4); + } + | f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, 0, $2); + } + | f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, $3, $4); + } + | f_block_arg + { + $$ = new_args(p, 0, 0, 0, 0, $1); + } + | /* none */ + { + local_add_f(p, 0); + $$ = new_args(p, 0, 0, 0, 0, 0); + } + ; + +f_bad_arg : tCONSTANT + { + yyerror(p, "formal argument cannot be a constant"); + $$ = 0; + } + | tIVAR + { + yyerror(p, "formal argument cannot be an instance variable"); + $$ = 0; + } + | tGVAR + { + yyerror(p, "formal argument cannot be a global variable"); + $$ = 0; + } + | tCVAR + { + yyerror(p, "formal argument cannot be a class variable"); + $$ = 0; + } + ; + +f_norm_arg : f_bad_arg + { + $$ = 0; + } + | tIDENTIFIER + { + local_add_f(p, $1); + $$ = $1; + } + ; + +f_arg_item : f_norm_arg + { + $$ = new_arg(p, $1); + } + | tLPAREN f_margs rparen + { + $$ = new_masgn(p, $2, 0); + } + ; + +f_arg : f_arg_item + { + $$ = list1($1); + } + | f_arg ',' f_arg_item + { + $$ = push($1, $3); + } + ; + +f_opt_asgn : tIDENTIFIER '=' + { + local_add_f(p, $1); + $$ = $1; + } + ; + +f_opt : f_opt_asgn arg + { + void_expr_error(p, $2); + $$ = cons(nsym($1), $2); + } + ; + +f_block_opt : f_opt_asgn primary_value + { + void_expr_error(p, $2); + $$ = cons(nsym($1), $2); + } + ; + +f_block_optarg : f_block_opt + { + $$ = list1($1); + } + | f_block_optarg ',' f_block_opt + { + $$ = push($1, $3); + } + ; + +f_optarg : f_opt + { + $$ = list1($1); + } + | f_optarg ',' f_opt + { + $$ = push($1, $3); + } + ; + +restarg_mark : '*' + | tSTAR + ; + +f_rest_arg : restarg_mark tIDENTIFIER + { + local_add_f(p, $2); + $$ = $2; + } + | restarg_mark + { + local_add_f(p, 0); + $$ = -1; + } + ; + +blkarg_mark : '&' + | tAMPER + ; + +f_block_arg : blkarg_mark tIDENTIFIER + { + local_add_f(p, $2); + $$ = $2; + } + ; + +opt_f_block_arg : ',' f_block_arg + { + $$ = $2; + } + | none + { + local_add_f(p, 0); + $$ = 0; + } + ; + +singleton : var_ref + { + $$ = $1; + if (!$$) $$ = new_nil(p); + } + | '(' {p->lstate = EXPR_BEG;} expr rparen + { + if ($3 == 0) { + yyerror(p, "can't define singleton method for ()."); + } + else { + switch ((enum node_type)intn($3->car)) { + case NODE_STR: + case NODE_DSTR: + case NODE_XSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_MATCH: + case NODE_FLOAT: + case NODE_ARRAY: + case NODE_HEREDOC: + yyerror(p, "can't define singleton method for literals"); + default: + break; + } + } + $$ = $3; + } + ; + +assoc_list : none + | assocs trailer + { + $$ = $1; + } + ; + +assocs : assoc + { + $$ = list1($1); + NODE_LINENO($$, $1); + } + | assocs ',' assoc + { + $$ = push($1, $3); + } + ; + +assoc : arg tASSOC arg + { + void_expr_error(p, $1); + void_expr_error(p, $3); + $$ = cons($1, $3); + } + | tLABEL arg + { + void_expr_error(p, $2); + $$ = cons(new_sym(p, $1), $2); + } + | tLABEL_END arg + { + void_expr_error(p, $2); + $$ = cons(new_sym(p, new_strsym(p, $1)), $2); + } + | tSTRING_BEG tLABEL_END arg + { + void_expr_error(p, $3); + $$ = cons(new_sym(p, new_strsym(p, $2)), $3); + } + | tSTRING_BEG string_rep tLABEL_END arg + { + void_expr_error(p, $4); + $$ = cons(new_dsym(p, push($2, $3)), $4); + } + ; + +operation : tIDENTIFIER + | tCONSTANT + | tFID + ; + +operation2 : tIDENTIFIER + | tCONSTANT + | tFID + | op + ; + +operation3 : tIDENTIFIER + | tFID + | op + ; + +dot_or_colon : '.' + | tCOLON2 + ; + +call_op : '.' + { + $$ = '.'; + } + | tANDDOT + { + $$ = 0; + } + ; + +call_op2 : call_op + | tCOLON2 + { + $$ = tCOLON2; + } + ; + +opt_terms : /* none */ + | terms + ; + +opt_nl : /* none */ + | nl + ; + +rparen : opt_nl ')' + ; + +rbracket : opt_nl ']' + ; + +trailer : /* none */ + | nl + | comma + ; + +term : ';' {yyerrok;} + | nl + | heredoc_body + ; + +nl : '\n' + { + p->lineno++; + p->column = 0; + } + ; + +terms : term + | terms term + ; + +none : /* none */ + { + $$ = 0; + } + ; +%% +#define pylval (*((YYSTYPE*)(p->ylval))) + +static void +yyerror(parser_state *p, const char *s) +{ + char* c; + size_t n; + + if (! p->capture_errors) { +#ifndef MRB_DISABLE_STDIO + if (p->filename) { + fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); + } + else { + fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); + } +#endif + } + else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { + n = strlen(s); + c = (char *)parser_palloc(p, n + 1); + memcpy(c, s, n + 1); + p->error_buffer[p->nerr].message = c; + p->error_buffer[p->nerr].lineno = p->lineno; + p->error_buffer[p->nerr].column = p->column; + } + p->nerr++; +} + +static void +yyerror_i(parser_state *p, const char *fmt, int i) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), fmt, i); + yyerror(p, buf); +} + +static void +yywarn(parser_state *p, const char *s) +{ + char* c; + size_t n; + + if (! p->capture_errors) { +#ifndef MRB_DISABLE_STDIO + if (p->filename) { + fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); + } + else { + fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); + } +#endif + } + else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { + n = strlen(s); + c = (char *)parser_palloc(p, n + 1); + memcpy(c, s, n + 1); + p->warn_buffer[p->nwarn].message = c; + p->warn_buffer[p->nwarn].lineno = p->lineno; + p->warn_buffer[p->nwarn].column = p->column; + } + p->nwarn++; +} + +static void +yywarning(parser_state *p, const char *s) +{ + yywarn(p, s); +} + +static void +yywarning_s(parser_state *p, const char *fmt, const char *s) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), fmt, s); + yywarning(p, buf); +} + +static void +backref_error(parser_state *p, node *n) +{ + int c; + + c = (int)(intptr_t)n->car; + + if (c == NODE_NTH_REF) { + yyerror_i(p, "can't set variable $%" MRB_PRId, (mrb_int)(intptr_t)n->cdr); + } + else if (c == NODE_BACK_REF) { + yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); + } + else { + mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); + } +} + +static void +void_expr_error(parser_state *p, node *n) +{ + int c; + + if (n == NULL) return; + c = (int)(intptr_t)n->car; + switch (c) { + case NODE_BREAK: + case NODE_RETURN: + case NODE_NEXT: + case NODE_REDO: + case NODE_RETRY: + yyerror(p, "void value expression"); + break; + case NODE_AND: + case NODE_OR: + void_expr_error(p, n->cdr->car); + void_expr_error(p, n->cdr->cdr); + break; + case NODE_BEGIN: + if (n->cdr) { + while (n->cdr) { + n = n->cdr; + } + void_expr_error(p, n->car); + } + break; + default: + break; + } +} + +static void pushback(parser_state *p, int c); +static mrb_bool peeks(parser_state *p, const char *s); +static mrb_bool skips(parser_state *p, const char *s); + +static inline int +nextc(parser_state *p) +{ + int c; + + if (p->pb) { + node *tmp; + + c = (int)(intptr_t)p->pb->car; + tmp = p->pb; + p->pb = p->pb->cdr; + cons_free(tmp); + } + else { +#ifndef MRB_DISABLE_STDIO + if (p->f) { + if (feof(p->f)) goto eof; + c = fgetc(p->f); + if (c == EOF) goto eof; + } + else +#endif + if (!p->s || p->s >= p->send) { + goto eof; + } + else { + c = (unsigned char)*p->s++; + } + } + if (c >= 0) { + p->column++; + } + if (c == '\r') { + c = nextc(p); + if (c != '\n') { + pushback(p, c); + return '\r'; + } + return c; + } + return c; + + eof: + if (!p->cxt) return -1; + else { + if (p->cxt->partial_hook(p) < 0) + return -1; /* end of program(s) */ + return -2; /* end of a file in the program files */ + } +} + +static void +pushback(parser_state *p, int c) +{ + if (c >= 0) { + p->column--; + } + p->pb = cons((node*)(intptr_t)c, p->pb); +} + +static void +skip(parser_state *p, char term) +{ + int c; + + for (;;) { + c = nextc(p); + if (c < 0) break; + if (c == term) break; + } +} + +static int +peekc_n(parser_state *p, int n) +{ + node *list = 0; + int c0; + + do { + c0 = nextc(p); + if (c0 == -1) return c0; /* do not skip partial EOF */ + if (c0 >= 0) --p->column; + list = push(list, (node*)(intptr_t)c0); + } while(n--); + if (p->pb) { + p->pb = append((node*)list, p->pb); + } + else { + p->pb = list; + } + return c0; +} + +static mrb_bool +peek_n(parser_state *p, int c, int n) +{ + return peekc_n(p, n) == c && c >= 0; +} +#define peek(p,c) peek_n((p), (c), 0) + +static mrb_bool +peeks(parser_state *p, const char *s) +{ + size_t len = strlen(s); + +#ifndef MRB_DISABLE_STDIO + if (p->f) { + int n = 0; + while (*s) { + if (!peek_n(p, *s++, n++)) return FALSE; + } + return TRUE; + } + else +#endif + if (p->s && p->s + len <= p->send) { + if (memcmp(p->s, s, len) == 0) return TRUE; + } + return FALSE; +} + +static mrb_bool +skips(parser_state *p, const char *s) +{ + int c; + + for (;;) { + /* skip until first char */ + for (;;) { + c = nextc(p); + if (c < 0) return FALSE; + if (c == '\n') { + p->lineno++; + p->column = 0; + } + if (c == *s) break; + } + s++; + if (peeks(p, s)) { + size_t len = strlen(s); + + while (len--) { + if (nextc(p) == '\n') { + p->lineno++; + p->column = 0; + } + } + return TRUE; + } + else{ + s--; + } + } + return FALSE; +} + + +static int +newtok(parser_state *p) +{ + if (p->tokbuf != p->buf) { + mrb_free(p->mrb, p->tokbuf); + p->tokbuf = p->buf; + p->tsiz = MRB_PARSER_TOKBUF_SIZE; + } + p->tidx = 0; + return p->column - 1; +} + +static void +tokadd(parser_state *p, int32_t c) +{ + char utf8[4]; + int i, len; + + /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ + if (c >= 0) { + /* Single byte from source or non-Unicode escape */ + utf8[0] = (char)c; + len = 1; + } + else { + /* Unicode character */ + c = -c; + if (c < 0x80) { + utf8[0] = (char)c; + len = 1; + } + else if (c < 0x800) { + utf8[0] = (char)(0xC0 | (c >> 6)); + utf8[1] = (char)(0x80 | (c & 0x3F)); + len = 2; + } + else if (c < 0x10000) { + utf8[0] = (char)(0xE0 | (c >> 12) ); + utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ( c & 0x3F)); + len = 3; + } + else { + utf8[0] = (char)(0xF0 | (c >> 18) ); + utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); + utf8[3] = (char)(0x80 | ( c & 0x3F)); + len = 4; + } + } + if (p->tidx+len >= p->tsiz) { + if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) { + p->tidx += len; + return; + } + p->tsiz *= 2; + if (p->tokbuf == p->buf) { + p->tokbuf = (char*)mrb_malloc(p->mrb, p->tsiz); + memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE); + } + else { + p->tokbuf = (char*)mrb_realloc(p->mrb, p->tokbuf, p->tsiz); + } + } + for (i = 0; i < len; i++) { + p->tokbuf[p->tidx++] = utf8[i]; + } +} + +static int +toklast(parser_state *p) +{ + return p->tokbuf[p->tidx-1]; +} + +static void +tokfix(parser_state *p) +{ + if (p->tidx >= MRB_PARSER_TOKBUF_MAX) { + p->tidx = MRB_PARSER_TOKBUF_MAX-1; + yyerror(p, "string too long (truncated)"); + } + p->tokbuf[p->tidx] = '\0'; +} + +static const char* +tok(parser_state *p) +{ + return p->tokbuf; +} + +static int +toklen(parser_state *p) +{ + return p->tidx; +} + +#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) +#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) +#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) +#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) +#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) +#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) + +static int32_t +scan_oct(const int *start, int len, int *retlen) +{ + const int *s = start; + int32_t retval = 0; + + /* mrb_assert(len <= 3) */ + while (len-- && *s >= '0' && *s <= '7') { + retval <<= 3; + retval |= *s++ - '0'; + } + *retlen = (int)(s - start); + + return retval; +} + +static int32_t +scan_hex(parser_state *p, const int *start, int len, int *retlen) +{ + static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; + const int *s = start; + uint32_t retval = 0; + char *tmp; + + /* mrb_assert(len <= 8) */ + while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { + retval <<= 4; + retval |= (tmp - hexdigit) & 15; + s++; + } + *retlen = (int)(s - start); + + return (int32_t)retval; +} + +static int32_t +read_escape_unicode(parser_state *p, int limit) +{ + int buf[9]; + int i; + int32_t hex; + + /* Look for opening brace */ + i = 0; + buf[0] = nextc(p); + if (buf[0] < 0) { + eof: + yyerror(p, "invalid escape character syntax"); + return -1; + } + if (ISXDIGIT(buf[0])) { + /* \uxxxx form */ + for (i=1; i<limit; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (!ISXDIGIT(buf[i])) { + pushback(p, buf[i]); + break; + } + } + } + else { + pushback(p, buf[0]); + } + hex = scan_hex(p, buf, i, &i); + if (i == 0 || hex > 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) { + yyerror(p, "invalid Unicode code point"); + return -1; + } + return hex; +} + +/* Return negative to indicate Unicode code point */ +static int32_t +read_escape(parser_state *p) +{ + int32_t c; + + switch (c = nextc(p)) { + case '\\':/* Backslash */ + return c; + + case 'n':/* newline */ + return '\n'; + + case 't':/* horizontal tab */ + return '\t'; + + case 'r':/* carriage-return */ + return '\r'; + + case 'f':/* form-feed */ + return '\f'; + + case 'v':/* vertical tab */ + return '\13'; + + case 'a':/* alarm(bell) */ + return '\007'; + + case 'e':/* escape */ + return 033; + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + { + int buf[3]; + int i; + + buf[0] = c; + for (i=1; i<3; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (buf[i] < '0' || '7' < buf[i]) { + pushback(p, buf[i]); + break; + } + } + c = scan_oct(buf, i, &i); + } + return c; + + case 'x': /* hex constant */ + { + int buf[2]; + int i; + + for (i=0; i<2; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (!ISXDIGIT(buf[i])) { + pushback(p, buf[i]); + break; + } + } + if (i == 0) { + yyerror(p, "invalid hex escape"); + return -1; + } + return scan_hex(p, buf, i, &i); + } + + case 'u': /* Unicode */ + if (peek(p, '{')) { + /* \u{xxxxxxxx} form */ + nextc(p); + c = read_escape_unicode(p, 8); + if (c < 0) return 0; + if (nextc(p) != '}') goto eof; + } + else { + c = read_escape_unicode(p, 4); + if (c < 0) return 0; + } + return -c; + + case 'b':/* backspace */ + return '\010'; + + case 's':/* space */ + return ' '; + + case 'M': + if ((c = nextc(p)) != '-') { + yyerror(p, "Invalid escape character syntax"); + pushback(p, c); + return '\0'; + } + if ((c = nextc(p)) == '\\') { + return read_escape(p) | 0x80; + } + else if (c < 0) goto eof; + else { + return ((c & 0xff) | 0x80); + } + + case 'C': + if ((c = nextc(p)) != '-') { + yyerror(p, "Invalid escape character syntax"); + pushback(p, c); + return '\0'; + } + case 'c': + if ((c = nextc(p))== '\\') { + c = read_escape(p); + } + else if (c == '?') + return 0177; + else if (c < 0) goto eof; + return c & 0x9f; + + eof: + case -1: + case -2: /* end of a file */ + yyerror(p, "Invalid escape character syntax"); + return '\0'; + + default: + return c; + } +} + +static int +parse_string(parser_state *p) +{ + int c; + string_type type = (string_type)(intptr_t)p->lex_strterm->car; + int nest_level = intn(p->lex_strterm->cdr->car); + int beg = intn(p->lex_strterm->cdr->cdr->car); + int end = intn(p->lex_strterm->cdr->cdr->cdr); + parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; + int cmd_state = p->cmd_start; + + if (beg == 0) beg = -3; /* should never happen */ + if (end == 0) end = -3; + newtok(p); + while ((c = nextc(p)) != end || nest_level != 0) { + if (hinf && (c == '\n' || c < 0)) { + mrb_bool line_head; + tokadd(p, '\n'); + tokfix(p); + p->lineno++; + p->column = 0; + line_head = hinf->line_head; + hinf->line_head = TRUE; + if (line_head) { + /* check whether end of heredoc */ + const char *s = tok(p); + int len = toklen(p); + if (hinf->allow_indent) { + while (ISSPACE(*s) && len > 0) { + ++s; + --len; + } + } + if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { + if (c < 0) { + p->parsing_heredoc = NULL; + } + else { + return tHEREDOC_END; + } + } + } + if (c < 0) { + char buf[256]; + snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term); + yyerror(p, buf); + return 0; + } + pylval.nd = new_str(p, tok(p), toklen(p)); + return tHD_STRING_MID; + } + if (c < 0) { + yyerror(p, "unterminated string meets end of file"); + return 0; + } + else if (c == beg) { + nest_level++; + p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; + } + else if (c == end) { + nest_level--; + p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; + } + else if (c == '\\') { + c = nextc(p); + if (type & STR_FUNC_EXPAND) { + if (c == end || c == beg) { + tokadd(p, c); + } + else if (c == '\n') { + p->lineno++; + p->column = 0; + if (type & STR_FUNC_ARRAY) { + tokadd(p, '\n'); + } + } + else if (type & STR_FUNC_REGEXP) { + tokadd(p, '\\'); + tokadd(p, c); + } + else if (c == 'u' && peek(p, '{')) { + /* \u{xxxx xxxx xxxx} form */ + nextc(p); + while (1) { + do c = nextc(p); while (ISSPACE(c)); + if (c == '}') break; + pushback(p, c); + c = read_escape_unicode(p, 8); + if (c < 0) break; + tokadd(p, -c); + } + if (hinf) + hinf->line_head = FALSE; + } + else { + pushback(p, c); + tokadd(p, read_escape(p)); + if (hinf) + hinf->line_head = FALSE; + } + } + else { + if (c != beg && c != end) { + if (c == '\n') { + p->lineno++; + p->column = 0; + } + if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { + tokadd(p, '\\'); + } + } + tokadd(p, c); + } + continue; + } + else if ((c == '#') && (type & STR_FUNC_EXPAND)) { + c = nextc(p); + if (c == '{') { + tokfix(p); + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + pylval.nd = new_str(p, tok(p), toklen(p)); + if (hinf) { + hinf->line_head = FALSE; + return tHD_STRING_PART; + } + return tSTRING_PART; + } + tokadd(p, '#'); + pushback(p, c); + continue; + } + if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { + if (toklen(p) == 0) { + do { + if (c == '\n') { + p->lineno++; + p->column = 0; + heredoc_treat_nextline(p); + if (p->parsing_heredoc != NULL) { + return tHD_LITERAL_DELIM; + } + } + c = nextc(p); + } while (ISSPACE(c)); + pushback(p, c); + return tLITERAL_DELIM; + } + else { + pushback(p, c); + tokfix(p); + pylval.nd = new_str(p, tok(p), toklen(p)); + return tSTRING_MID; + } + } + if (c == '\n') { + p->lineno++; + p->column = 0; + } + tokadd(p, c); + } + + tokfix(p); + p->lstate = EXPR_END; + end_strterm(p); + + if (type & STR_FUNC_XQUOTE) { + pylval.nd = new_xstr(p, tok(p), toklen(p)); + return tXSTRING; + } + + if (type & STR_FUNC_REGEXP) { + int f = 0; + int re_opt; + char *s = strndup(tok(p), toklen(p)); + char flags[3]; + char *flag = flags; + char enc = '\0'; + char *encp; + char *dup; + + newtok(p); + while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { + switch (re_opt) { + case 'i': f |= 1; break; + case 'x': f |= 2; break; + case 'm': f |= 4; break; + case 'u': f |= 16; break; + case 'n': f |= 32; break; + default: tokadd(p, re_opt); break; + } + } + pushback(p, re_opt); + if (toklen(p)) { + char msg[128]; + tokfix(p); + snprintf(msg, sizeof(msg), "unknown regexp option%s - %s", + toklen(p) > 1 ? "s" : "", tok(p)); + yyerror(p, msg); + } + if (f != 0) { + if (f & 1) *flag++ = 'i'; + if (f & 2) *flag++ = 'x'; + if (f & 4) *flag++ = 'm'; + if (f & 16) enc = 'u'; + if (f & 32) enc = 'n'; + } + if (flag > flags) { + dup = strndup(flags, (size_t)(flag - flags)); + } + else { + dup = NULL; + } + if (enc) { + encp = strndup(&enc, 1); + } + else { + encp = NULL; + } + pylval.nd = new_regx(p, s, dup, encp); + + return tREGEXP; + } + pylval.nd = new_str(p, tok(p), toklen(p)); + if (IS_LABEL_POSSIBLE()) { + if (IS_LABEL_SUFFIX(0)) { + p->lstate = EXPR_BEG; + nextc(p); + return tLABEL_END; + } + } + + return tSTRING; +} + + +static int +heredoc_identifier(parser_state *p) +{ + int c; + int type = str_heredoc; + mrb_bool indent = FALSE; + mrb_bool quote = FALSE; + node *newnode; + parser_heredoc_info *info; + + c = nextc(p); + if (ISSPACE(c) || c == '=') { + pushback(p, c); + return 0; + } + if (c == '-') { + indent = TRUE; + c = nextc(p); + } + if (c == '\'' || c == '"') { + int term = c; + if (c == '\'') + quote = TRUE; + newtok(p); + while ((c = nextc(p)) >= 0 && c != term) { + if (c == '\n') { + c = -1; + break; + } + tokadd(p, c); + } + if (c < 0) { + yyerror(p, "unterminated here document identifier"); + return 0; + } + } + else { + if (c < 0) { + return 0; /* missing here document identifier */ + } + if (! identchar(c)) { + pushback(p, c); + if (indent) pushback(p, '-'); + return 0; + } + newtok(p); + do { + tokadd(p, c); + } while ((c = nextc(p)) >= 0 && identchar(c)); + pushback(p, c); + } + tokfix(p); + newnode = new_heredoc(p); + info = (parser_heredoc_info*)newnode->cdr; + info->term = strndup(tok(p), toklen(p)); + info->term_len = toklen(p); + if (! quote) + type |= STR_FUNC_EXPAND; + info->type = (string_type)type; + info->allow_indent = indent; + info->line_head = TRUE; + info->doc = NULL; + p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); + p->lstate = EXPR_END; + + pylval.nd = newnode; + return tHEREDOC_BEG; +} + +static int +arg_ambiguous(parser_state *p) +{ + yywarning(p, "ambiguous first argument; put parentheses or even spaces"); + return 1; +} + +#include "lex.def" + +static int +parser_yylex(parser_state *p) +{ + int32_t c; + int space_seen = 0; + int cmd_state; + enum mrb_lex_state_enum last_state; + int token_column; + + if (p->lex_strterm) { + if (is_strterm_type(p, STR_FUNC_HEREDOC)) { + if (p->parsing_heredoc != NULL) + return parse_string(p); + } + else + return parse_string(p); + } + cmd_state = p->cmd_start; + p->cmd_start = FALSE; + retry: + last_state = p->lstate; + switch (c = nextc(p)) { + case '\004': /* ^D */ + case '\032': /* ^Z */ + case '\0': /* NUL */ + case -1: /* end of script. */ + if (p->heredocs_from_nextline) + goto maybe_heredoc; + return 0; + + /* white spaces */ + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen = 1; + goto retry; + + case '#': /* it's a comment */ + skip(p, '\n'); + /* fall through */ + case -2: /* end of a file */ + case '\n': + maybe_heredoc: + heredoc_treat_nextline(p); + switch (p->lstate) { + case EXPR_BEG: + case EXPR_FNAME: + case EXPR_DOT: + case EXPR_CLASS: + case EXPR_VALUE: + p->lineno++; + p->column = 0; + if (p->parsing_heredoc != NULL) { + if (p->lex_strterm) { + return parse_string(p); + } + } + goto retry; + default: + break; + } + if (p->parsing_heredoc != NULL) { + return '\n'; + } + while ((c = nextc(p))) { + switch (c) { + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen = 1; + break; + case '.': + if ((c = nextc(p)) != '.') { + pushback(p, c); + pushback(p, '.'); + goto retry; + } + case -1: /* EOF */ + case -2: /* end of a file */ + goto normal_newline; + default: + pushback(p, c); + goto normal_newline; + } + } + normal_newline: + p->cmd_start = TRUE; + p->lstate = EXPR_BEG; + return '\n'; + + case '*': + if ((c = nextc(p)) == '*') { + if ((c = nextc(p)) == '=') { + pylval.id = intern("**",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + c = tPOW; + } + else { + if (c == '=') { + pylval.id = intern_c('*'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + yywarning(p, "'*' interpreted as argument prefix"); + c = tSTAR; + } + else if (IS_BEG()) { + c = tSTAR; + } + else { + c = '*'; + } + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return c; + + case '!': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return '!'; + } + } + else { + p->lstate = EXPR_BEG; + } + if (c == '=') { + return tNEQ; + } + if (c == '~') { + return tNMATCH; + } + pushback(p, c); + return '!'; + + case '=': + if (p->column == 1) { + static const char begin[] = "begin"; + static const char end[] = "\n=end"; + if (peeks(p, begin)) { + c = peekc_n(p, sizeof(begin)-1); + if (c < 0 || ISSPACE(c)) { + do { + if (!skips(p, end)) { + yyerror(p, "embedded document meets end of file"); + return 0; + } + c = nextc(p); + } while (!(c < 0 || ISSPACE(c))); + if (c != '\n') skip(p, '\n'); + p->lineno++; + p->column = 0; + goto retry; + } + } + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + if ((c = nextc(p)) == '=') { + if ((c = nextc(p)) == '=') { + return tEQQ; + } + pushback(p, c); + return tEQ; + } + if (c == '~') { + return tMATCH; + } + else if (c == '>') { + return tASSOC; + } + pushback(p, c); + return '='; + + case '<': + c = nextc(p); + if (c == '<' && + p->lstate != EXPR_DOT && + p->lstate != EXPR_CLASS && + !IS_END() && + (!IS_ARG() || space_seen)) { + int token = heredoc_identifier(p); + if (token) + return token; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + if (p->lstate == EXPR_CLASS) { + p->cmd_start = TRUE; + } + } + if (c == '=') { + if ((c = nextc(p)) == '>') { + return tCMP; + } + pushback(p, c); + return tLEQ; + } + if (c == '<') { + if ((c = nextc(p)) == '=') { + pylval.id = intern("<<",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tLSHFT; + } + pushback(p, c); + return '<'; + + case '>': + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + if ((c = nextc(p)) == '=') { + return tGEQ; + } + if (c == '>') { + if ((c = nextc(p)) == '=') { + pylval.id = intern(">>",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tRSHFT; + } + pushback(p, c); + return '>'; + + case '"': + p->lex_strterm = new_strterm(p, str_dquote, '"', 0); + return tSTRING_BEG; + + case '\'': + p->lex_strterm = new_strterm(p, str_squote, '\'', 0); + return parse_string(p); + + case '`': + if (p->lstate == EXPR_FNAME) { + p->lstate = EXPR_ENDFN; + return '`'; + } + if (p->lstate == EXPR_DOT) { + if (cmd_state) + p->lstate = EXPR_CMDARG; + else + p->lstate = EXPR_ARG; + return '`'; + } + p->lex_strterm = new_strterm(p, str_xquote, '`', 0); + return tXSTRING_BEG; + + case '?': + if (IS_END()) { + p->lstate = EXPR_VALUE; + return '?'; + } + c = nextc(p); + if (c < 0) { + yyerror(p, "incomplete character syntax"); + return 0; + } + if (ISSPACE(c)) { + if (!IS_ARG()) { + int c2; + switch (c) { + case ' ': + c2 = 's'; + break; + case '\n': + c2 = 'n'; + break; + case '\t': + c2 = 't'; + break; + case '\v': + c2 = 'v'; + break; + case '\r': + c2 = 'r'; + break; + case '\f': + c2 = 'f'; + break; + default: + c2 = 0; + break; + } + if (c2) { + char buf[256]; + snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2); + yyerror(p, buf); + } + } + ternary: + pushback(p, c); + p->lstate = EXPR_VALUE; + return '?'; + } + newtok(p); + /* need support UTF-8 if configured */ + if ((isalnum(c) || c == '_')) { + int c2 = nextc(p); + pushback(p, c2); + if ((isalnum(c2) || c2 == '_')) { + goto ternary; + } + } + if (c == '\\') { + c = read_escape(p); + tokadd(p, c); + } + else { + tokadd(p, c); + } + tokfix(p); + pylval.nd = new_str(p, tok(p), toklen(p)); + p->lstate = EXPR_END; + return tCHAR; + + case '&': + if ((c = nextc(p)) == '&') { + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '=') { + pylval.id = intern("&&",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tANDOP; + } + else if (c == '.') { + p->lstate = EXPR_DOT; + return tANDDOT; + } + else if (c == '=') { + pylval.id = intern_c('&'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + yywarning(p, "'&' interpreted as argument prefix"); + c = tAMPER; + } + else if (IS_BEG()) { + c = tAMPER; + } + else { + c = '&'; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return c; + + case '|': + if ((c = nextc(p)) == '|') { + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '=') { + pylval.id = intern("||",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tOROP; + } + if (c == '=') { + pylval.id = intern_c('|'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '|'; + + case '+': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return tUPLUS; + } + pushback(p, c); + return '+'; + } + if (c == '=') { + pylval.id = intern_c('+'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { + p->lstate = EXPR_BEG; + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + c = '+'; + goto start_num; + } + return tUPLUS; + } + p->lstate = EXPR_BEG; + pushback(p, c); + return '+'; + + case '-': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return tUMINUS; + } + pushback(p, c); + return '-'; + } + if (c == '=') { + pylval.id = intern_c('-'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (c == '>') { + p->lstate = EXPR_ENDFN; + return tLAMBDA; + } + if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { + p->lstate = EXPR_BEG; + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + return tUMINUS_NUM; + } + return tUMINUS; + } + p->lstate = EXPR_BEG; + pushback(p, c); + return '-'; + + case '.': + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '.') { + if ((c = nextc(p)) == '.') { + return tDOT3; + } + pushback(p, c); + return tDOT2; + } + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + yyerror(p, "no .<digit> floating literal anymore; put 0 before dot"); + } + p->lstate = EXPR_DOT; + return '.'; + + start_num: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int is_float, seen_point, seen_e, nondigit; + + is_float = seen_point = seen_e = nondigit = 0; + p->lstate = EXPR_END; + newtok(p); + if (c == '-' || c == '+') { + tokadd(p, c); + c = nextc(p); + } + if (c == '0') { +#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0) + int start = toklen(p); + c = nextc(p); + if (c == 'x' || c == 'X') { + /* hexadecimal */ + c = nextc(p); + if (c >= 0 && ISXDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISXDIGIT(c)) break; + nondigit = 0; + tokadd(p, tolower(c)); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + pylval.nd = new_int(p, tok(p), 16); + return tINTEGER; + } + if (c == 'b' || c == 'B') { + /* binary */ + c = nextc(p); + if (c == '0' || c == '1') { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c != '0' && c != '1') break; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + pylval.nd = new_int(p, tok(p), 2); + return tINTEGER; + } + if (c == 'd' || c == 'D') { + /* decimal */ + c = nextc(p); + if (c >= 0 && ISDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISDIGIT(c)) break; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + pylval.nd = new_int(p, tok(p), 10); + return tINTEGER; + } + if (c == '_') { + /* 0_0 */ + goto octal_number; + } + if (c == 'o' || c == 'O') { + /* prefixed octal */ + c = nextc(p); + if (c < 0 || c == '_' || !ISDIGIT(c)) { + no_digits(); + } + } + if (c >= '0' && c <= '7') { + /* octal */ + octal_number: + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c < '0' || c > '9') break; + if (c > '7') goto invalid_octal; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + + if (toklen(p) > start) { + pushback(p, c); + tokfix(p); + if (nondigit) goto trailing_uc; + pylval.nd = new_int(p, tok(p), 8); + return tINTEGER; + } + if (nondigit) { + pushback(p, c); + goto trailing_uc; + } + } + if (c > '7' && c <= '9') { + invalid_octal: + yyerror(p, "Invalid octal digit"); + } + else if (c == '.' || c == 'e' || c == 'E') { + tokadd(p, '0'); + } + else { + pushback(p, c); + pylval.nd = new_int(p, "0", 10); + return tINTEGER; + } + } + + for (;;) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + nondigit = 0; + tokadd(p, c); + break; + + case '.': + if (nondigit) goto trailing_uc; + if (seen_point || seen_e) { + goto decode_num; + } + else { + int c0 = nextc(p); + if (c0 < 0 || !ISDIGIT(c0)) { + pushback(p, c0); + goto decode_num; + } + c = c0; + } + tokadd(p, '.'); + tokadd(p, c); + is_float++; + seen_point++; + nondigit = 0; + break; + + case 'e': + case 'E': + if (nondigit) { + pushback(p, c); + c = nondigit; + goto decode_num; + } + if (seen_e) { + goto decode_num; + } + tokadd(p, c); + seen_e++; + is_float++; + nondigit = c; + c = nextc(p); + if (c != '-' && c != '+') continue; + tokadd(p, c); + nondigit = c; + break; + + case '_': /* '_' in number just ignored */ + if (nondigit) goto decode_num; + nondigit = c; + break; + + default: + goto decode_num; + } + c = nextc(p); + } + + decode_num: + pushback(p, c); + if (nondigit) { + trailing_uc: + yyerror_i(p, "trailing '%c' in number", nondigit); + } + tokfix(p); + if (is_float) { + double d; + char *endp; + + errno = 0; + d = mrb_float_read(tok(p), &endp); + if (d == 0 && endp == tok(p)) { + yywarning_s(p, "corrupted float value %s", tok(p)); + } + else if (errno == ERANGE) { + yywarning_s(p, "float %s out of range", tok(p)); + errno = 0; + } + pylval.nd = new_float(p, tok(p)); + return tFLOAT; + } + pylval.nd = new_int(p, tok(p), 10); + return tINTEGER; + } + + case ')': + case ']': + p->paren_nest--; + /* fall through */ + case '}': + COND_LEXPOP(); + CMDARG_LEXPOP(); + if (c == ')') + p->lstate = EXPR_ENDFN; + else + p->lstate = EXPR_ENDARG; + return c; + + case ':': + c = nextc(p); + if (c == ':') { + if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { + p->lstate = EXPR_BEG; + return tCOLON3; + } + p->lstate = EXPR_DOT; + return tCOLON2; + } + if (IS_END() || ISSPACE(c)) { + pushback(p, c); + p->lstate = EXPR_BEG; + return ':'; + } + pushback(p, c); + p->lstate = EXPR_FNAME; + return tSYMBEG; + + case '/': + if (IS_BEG()) { + p->lex_strterm = new_strterm(p, str_regexp, '/', 0); + return tREGEXP_BEG; + } + if ((c = nextc(p)) == '=') { + pylval.id = intern_c('/'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + p->lex_strterm = new_strterm(p, str_regexp, '/', 0); + return tREGEXP_BEG; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return '/'; + + case '^': + if ((c = nextc(p)) == '=') { + pylval.id = intern_c('^'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '^'; + + case ';': + p->lstate = EXPR_BEG; + return ';'; + + case ',': + p->lstate = EXPR_BEG; + return ','; + + case '~': + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + if ((c = nextc(p)) != '@') { + pushback(p, c); + } + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return '~'; + + case '(': + if (IS_BEG()) { + c = tLPAREN; + } + else if (IS_SPCARG(-1)) { + c = tLPAREN_ARG; + } + p->paren_nest++; + COND_PUSH(0); + CMDARG_PUSH(0); + p->lstate = EXPR_BEG; + return c; + + case '[': + p->paren_nest++; + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if ((c = nextc(p)) == ']') { + if ((c = nextc(p)) == '=') { + return tASET; + } + pushback(p, c); + return tAREF; + } + pushback(p, c); + return '['; + } + else if (IS_BEG()) { + c = tLBRACK; + } + else if (IS_ARG() && space_seen) { + c = tLBRACK; + } + p->lstate = EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + return c; + + case '{': + if (p->lpar_beg && p->lpar_beg == p->paren_nest) { + p->lstate = EXPR_BEG; + p->lpar_beg = 0; + p->paren_nest--; + COND_PUSH(0); + CMDARG_PUSH(0); + return tLAMBEG; + } + if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) + c = '{'; /* block (primary) */ + else if (p->lstate == EXPR_ENDARG) + c = tLBRACE_ARG; /* block (expr) */ + else + c = tLBRACE; /* hash */ + COND_PUSH(0); + CMDARG_PUSH(0); + p->lstate = EXPR_BEG; + return c; + + case '\\': + c = nextc(p); + if (c == '\n') { + p->lineno++; + p->column = 0; + space_seen = 1; + goto retry; /* skip \\n */ + } + pushback(p, c); + return '\\'; + + case '%': + if (IS_BEG()) { + int term; + int paren; + + c = nextc(p); + quotation: + if (c < 0 || !ISALNUM(c)) { + term = c; + c = 'Q'; + } + else { + term = nextc(p); + if (isalnum(term)) { + yyerror(p, "unknown type of %string"); + return 0; + } + } + if (c < 0 || term < 0) { + yyerror(p, "unterminated quoted string meets end of file"); + return 0; + } + paren = term; + if (term == '(') term = ')'; + else if (term == '[') term = ']'; + else if (term == '{') term = '}'; + else if (term == '<') term = '>'; + else paren = 0; + + switch (c) { + case 'Q': + p->lex_strterm = new_strterm(p, str_dquote, term, paren); + return tSTRING_BEG; + + case 'q': + p->lex_strterm = new_strterm(p, str_squote, term, paren); + return parse_string(p); + + case 'W': + p->lex_strterm = new_strterm(p, str_dword, term, paren); + return tWORDS_BEG; + + case 'w': + p->lex_strterm = new_strterm(p, str_sword, term, paren); + return tWORDS_BEG; + + case 'x': + p->lex_strterm = new_strterm(p, str_xquote, term, paren); + return tXSTRING_BEG; + + case 'r': + p->lex_strterm = new_strterm(p, str_regexp, term, paren); + return tREGEXP_BEG; + + case 's': + p->lex_strterm = new_strterm(p, str_ssym, term, paren); + return tSYMBEG; + + case 'I': + p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); + return tSYMBOLS_BEG; + + case 'i': + p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); + return tSYMBOLS_BEG; + + default: + yyerror(p, "unknown type of %string"); + return 0; + } + } + if ((c = nextc(p)) == '=') { + pylval.id = intern_c('%'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (IS_SPCARG(c)) { + goto quotation; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '%'; + + case '$': + p->lstate = EXPR_END; + token_column = newtok(p); + c = nextc(p); + if (c < 0) { + yyerror(p, "incomplete global variable syntax"); + return 0; + } + switch (c) { + case '_': /* $_: last read line string */ + c = nextc(p); + if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ + tokadd(p, '$'); + tokadd(p, c); + break; + } + pushback(p, c); + c = '_'; + /* fall through */ + case '~': /* $~: match-data */ + case '*': /* $*: argv */ + case '$': /* $$: pid */ + case '?': /* $?: last status */ + case '!': /* $!: error string */ + case '@': /* $@: error position */ + case '/': /* $/: input record separator */ + case '\\': /* $\: output record separator */ + case ';': /* $;: field separator */ + case ',': /* $,: output field separator */ + case '.': /* $.: last read line number */ + case '=': /* $=: ignorecase */ + case ':': /* $:: load path */ + case '<': /* $<: reading filename */ + case '>': /* $>: default output handle */ + case '\"': /* $": already loaded files */ + tokadd(p, '$'); + tokadd(p, c); + tokfix(p); + pylval.id = intern_cstr(tok(p)); + return tGVAR; + + case '-': + tokadd(p, '$'); + tokadd(p, c); + c = nextc(p); + pushback(p, c); + gvar: + tokfix(p); + pylval.id = intern_cstr(tok(p)); + return tGVAR; + + case '&': /* $&: last match */ + case '`': /* $`: string before last match */ + case '\'': /* $': string after last match */ + case '+': /* $+: string matches last pattern */ + if (last_state == EXPR_FNAME) { + tokadd(p, '$'); + tokadd(p, c); + goto gvar; + } + pylval.nd = new_back_ref(p, c); + return tBACK_REF; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + do { + tokadd(p, c); + c = nextc(p); + } while (c >= 0 && isdigit(c)); + pushback(p, c); + if (last_state == EXPR_FNAME) goto gvar; + tokfix(p); + { + unsigned long n = strtoul(tok(p), NULL, 10); + if (n > INT_MAX) { + yyerror_i(p, "capture group index must be <= %d", INT_MAX); + return 0; + } + pylval.nd = new_nth_ref(p, (int)n); + } + return tNTH_REF; + + default: + if (!identchar(c)) { + pushback(p, c); + return '$'; + } + /* fall through */ + case '0': + tokadd(p, '$'); + } + break; + + case '@': + c = nextc(p); + token_column = newtok(p); + tokadd(p, '@'); + if (c == '@') { + tokadd(p, '@'); + c = nextc(p); + } + if (c < 0) { + if (p->tidx == 1) { + yyerror(p, "incomplete instance variable syntax"); + } + else { + yyerror(p, "incomplete class variable syntax"); + } + return 0; + } + else if (isdigit(c)) { + if (p->tidx == 1) { + yyerror_i(p, "'@%c' is not allowed as an instance variable name", c); + } + else { + yyerror_i(p, "'@@%c' is not allowed as a class variable name", c); + } + return 0; + } + if (!identchar(c)) { + pushback(p, c); + return '@'; + } + break; + + case '_': + token_column = newtok(p); + break; + + default: + if (!identchar(c)) { + yyerror_i(p, "Invalid char '\\x%02X' in expression", c); + goto retry; + } + + token_column = newtok(p); + break; + } + + do { + tokadd(p, c); + c = nextc(p); + if (c < 0) break; + } while (identchar(c)); + if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && + strncmp(tok(p), "__END__", toklen(p)) == 0) + return -1; + + switch (tok(p)[0]) { + case '@': case '$': + pushback(p, c); + break; + default: + if ((c == '!' || c == '?') && !peek(p, '=')) { + tokadd(p, c); + } + else { + pushback(p, c); + } + } + tokfix(p); + { + int result = 0; + + switch (tok(p)[0]) { + case '$': + p->lstate = EXPR_END; + result = tGVAR; + break; + case '@': + p->lstate = EXPR_END; + if (tok(p)[1] == '@') + result = tCVAR; + else + result = tIVAR; + break; + + default: + if (toklast(p) == '!' || toklast(p) == '?') { + result = tFID; + } + else { + if (p->lstate == EXPR_FNAME) { + if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && + (!peek(p, '=') || (peek_n(p, '>', 1)))) { + result = tIDENTIFIER; + tokadd(p, c); + tokfix(p); + } + else { + pushback(p, c); + } + } + if (result == 0 && ISUPPER(tok(p)[0])) { + result = tCONSTANT; + } + else { + result = tIDENTIFIER; + } + } + + if (IS_LABEL_POSSIBLE()) { + if (IS_LABEL_SUFFIX(0)) { + p->lstate = EXPR_BEG; + nextc(p); + tokfix(p); + pylval.id = intern_cstr(tok(p)); + return tLABEL; + } + } + if (p->lstate != EXPR_DOT) { + const struct kwtable *kw; + + /* See if it is a reserved word. */ + kw = mrb_reserved_word(tok(p), toklen(p)); + if (kw) { + enum mrb_lex_state_enum state = p->lstate; + pylval.num = p->lineno; + p->lstate = kw->state; + if (state == EXPR_FNAME) { + pylval.id = intern_cstr(kw->name); + return kw->id[0]; + } + if (p->lstate == EXPR_BEG) { + p->cmd_start = TRUE; + } + if (kw->id[0] == keyword_do) { + if (p->lpar_beg && p->lpar_beg == p->paren_nest) { + p->lpar_beg = 0; + p->paren_nest--; + return keyword_do_LAMBDA; + } + if (COND_P()) return keyword_do_cond; + if (CMDARG_P() && state != EXPR_CMDARG) + return keyword_do_block; + if (state == EXPR_ENDARG || state == EXPR_BEG) + return keyword_do_block; + return keyword_do; + } + if (state == EXPR_BEG || state == EXPR_VALUE) + return kw->id[0]; + else { + if (kw->id[0] != kw->id[1]) + p->lstate = EXPR_BEG; + return kw->id[1]; + } + } + } + + if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { + if (cmd_state) { + p->lstate = EXPR_CMDARG; + } + else { + p->lstate = EXPR_ARG; + } + } + else if (p->lstate == EXPR_FNAME) { + p->lstate = EXPR_ENDFN; + } + else { + p->lstate = EXPR_END; + } + } + { + mrb_sym ident = intern_cstr(tok(p)); + + pylval.id = ident; +#if 0 + if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) { + p->lstate = EXPR_END; + } +#endif + } + return result; + } +} + +static int +yylex(void *lval, parser_state *p) +{ + p->ylval = lval; + return parser_yylex(p); +} + +static void +parser_init_cxt(parser_state *p, mrbc_context *cxt) +{ + if (!cxt) return; + if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); + if (cxt->lineno) p->lineno = cxt->lineno; + if (cxt->syms) { + int i; + + p->locals = cons(0,0); + for (i=0; i<cxt->slen; i++) { + local_add_f(p, cxt->syms[i]); + } + } + p->capture_errors = cxt->capture_errors; + p->no_optimize = cxt->no_optimize; + if (cxt->partial_hook) { + p->cxt = cxt; + } +} + +static void +parser_update_cxt(parser_state *p, mrbc_context *cxt) +{ + node *n, *n0; + int i = 0; + + if (!cxt) return; + if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return; + n0 = n = p->tree->cdr->car; + while (n) { + i++; + n = n->cdr; + } + cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym)); + cxt->slen = i; + for (i=0, n=n0; n; i++,n=n->cdr) { + cxt->syms[i] = sym(n->car); + } +} + +void mrb_codedump_all(mrb_state*, struct RProc*); +void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); + +MRB_API void +mrb_parser_parse(parser_state *p, mrbc_context *c) +{ + struct mrb_jmpbuf buf1; + p->jmp = &buf1; + + MRB_TRY(p->jmp) { + int n = 1; + + p->cmd_start = TRUE; + p->in_def = p->in_single = 0; + p->nerr = p->nwarn = 0; + p->lex_strterm = NULL; + + parser_init_cxt(p, c); + + if (p->mrb->jmp) { + n = yyparse(p); + } + else { + struct mrb_jmpbuf buf2; + + p->mrb->jmp = &buf2; + MRB_TRY(p->mrb->jmp) { + n = yyparse(p); + } + MRB_CATCH(p->mrb->jmp) { + p->nerr++; + } + MRB_END_EXC(p->mrb->jmp); + p->mrb->jmp = 0; + } + if (n != 0 || p->nerr > 0) { + p->tree = 0; + return; + } + if (!p->tree) { + p->tree = new_nil(p); + } + parser_update_cxt(p, c); + if (c && c->dump_result) { + mrb_parser_dump(p->mrb, p->tree, 0); + } + } + MRB_CATCH(p->jmp) { + yyerror(p, "memory allocation error"); + p->nerr++; + p->tree = 0; + return; + } + MRB_END_EXC(p->jmp); +} + +MRB_API parser_state* +mrb_parser_new(mrb_state *mrb) +{ + mrb_pool *pool; + parser_state *p; + static const parser_state parser_state_zero = { 0 }; + + pool = mrb_pool_open(mrb); + if (!pool) return NULL; + p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); + if (!p) return NULL; + + *p = parser_state_zero; + p->mrb = mrb; + p->pool = pool; + + p->s = p->send = NULL; +#ifndef MRB_DISABLE_STDIO + p->f = NULL; +#endif + + p->cmd_start = TRUE; + p->in_def = p->in_single = 0; + + p->capture_errors = FALSE; + p->lineno = 1; + p->column = 0; +#if defined(PARSER_TEST) || defined(PARSER_DEBUG) + yydebug = 1; +#endif + p->tsiz = MRB_PARSER_TOKBUF_SIZE; + p->tokbuf = p->buf; + + p->lex_strterm = NULL; + p->all_heredocs = p->parsing_heredoc = NULL; + p->lex_strterm_before_heredoc = NULL; + + p->current_filename_index = -1; + p->filename_table = NULL; + p->filename_table_length = 0; + + return p; +} + +MRB_API void +mrb_parser_free(parser_state *p) { + if (p->tokbuf != p->buf) { + mrb_free(p->mrb, p->tokbuf); + } + mrb_pool_close(p->pool); +} + +MRB_API mrbc_context* +mrbc_context_new(mrb_state *mrb) +{ + return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); +} + +MRB_API void +mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) +{ + mrb_free(mrb, cxt->filename); + mrb_free(mrb, cxt->syms); + mrb_free(mrb, cxt); +} + +MRB_API const char* +mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) +{ + if (s) { + size_t len = strlen(s); + char *p = (char *)mrb_malloc(mrb, len + 1); + + memcpy(p, s, len + 1); + if (c->filename) { + mrb_free(mrb, c->filename); + } + c->filename = p; + } + return c->filename; +} + +MRB_API void +mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data) +{ + c->partial_hook = func; + c->partial_data = data; +} + +MRB_API void +mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) +{ + mrb_sym sym; + size_t i; + mrb_sym* new_table; + + sym = mrb_intern_cstr(p->mrb, f); + p->filename = mrb_sym2name_len(p->mrb, sym, NULL); + p->lineno = (p->filename_table_length > 0)? 0 : 1; + + for (i = 0; i < p->filename_table_length; ++i) { + if (p->filename_table[i] == sym) { + p->current_filename_index = (int)i; + return; + } + } + + p->current_filename_index = (int)p->filename_table_length++; + + new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); + if (p->filename_table) { + memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); + } + p->filename_table = new_table; + p->filename_table[p->filename_table_length - 1] = sym; +} + +MRB_API char const* +mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { + if (idx >= p->filename_table_length) { return NULL; } + else { + return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL); + } +} + +#ifndef MRB_DISABLE_STDIO +MRB_API parser_state* +mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) +{ + parser_state *p; + + p = mrb_parser_new(mrb); + if (!p) return NULL; + p->s = p->send = NULL; + p->f = f; + + mrb_parser_parse(p, c); + return p; +} +#endif + +MRB_API parser_state* +mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrbc_context *c) +{ + parser_state *p; + + p = mrb_parser_new(mrb); + if (!p) return NULL; + p->s = s; + p->send = s + len; + + mrb_parser_parse(p, c); + return p; +} + +MRB_API parser_state* +mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) +{ + return mrb_parse_nstring(mrb, s, strlen(s), c); +} + +MRB_API mrb_value +mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) +{ + struct RClass *target = mrb->object_class; + struct RProc *proc; + mrb_value v; + unsigned int keep = 0; + + if (!p) { + return mrb_undef_value(); + } + if (!p->tree || p->nerr) { + if (c) c->parser_nerr = p->nerr; + if (p->capture_errors) { + char buf[256]; + int n; + + n = snprintf(buf, sizeof(buf), "line %d: %s\n", + p->error_buffer[0].lineno, p->error_buffer[0].message); + mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); + mrb_parser_free(p); + return mrb_undef_value(); + } + else { + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); + } + mrb_parser_free(p); + return mrb_undef_value(); + } + } + proc = mrb_generate_code(mrb, p); + mrb_parser_free(p); + if (proc == NULL) { + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); + } + return mrb_undef_value(); + } + if (c) { + if (c->dump_result) mrb_codedump_all(mrb, proc); + if (c->no_exec) return mrb_obj_value(proc); + if (c->target_class) { + target = c->target_class; + } + if (c->keep_lv) { + keep = c->slen + 1; + } + else { + c->keep_lv = TRUE; + } + } + proc->target_class = target; + if (mrb->c->ci) { + mrb->c->ci->target_class = target; + } + v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); + if (mrb->exc) return mrb_nil_value(); + return v; +} + +#ifndef MRB_DISABLE_STDIO +MRB_API mrb_value +mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) +{ + return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c); +} + +MRB_API mrb_value +mrb_load_file(mrb_state *mrb, FILE *f) +{ + return mrb_load_file_cxt(mrb, f, NULL); +} +#endif + +MRB_API mrb_value +mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrbc_context *c) +{ + return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); +} + +MRB_API mrb_value +mrb_load_nstring(mrb_state *mrb, const char *s, size_t len) +{ + return mrb_load_nstring_cxt(mrb, s, len, NULL); +} + +MRB_API mrb_value +mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c) +{ + return mrb_load_nstring_cxt(mrb, s, strlen(s), c); +} + +MRB_API mrb_value +mrb_load_string(mrb_state *mrb, const char *s) +{ + return mrb_load_string_cxt(mrb, s, NULL); +} + +#ifndef MRB_DISABLE_STDIO + +static void +dump_prefix(node *tree, int offset) +{ + printf("%05d ", tree->lineno); + while (offset--) { + putc(' ', stdout); + putc(' ', stdout); + } +} + +static void +dump_recur(mrb_state *mrb, node *tree, int offset) +{ + while (tree) { + mrb_parser_dump(mrb, tree->car, offset); + tree = tree->cdr; + } +} + +#endif + +void +mrb_parser_dump(mrb_state *mrb, node *tree, int offset) +{ +#ifndef MRB_DISABLE_STDIO + int nodetype; + + if (!tree) return; + again: + dump_prefix(tree, offset); + nodetype = (int)(intptr_t)tree->car; + tree = tree->cdr; + switch (nodetype) { + case NODE_BEGIN: + printf("NODE_BEGIN:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_RESCUE: + printf("NODE_RESCUE:\n"); + if (tree->car) { + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + } + tree = tree->cdr; + if (tree->car) { + node *n2 = tree->car; + + dump_prefix(n2, offset+1); + printf("rescue:\n"); + while (n2) { + node *n3 = n2->car; + if (n3->car) { + dump_prefix(n2, offset+2); + printf("handle classes:\n"); + dump_recur(mrb, n3->car, offset+3); + } + if (n3->cdr->car) { + dump_prefix(n3, offset+2); + printf("exc_var:\n"); + mrb_parser_dump(mrb, n3->cdr->car, offset+3); + } + if (n3->cdr->cdr->car) { + dump_prefix(n3, offset+2); + printf("rescue body:\n"); + mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); + } + n2 = n2->cdr; + } + } + tree = tree->cdr; + if (tree->car) { + dump_prefix(tree, offset+1); + printf("else:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + } + break; + + case NODE_ENSURE: + printf("NODE_ENSURE:\n"); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("ensure:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); + break; + + case NODE_LAMBDA: + printf("NODE_BLOCK:\n"); + goto block; + + case NODE_BLOCK: + block: + printf("NODE_BLOCK:\n"); + tree = tree->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + if (n->cdr) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr))); + } + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + break; + + case NODE_IF: + printf("NODE_IF:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("then:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + if (tree->cdr->cdr->car) { + dump_prefix(tree, offset+1); + printf("else:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); + } + break; + + case NODE_AND: + printf("NODE_AND:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_OR: + printf("NODE_OR:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_CASE: + printf("NODE_CASE:\n"); + if (tree->car) { + mrb_parser_dump(mrb, tree->car, offset+1); + } + tree = tree->cdr; + while (tree) { + dump_prefix(tree, offset+1); + printf("case:\n"); + dump_recur(mrb, tree->car->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car->cdr, offset+2); + tree = tree->cdr; + } + break; + + case NODE_WHILE: + printf("NODE_WHILE:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_UNTIL: + printf("NODE_UNTIL:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_FOR: + printf("NODE_FOR:\n"); + dump_prefix(tree, offset+1); + printf("var:\n"); + { + node *n2 = tree->car; + + if (n2->car) { + dump_prefix(n2, offset+2); + printf("pre:\n"); + dump_recur(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("rest:\n"); + mrb_parser_dump(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("post:\n"); + dump_recur(mrb, n2->car, offset+3); + } + } + } + } + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("in:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("do:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + break; + + case NODE_SCOPE: + printf("NODE_SCOPE:\n"); + { + node *n2 = tree->car; + mrb_bool first_lval = TRUE; + + if (n2 && (n2->car || n2->cdr)) { + dump_prefix(n2, offset+1); + printf("local variables:\n"); + dump_prefix(n2, offset+2); + while (n2) { + if (n2->car) { + if (!first_lval) printf(", "); + printf("%s", mrb_sym2name(mrb, sym(n2->car))); + first_lval = FALSE; + } + n2 = n2->cdr; + } + printf("\n"); + } + } + tree = tree->cdr; + offset++; + goto again; + + case NODE_FCALL: + case NODE_CALL: + case NODE_SCALL: + switch (nodetype) { + case NODE_FCALL: + printf("NODE_FCALL:\n"); break; + case NODE_CALL: + printf("NODE_CALL(.):\n"); break; + case NODE_SCALL: + printf("NODE_SCALL(&.):\n"); break; + default: + break; + } + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("method='%s' (%d)\n", + mrb_sym2name(mrb, sym(tree->cdr->car)), + (int)(intptr_t)tree->cdr->car); + tree = tree->cdr->cdr->car; + if (tree) { + dump_prefix(tree, offset+1); + printf("args:\n"); + dump_recur(mrb, tree->car, offset+2); + if (tree->cdr) { + dump_prefix(tree, offset+1); + printf("block:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + } + } + break; + + case NODE_DOT2: + printf("NODE_DOT2:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_DOT3: + printf("NODE_DOT3:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_COLON2: + printf("NODE_COLON2:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); + break; + + case NODE_COLON3: + printf("NODE_COLON3: ::%s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_ARRAY: + printf("NODE_ARRAY:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_HASH: + printf("NODE_HASH:\n"); + while (tree) { + dump_prefix(tree, offset+1); + printf("key:\n"); + mrb_parser_dump(mrb, tree->car->car, offset+2); + dump_prefix(tree, offset+1); + printf("value:\n"); + mrb_parser_dump(mrb, tree->car->cdr, offset+2); + tree = tree->cdr; + } + break; + + case NODE_SPLAT: + printf("NODE_SPLAT:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_ASGN: + printf("NODE_ASGN:\n"); + dump_prefix(tree, offset+1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_MASGN: + printf("NODE_MASGN:\n"); + dump_prefix(tree, offset+1); + printf("mlhs:\n"); + { + node *n2 = tree->car; + + if (n2->car) { + dump_prefix(tree, offset+2); + printf("pre:\n"); + dump_recur(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("rest:\n"); + if (n2->car == (node*)-1) { + dump_prefix(n2, offset+2); + printf("(empty)\n"); + } + else { + mrb_parser_dump(mrb, n2->car, offset+3); + } + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("post:\n"); + dump_recur(mrb, n2->car, offset+3); + } + } + } + } + dump_prefix(tree, offset+1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_OP_ASGN: + printf("NODE_OP_ASGN:\n"); + dump_prefix(tree, offset+1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); + tree = tree->cdr; + mrb_parser_dump(mrb, tree->car, offset+1); + break; + + case NODE_SUPER: + printf("NODE_SUPER:\n"); + if (tree) { + dump_prefix(tree, offset+1); + printf("args:\n"); + dump_recur(mrb, tree->car, offset+2); + if (tree->cdr) { + dump_prefix(tree, offset+1); + printf("block:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + } + } + break; + + case NODE_ZSUPER: + printf("NODE_ZSUPER\n"); + break; + + case NODE_RETURN: + printf("NODE_RETURN:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_YIELD: + printf("NODE_YIELD:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_BREAK: + printf("NODE_BREAK:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_NEXT: + printf("NODE_NEXT:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_REDO: + printf("NODE_REDO\n"); + break; + + case NODE_RETRY: + printf("NODE_RETRY\n"); + break; + + case NODE_LVAR: + printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_GVAR: + printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_IVAR: + printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_CVAR: + printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_CONST: + printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_MATCH: + printf("NODE_MATCH:\n"); + dump_prefix(tree, offset + 1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset + 2); + dump_prefix(tree, offset + 1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset + 2); + break; + + case NODE_BACK_REF: + printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree); + break; + + case NODE_NTH_REF: + printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree); + break; + + case NODE_ARG: + printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_BLOCK_ARG: + printf("NODE_BLOCK_ARG:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_INT: + printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car); + break; + + case NODE_FLOAT: + printf("NODE_FLOAT %s\n", (char*)tree); + break; + + case NODE_NEGATE: + printf("NODE_NEGATE\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_STR: + printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); + break; + + case NODE_DSTR: + printf("NODE_DSTR\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_XSTR: + printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); + break; + + case NODE_DXSTR: + printf("NODE_DXSTR\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_REGX: + printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr); + break; + + case NODE_DREGX: + printf("NODE_DREGX\n"); + dump_recur(mrb, tree->car, offset+1); + dump_prefix(tree, offset); + printf("tail: %s\n", (char*)tree->cdr->cdr->car); + if (tree->cdr->cdr->cdr->car) { + dump_prefix(tree, offset); + printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car); + } + if (tree->cdr->cdr->cdr->cdr) { + dump_prefix(tree, offset); + printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr); + } + break; + + case NODE_SYM: + printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)), + (int)(intptr_t)tree); + break; + + case NODE_SELF: + printf("NODE_SELF\n"); + break; + + case NODE_NIL: + printf("NODE_NIL\n"); + break; + + case NODE_TRUE: + printf("NODE_TRUE\n"); + break; + + case NODE_FALSE: + printf("NODE_FALSE\n"); + break; + + case NODE_ALIAS: + printf("NODE_ALIAS %s %s:\n", + mrb_sym2name(mrb, sym(tree->car)), + mrb_sym2name(mrb, sym(tree->cdr))); + break; + + case NODE_UNDEF: + printf("NODE_UNDEF"); + { + node *t = tree; + while (t) { + printf(" %s", mrb_sym2name(mrb, sym(t->car))); + t = t->cdr; + } + } + printf(":\n"); + break; + + case NODE_CLASS: + printf("NODE_CLASS:\n"); + if (tree->car->car == (node*)0) { + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else if (tree->car->car == (node*)1) { + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else { + mrb_parser_dump(mrb, tree->car->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + if (tree->cdr->car) { + dump_prefix(tree, offset+1); + printf("super:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); + break; + + case NODE_MODULE: + printf("NODE_MODULE:\n"); + if (tree->car->car == (node*)0) { + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else if (tree->car->car == (node*)1) { + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else { + mrb_parser_dump(mrb, tree->car->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); + break; + + case NODE_SCLASS: + printf("NODE_SCLASS:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); + break; + + case NODE_DEF: + printf("NODE_DEF:\n"); + dump_prefix(tree, offset+1); + printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); + tree = tree->cdr; + { + node *n2 = tree->car; + mrb_bool first_lval = TRUE; + + if (n2 && (n2->car || n2->cdr)) { + dump_prefix(n2, offset+1); + printf("local variables:\n"); + dump_prefix(n2, offset+2); + while (n2) { + if (n2->car) { + if (!first_lval) printf(", "); + printf("%s", mrb_sym2name(mrb, sym(n2->car))); + first_lval = FALSE; + } + n2 = n2->cdr; + } + printf("\n"); + } + } + tree = tree->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + if (n->cdr) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr))); + } + } + mrb_parser_dump(mrb, tree->cdr->car, offset+1); + break; + + case NODE_SDEF: + printf("NODE_SDEF:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car))); + tree = tree->cdr->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); + } + } + tree = tree->cdr; + mrb_parser_dump(mrb, tree->car, offset+1); + break; + + case NODE_POSTEXE: + printf("NODE_POSTEXE:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_HEREDOC: + printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term); + dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); + break; + + default: + printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); + break; + } +#endif +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake new file mode 100644 index 00000000..3bf6d6ae --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake @@ -0,0 +1,40 @@ +MRuby::Gem::Specification.new 'mruby-compiler' do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby compiler library' + + current_dir = spec.dir + current_build_dir = spec.build_dir + + lex_def = "#{current_dir}/core/lex.def" + core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f| + next nil if build.cxx_exception_enabled? and f =~ /(codegen).c$/ + objfile(f.pathmap("#{current_build_dir}/core/%n")) + }.compact + + if build.cxx_exception_enabled? + core_objs << + build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx", + objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) << + build.compile_as_cxx("#{current_dir}/core/codegen.c", "#{current_build_dir}/core/codegen.cxx") + else + core_objs << objfile("#{current_build_dir}/core/y.tab") + file objfile("#{current_build_dir}/core/y.tab") => "#{current_build_dir}/core/y.tab.c" do |t| + cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"] + end + end + file objfile("#{current_build_dir}/core/y.tab") => lex_def + + # Parser + file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t| + yacc.run t.name, t.prerequisites.first + end + + # Lexical analyzer + file lex_def => "#{current_dir}/core/keywords" do |t| + gperf.run t.name, t.prerequisites.first + end + + file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs + build.libmruby << core_objs +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake new file mode 100644 index 00000000..d5816b80 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Enumerable module extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb new file mode 100644 index 00000000..7741e515 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -0,0 +1,711 @@ +## +# Enumerable +# +module Enumerable + ## + # call-seq: + # enum.drop(n) -> array + # + # Drops first n elements from <i>enum</i>, and returns rest elements + # in an array. + # + # a = [1, 2, 3, 4, 5, 0] + # a.drop(3) #=> [4, 5, 0] + + def drop(n) + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + raise ArgumentError, "attempt to drop negative size" if n < 0 + + n = n.to_int + ary = [] + self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 } + ary + end + + ## + # call-seq: + # enum.drop_while {|arr| block } -> array + # enum.drop_while -> an_enumerator + # + # Drops elements up to, but not including, the first element for + # which the block returns +nil+ or +false+ and returns an array + # containing the remaining elements. + # + # If no block is given, an enumerator is returned instead. + # + # a = [1, 2, 3, 4, 5, 0] + # a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0] + + def drop_while(&block) + return to_enum :drop_while unless block + + ary, state = [], false + self.each do |*val| + state = true if !state and !block.call(*val) + ary << val.__svalue if state + end + ary + end + + ## + # call-seq: + # enum.take(n) -> array + # + # Returns first n elements from <i>enum</i>. + # + # a = [1, 2, 3, 4, 5, 0] + # a.take(3) #=> [1, 2, 3] + + def take(n) + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + i = n.to_int + raise ArgumentError, "attempt to take negative size" if i < 0 + ary = [] + return ary if i == 0 + self.each do |*val| + ary << val.__svalue + i -= 1 + break if i == 0 + end + ary + end + + ## + # call-seq: + # enum.take_while {|arr| block } -> array + # enum.take_while -> an_enumerator + # + # Passes elements to the block until the block returns +nil+ or +false+, + # then stops iterating and returns an array of all prior elements. + # + # If no block is given, an enumerator is returned instead. + # + # a = [1, 2, 3, 4, 5, 0] + # a.take_while {|i| i < 3 } #=> [1, 2] + # + def take_while(&block) + return to_enum :take_while unless block + + ary = [] + self.each do |*val| + return ary unless block.call(*val) + ary << val.__svalue + end + ary + end + + ## + # Iterates the given block for each array of consecutive <n> + # elements. + # + # @return [nil] + # + # @example + # (1..10).each_cons(3) {|a| p a} + # # outputs below + # [1, 2, 3] + # [2, 3, 4] + # [3, 4, 5] + # [4, 5, 6] + # [5, 6, 7] + # [6, 7, 8] + # [7, 8, 9] + # [8, 9, 10] + + def each_cons(n, &block) + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + raise ArgumentError, "invalid size" if n <= 0 + + return to_enum(:each_cons,n) unless block + ary = [] + n = n.to_int + self.each do |*val| + ary.shift if ary.size == n + ary << val.__svalue + block.call(ary.dup) if ary.size == n + end + nil + end + + ## + # Iterates the given block for each slice of <n> elements. + # + # @return [nil] + # + # @example + # (1..10).each_slice(3) {|a| p a} + # # outputs below + # [1, 2, 3] + # [4, 5, 6] + # [7, 8, 9] + # [10] + + def each_slice(n, &block) + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + raise ArgumentError, "invalid slice size" if n <= 0 + + return to_enum(:each_slice,n) unless block + ary = [] + n = n.to_int + self.each do |*val| + ary << val.__svalue + if ary.size == n + block.call(ary) + ary = [] + end + end + block.call(ary) unless ary.empty? + nil + end + + ## + # call-seq: + # enum.group_by {| obj | block } -> a_hash + # enum.group_by -> an_enumerator + # + # Returns a hash, which keys are evaluated result from the + # block, and values are arrays of elements in <i>enum</i> + # corresponding to the key. + # + # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]} + # + def group_by(&block) + return to_enum :group_by unless block + + h = {} + self.each do |*val| + key = block.call(*val) + sv = val.__svalue + h.key?(key) ? (h[key] << sv) : (h[key] = [sv]) + end + h + end + + ## + # call-seq: + # enum.sort_by { |obj| block } -> array + # enum.sort_by -> an_enumerator + # + # Sorts <i>enum</i> using a set of keys generated by mapping the + # values in <i>enum</i> through the given block. + # + # If no block is given, an enumerator is returned instead. + + def sort_by(&block) + return to_enum :sort_by unless block + + ary = [] + orig = [] + self.each_with_index{|e, i| + orig.push(e) + ary.push([block.call(e), i]) + } + if ary.size > 1 + __sort_sub__(ary, 0, ary.size - 1) do |a,b| + a <=> b + end + end + ary.collect{|e,i| orig[i]} + end + + NONE = Object.new + ## + # call-seq: + # enum.first -> obj or nil + # enum.first(n) -> an_array + # + # Returns the first element, or the first +n+ elements, of the enumerable. + # If the enumerable is empty, the first form returns <code>nil</code>, and the + # second form returns an empty array. + def first(*args) + case args.length + when 0 + self.each do |*val| + return val.__svalue + end + return nil + when 1 + n = args[0] + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + i = n.to_int + raise ArgumentError, "attempt to take negative size" if i < 0 + ary = [] + return ary if i == 0 + self.each do |*val| + ary << val.__svalue + i -= 1 + break if i == 0 + end + ary + else + raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" + end + end + + ## + # call-seq: + # enum.count -> int + # enum.count(item) -> int + # enum.count { |obj| block } -> int + # + # Returns the number of items in +enum+ through enumeration. + # If an argument is given, the number of items in +enum+ that + # are equal to +item+ are counted. If a block is given, it + # counts the number of elements yielding a true value. + def count(v=NONE, &block) + count = 0 + if block + self.each do |*val| + count += 1 if block.call(*val) + end + else + if v == NONE + self.each { count += 1 } + else + self.each do |*val| + count += 1 if val.__svalue == v + end + end + end + count + end + + ## + # call-seq: + # enum.flat_map { |obj| block } -> array + # enum.collect_concat { |obj| block } -> array + # enum.flat_map -> an_enumerator + # enum.collect_concat -> an_enumerator + # + # Returns a new array with the concatenated results of running + # <em>block</em> once for every element in <i>enum</i>. + # + # If no block is given, an enumerator is returned instead. + # + # [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4] + # [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100] + def flat_map(&block) + return to_enum :flat_map unless block + + ary = [] + self.each do |*e| + e2 = block.call(*e) + if e2.respond_to? :each + e2.each {|e3| ary.push(e3) } + else + ary.push(e2) + end + end + ary + end + alias collect_concat flat_map + + ## + # call-seq: + # enum.max_by {|obj| block } -> obj + # enum.max_by -> an_enumerator + # + # Returns the object in <i>enum</i> that gives the maximum + # value from the given block. + # + # If no block is given, an enumerator is returned instead. + # + # %w[albatross dog horse].max_by {|x| x.length } #=> "albatross" + + def max_by(&block) + return to_enum :max_by unless block + + first = true + max = nil + max_cmp = nil + + self.each do |*val| + if first + max = val.__svalue + max_cmp = block.call(*val) + first = false + else + if (cmp = block.call(*val)) > max_cmp + max = val.__svalue + max_cmp = cmp + end + end + end + max + end + + ## + # call-seq: + # enum.min_by {|obj| block } -> obj + # enum.min_by -> an_enumerator + # + # Returns the object in <i>enum</i> that gives the minimum + # value from the given block. + # + # If no block is given, an enumerator is returned instead. + # + # %w[albatross dog horse].min_by {|x| x.length } #=> "dog" + + def min_by(&block) + return to_enum :min_by unless block + + first = true + min = nil + min_cmp = nil + + self.each do |*val| + if first + min = val.__svalue + min_cmp = block.call(*val) + first = false + else + if (cmp = block.call(*val)) < min_cmp + min = val.__svalue + min_cmp = cmp + end + end + end + min + end + + ## + # call-seq: + # enum.minmax -> [min, max] + # enum.minmax { |a, b| block } -> [min, max] + # + # Returns two elements array which contains the minimum and the + # maximum value in the enumerable. The first form assumes all + # objects implement <code>Comparable</code>; the second uses the + # block to return <em>a <=> b</em>. + # + # a = %w(albatross dog horse) + # a.minmax #=> ["albatross", "horse"] + # a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"] + + def minmax(&block) + max = nil + min = nil + first = true + + self.each do |*val| + if first + val = val.__svalue + max = val + min = val + first = false + else + val = val.__svalue + if block + max = val if block.call(val, max) > 0 + min = val if block.call(val, min) < 0 + else + max = val if (val <=> max) > 0 + min = val if (val <=> min) < 0 + end + end + end + [min, max] + end + + ## + # call-seq: + # enum.minmax_by { |obj| block } -> [min, max] + # enum.minmax_by -> an_enumerator + # + # Returns a two element array containing the objects in + # <i>enum</i> that correspond to the minimum and maximum values respectively + # from the given block. + # + # If no block is given, an enumerator is returned instead. + # + # %w(albatross dog horse).minmax_by { |x| x.length } #=> ["dog", "albatross"] + + def minmax_by(&block) + return to_enum :minmax_by unless block + + max = nil + max_cmp = nil + min = nil + min_cmp = nil + first = true + + self.each do |*val| + if first + max = min = val.__svalue + max_cmp = min_cmp = block.call(*val) + first = false + else + if (cmp = block.call(*val)) > max_cmp + max = val.__svalue + max_cmp = cmp + end + if (cmp = block.call(*val)) < min_cmp + min = val.__svalue + min_cmp = cmp + end + end + end + [min, max] + end + + ## + # call-seq: + # enum.none? [{ |obj| block }] -> true or false + # + # Passes each element of the collection to the given block. The method + # returns <code>true</code> if the block never returns <code>true</code> + # for all elements. If the block is not given, <code>none?</code> will return + # <code>true</code> only if none of the collection members is true. + # + # %w(ant bear cat).none? { |word| word.length == 5 } #=> true + # %w(ant bear cat).none? { |word| word.length >= 4 } #=> false + # [].none? #=> true + # [nil, false].none? #=> true + # [nil, true].none? #=> false + + def none?(&block) + if block + self.each do |*val| + return false if block.call(*val) + end + else + self.each do |*val| + return false if val.__svalue + end + end + true + end + + ## + # call-seq: + # enum.one? [{ |obj| block }] -> true or false + # + # Passes each element of the collection to the given block. The method + # returns <code>true</code> if the block returns <code>true</code> + # exactly once. If the block is not given, <code>one?</code> will return + # <code>true</code> only if exactly one of the collection members is + # true. + # + # %w(ant bear cat).one? { |word| word.length == 4 } #=> true + # %w(ant bear cat).one? { |word| word.length > 4 } #=> false + # %w(ant bear cat).one? { |word| word.length < 4 } #=> false + # [nil, true, 99].one? #=> false + # [nil, true, false].one? #=> true + # + + def one?(&block) + count = 0 + if block + self.each do |*val| + count += 1 if block.call(*val) + return false if count > 1 + end + else + self.each do |*val| + count += 1 if val.__svalue + return false if count > 1 + end + end + + count == 1 ? true : false + end + + ## + # call-seq: + # enum.each_with_object(obj) { |(*args), memo_obj| ... } -> obj + # enum.each_with_object(obj) -> an_enumerator + # + # Iterates the given block for each element with an arbitrary + # object given, and returns the initially given object. + # + # If no block is given, returns an enumerator. + # + # (1..10).each_with_object([]) { |i, a| a << i*2 } + # #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + # + + def each_with_object(obj=nil, &block) + raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil? + + return to_enum(:each_with_object, obj) unless block + + self.each {|*val| block.call(val.__svalue, obj) } + obj + end + + ## + # call-seq: + # enum.reverse_each { |item| block } -> enum + # enum.reverse_each -> an_enumerator + # + # Builds a temporary array and traverses that array in reverse order. + # + # If no block is given, an enumerator is returned instead. + # + # (1..3).reverse_each { |v| p v } + # + # produces: + # + # 3 + # 2 + # 1 + # + + def reverse_each(&block) + return to_enum :reverse_each unless block + + ary = self.to_a + i = ary.size - 1 + while i>=0 + block.call(ary[i]) + i -= 1 + end + self + end + + ## + # call-seq: + # enum.cycle(n=nil) { |obj| block } -> nil + # enum.cycle(n=nil) -> an_enumerator + # + # Calls <i>block</i> for each element of <i>enum</i> repeatedly _n_ + # times or forever if none or +nil+ is given. If a non-positive + # number is given or the collection is empty, does nothing. Returns + # +nil+ if the loop has finished without getting interrupted. + # + # Enumerable#cycle saves elements in an internal array so changes + # to <i>enum</i> after the first pass have no effect. + # + # If no block is given, an enumerator is returned instead. + # + # a = ["a", "b", "c"] + # a.cycle { |x| puts x } # print, a, b, c, a, b, c,.. forever. + # a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c. + # + + def cycle(nv = nil, &block) + return to_enum(:cycle, nv) unless block + + n = nil + + if nv.nil? + n = -1 + else + unless nv.respond_to?(:to_int) + raise TypeError, "no implicit conversion of #{nv.class} into Integer" + end + n = nv.to_int + unless n.kind_of?(Integer) + raise TypeError, "no implicit conversion of #{nv.class} into Integer" + end + return nil if n <= 0 + end + + ary = [] + each do |*i| + ary.push(i) + yield(*i) + end + return nil if ary.empty? + + while n < 0 || 0 < (n -= 1) + ary.each do |i| + yield(*i) + end + end + + nil + end + + ## + # call-seq: + # enum.find_index(value) -> int or nil + # enum.find_index { |obj| block } -> int or nil + # enum.find_index -> an_enumerator + # + # Compares each entry in <i>enum</i> with <em>value</em> or passes + # to <em>block</em>. Returns the index for the first for which the + # evaluated value is non-false. If no object matches, returns + # <code>nil</code> + # + # If neither block nor argument is given, an enumerator is returned instead. + # + # (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> nil + # (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> 34 + # (1..100).find_index(50) #=> 49 + # + + def find_index(val=NONE, &block) + return to_enum(:find_index, val) if !block && val == NONE + + idx = 0 + if block + self.each do |*e| + return idx if block.call(*e) + idx += 1 + end + else + self.each do |*e| + return idx if e.__svalue == val + idx += 1 + end + end + nil + end + + ## + # call-seq: + # enum.zip(arg, ...) -> an_array_of_array + # + # Takes one element from <i>enum</i> and merges corresponding + # elements from each <i>args</i>. This generates a sequence of + # <em>n</em>-element arrays, where <em>n</em> is one more than the + # count of arguments. The length of the resulting sequence will be + # <code>enum#size</code>. If the size of any argument is less than + # <code>enum#size</code>, <code>nil</code> values are supplied. + # + + def zip(*arg) + ary = [] + arg = arg.map{|a|a.to_a} + i = 0 + self.each do |*val| + a = [] + a.push(val.__svalue) + idx = 0 + while idx < arg.size + a.push(arg[idx][i]) + idx += 1 + end + ary.push(a) + i += 1 + end + ary + end + + ## + # call-seq: + # enum.to_h -> hash + # + # Returns the result of interpreting <i>enum</i> as a list of + # <tt>[key, value]</tt> pairs. + # + # %i[hello world].each_with_index.to_h + # # => {:hello => 0, :world => 1} + # + + def to_h + h = {} + self.each do |*v| + v = v.__svalue + raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array + raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 + h[v[0]] = v[1] + end + h + end + + def nil.to_h + {} + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb new file mode 100644 index 00000000..e772f85b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb @@ -0,0 +1,171 @@ +## +# Enumerable(Ext) Test + +assert("Enumerable#drop") do + a = [1, 2, 3, 4, 5, 0] + + assert_equal [4, 5, 0], a.drop(3) + assert_equal [], a.drop(6) +end + +assert("Enumerable#drop_while") do + a = [1, 2, 3, 4, 5, 0] + assert_equal [3, 4, 5, 0], a.drop_while {|i| i < 3 } +end + +assert("Enumerable#take") do + a = [1, 2, 3, 4, 5, 0] + assert_equal [1, 2, 3], a.take(3) +end + +assert("Enumerable#take_while") do + a = [1, 2, 3, 4, 5, 0] + assert_equal [1, 2], a.take_while {|i| i < 3} +end + +assert("Enumerable#each_cons") do + a = [] + b = (1..5).each_cons(3){|e| a << e} + assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a + assert_equal nil, b +end + +assert("Enumerable#each_slice") do + a = [] + b = (1..10).each_slice(3){|e| a << e} + assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a + assert_equal nil, b +end + +assert("Enumerable#group_by") do + r = (1..6).group_by {|i| i % 3 } + assert_equal [3, 6], r[0] + assert_equal [1, 4], r[1] + assert_equal [2, 5], r[2] +end + +assert("Enumerable#sort_by") do + assert_equal ["car", "train", "bicycle"], %w{car bicycle train}.sort_by {|e| e.length} +end + +assert("Enumerable#first") do + a = Object.new + a.extend Enumerable + def a.each + yield 1 + yield 2 + yield 3 + end + assert_equal 1, a.first + assert_equal [1, 2], a.first(2) + assert_equal [1, 2, 3], a.first(10) + a = Object.new + a.extend Enumerable + def a.each + end + assert_nil a.first +end + +assert("Enumerable#count") do + a = [1, 2, 4, 2] + assert_equal 4, a.count + assert_equal 2, a.count(2) + assert_equal 3, a.count{|x| x % 2 == 0} +end + +assert("Enumerable#flat_map") do + assert_equal [1, 2, 3, 4], [1, 2, 3, 4].flat_map { |e| e } + assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] } + assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] } +end + +assert("Enumerable#max_by") do + assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length } +end + +assert("Enumerable#min_by") do + assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length } +end + +assert("Enumerable#minmax") do + a = %w(albatross dog horse) + assert_equal ["albatross", "horse"], a.minmax + assert_equal ["dog", "albatross"], a.minmax { |a, b| a.length <=> b.length } +end + +assert("Enumerable#minmax_by") do + assert_equal ["dog", "albatross"], %w(albatross dog horse).minmax_by { |x| x.length } +end + +assert("Enumerable#none?") do + assert_true %w(ant bear cat).none? { |word| word.length == 5 } + assert_false %w(ant bear cat).none? { |word| word.length >= 4 } + assert_true [].none? + assert_true [nil, false].none? + assert_false [nil, true].none? +end + +assert("Enumerable#one?") do + assert_true %w(ant bear cat).one? { |word| word.length == 4 } + assert_false %w(ant bear cat).one? { |word| word.length > 4 } + assert_false %w(ant bear cat).one? { |word| word.length < 4 } + assert_false [nil, true, 99].one? + assert_true [nil, true, false].one? +end + +assert("Enumerable#each_with_object") do + assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 } + assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } } +end + +assert("Enumerable#reverse_each") do + r = (1..3) + a = [] + assert_equal (1..3), r.reverse_each { |v| a << v } + assert_equal [3, 2, 1], a +end + +assert("Enumerable#cycle") do + a = [] + ["a", "b", "c"].cycle(2) { |v| a << v } + assert_equal ["a", "b", "c", "a", "b", "c"], a + assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } } + + empty = Class.new do + include Enumerable + def each + end + end + assert_nil empty.new.cycle { break :nope } +end + +assert("Enumerable#find_index") do + assert_nil (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } + assert_equal 34, (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } + assert_equal 49 ,(1..100).find_index(50) +end + +assert("Enumerable#zip") do + a = [ 4, 5, 6 ] + b = [ 7, 8, 9 ] + assert_equal [[4, 7], [5, 8], [6, 9]], a.zip(b) + assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b) + assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b) + assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8]) +end + +assert("Enumerable#to_h") do + c = Class.new { + include Enumerable + def each + yield [1,2] + yield [3,4] + end + } + h0 = {1=>2, 3=>4} + h = c.new.to_h + assert_equal Hash, h.class + assert_equal h0, h + # mruby-enum-ext also provides nil.to_h + assert_equal Hash.new, nil.to_h +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake new file mode 100644 index 00000000..682134c4 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake @@ -0,0 +1,7 @@ +MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Enumerator::Lazy class' + spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') + spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext') +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb new file mode 100644 index 00000000..c98681ed --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb @@ -0,0 +1,163 @@ +module Enumerable + + # = Enumerable#lazy implementation + # + # Enumerable#lazy returns an instance of Enumerator::Lazy. + # You can use it just like as normal Enumerable object, + # except these methods act as 'lazy': + # + # - map collect + # - select find_all + # - reject + # - grep + # - drop + # - drop_while + # - take_while + # - flat_map collect_concat + # - zip + def lazy + Enumerator::Lazy.new(self) + end +end + +class Enumerator + # == Acknowledgements + # + # Based on https://github.com/yhara/enumerable-lazy + # Inspired by https://github.com/antimon2/enumerable_lz + # http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja) + class Lazy < Enumerator + def initialize(obj, &block) + super(){|yielder| + begin + obj.each{|x| + if block + block.call(yielder, x) + else + yielder << x + end + } + rescue StopIteration + end + } + end + + def to_enum(meth=:each, *args, &block) + unless self.respond_to?(meth) + raise NoMethodError, "undefined method #{meth}" + end + lz = Lazy.new(self, &block) + lz.obj = self + lz.meth = meth + lz.args = args + lz + end + alias enum_for to_enum + + def map(&block) + Lazy.new(self){|yielder, val| + yielder << block.call(val) + } + end + alias collect map + + def select(&block) + Lazy.new(self){|yielder, val| + if block.call(val) + yielder << val + end + } + end + alias find_all select + + def reject(&block) + Lazy.new(self){|yielder, val| + unless block.call(val) + yielder << val + end + } + end + + def grep(pattern) + Lazy.new(self){|yielder, val| + if pattern === val + yielder << val + end + } + end + + def drop(n) + dropped = 0 + Lazy.new(self){|yielder, val| + if dropped < n + dropped += 1 + else + yielder << val + end + } + end + + def drop_while(&block) + dropping = true + Lazy.new(self){|yielder, val| + if dropping + if not block.call(val) + yielder << val + dropping = false + end + else + yielder << val + end + } + end + + def take(n) + if n == 0 + return Lazy.new(self){raise StopIteration} + end + taken = 0 + Lazy.new(self){|yielder, val| + yielder << val + taken += 1 + if taken >= n + raise StopIteration + end + } + end + + def take_while(&block) + Lazy.new(self){|yielder, val| + if block.call(val) + yielder << val + else + raise StopIteration + end + } + end + + def flat_map(&block) + Lazy.new(self){|yielder, val| + ary = block.call(val) + # TODO: check ary is an Array + ary.each{|x| + yielder << x + } + } + end + alias collect_concat flat_map + + def zip(*args, &block) + enums = [self] + args + Lazy.new(self){|yielder, val| + ary = enums.map{|e| e.next} + if block + yielder << block.call(ary) + else + yielder << ary + end + } + end + + alias force to_a + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb new file mode 100644 index 00000000..940d070e --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb @@ -0,0 +1,53 @@ +assert("Enumerator::Lazy") do + a = [1, 2] + assert_equal Enumerator::Lazy, a.lazy.class +end + +assert("Enumerator::Lazy laziness") do + a = Object.new + def a.each + return to_enum :each unless block_given? + self.b << 10 + yield 1 + self.b << 20 + yield 2 + self.b << 30 + yield 3 + self.b << 40 + yield 4 + self.b << 50 + yield 5 + end + def a.b(b=nil) + @b = b if b + @b + end + + a.b([]) + assert_equal [1,2], a.each.lazy.take(2).force + assert_equal [10,20], a.b + + a.b([]) + assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force + assert_equal [10,20,30,40], a.b + + a.b([]) + assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force + assert_equal [10], a.b + + a.b([]) + assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force + assert_equal [10,20], a.b +end + +assert("Enumrator::Lazy#to_enum") do + lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2) + assert_kind_of Enumerator::Lazy, lazy_enum + assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4) +end + +assert("Enumerator::Lazy#zip with cycle") do + e1 = [1, 2, 3].cycle + e2 = [:a, :b].cycle + assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake new file mode 100644 index 00000000..8757a15e --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake @@ -0,0 +1,7 @@ +MRuby::Gem::Specification.new('mruby-enumerator') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.add_dependency('mruby-fiber', :core => 'mruby-fiber') + spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext' + spec.summary = 'Enumerator class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb new file mode 100644 index 00000000..1e77af36 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb @@ -0,0 +1,645 @@ +## +# enumerator.rb Enumerator class +# See Copyright Notice in mruby.h + +## +# A class which allows both internal and external iteration. +# +# An Enumerator can be created by the following methods. +# - {Kernel#to_enum} +# - {Kernel#enum_for} +# - {Enumerator#initialize Enumerator.new} +# +# Most methods have two forms: a block form where the contents +# are evaluated for each item in the enumeration, and a non-block form +# which returns a new Enumerator wrapping the iteration. +# +# enumerator = %w(one two three).each +# puts enumerator.class # => Enumerator +# +# enumerator.each_with_object("foo") do |item, obj| +# puts "#{obj}: #{item}" +# end +# +# # foo: one +# # foo: two +# # foo: three +# +# enum_with_obj = enumerator.each_with_object("foo") +# puts enum_with_obj.class # => Enumerator +# +# enum_with_obj.each do |item, obj| +# puts "#{obj}: #{item}" +# end +# +# # foo: one +# # foo: two +# # foo: three +# +# This allows you to chain Enumerators together. For example, you +# can map a list's elements to strings containing the index +# and the element as a string via: +# +# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } +# # => ["0:foo", "1:bar", "2:baz"] +# +# An Enumerator can also be used as an external iterator. +# For example, Enumerator#next returns the next value of the iterator +# or raises StopIteration if the Enumerator is at the end. +# +# e = [1,2,3].each # returns an enumerator object. +# puts e.next # => 1 +# puts e.next # => 2 +# puts e.next # => 3 +# puts e.next # raises StopIteration +# +# You can use this to implement an internal iterator as follows: +# +# def ext_each(e) +# while true +# begin +# vs = e.next_values +# rescue StopIteration +# return $!.result +# end +# y = yield(*vs) +# e.feed y +# end +# end +# +# o = Object.new +# +# def o.each +# puts yield +# puts yield(1) +# puts yield(1, 2) +# 3 +# end +# +# # use o.each as an internal iterator directly. +# puts o.each {|*x| puts x; [:b, *x] } +# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 +# +# # convert o.each to an external iterator for +# # implementing an internal iterator. +# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] } +# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 +# +class Enumerator + include Enumerable + + ## + # @overload initialize(size = nil, &block) + # @overload initialize(obj, method = :each, *args) + # + # Creates a new Enumerator object, which can be used as an + # Enumerable. + # + # In the first form, iteration is defined by the given block, in + # which a "yielder" object, given as block parameter, can be used to + # yield a value by calling the +yield+ method (aliased as +<<+): + # + # fib = Enumerator.new do |y| + # a = b = 1 + # loop do + # y << a + # a, b = b, a + b + # end + # end + # + # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + # + def initialize(obj=nil, meth=:each, *args, &block) + if block + obj = Generator.new(&block) + else + raise ArgumentError unless obj + end + if @obj and !self.respond_to?(meth) + raise NoMethodError, "undefined method #{meth}" + end + + @obj = obj + @meth = meth + @args = args.dup + @fib = nil + @dst = nil + @lookahead = nil + @feedvalue = nil + @stop_exc = false + end + attr_accessor :obj, :meth, :args, :fib + private :obj, :meth, :args, :fib + + def initialize_copy(obj) + raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator + raise TypeError, "can't copy execution context" if obj.fib + @obj = obj.obj + @meth = obj.meth + @args = obj.args + @fib = nil + @lookahead = nil + @feedvalue = nil + self + end + + ## + # call-seq: + # e.with_index(offset = 0) {|(*args), idx| ... } + # e.with_index(offset = 0) + # + # Iterates the given block for each element with an index, which + # starts from +offset+. If no block is given, returns a new Enumerator + # that includes the index, starting from +offset+ + # + # +offset+:: the starting index to use + # + def with_index(offset=0, &block) + return to_enum :with_index, offset unless block + + offset = if offset.nil? + 0 + elsif offset.respond_to?(:to_int) + offset.to_int + else + raise TypeError, "no implicit conversion of #{offset.class} into Integer" + end + + n = offset - 1 + enumerator_block_call do |*i| + n += 1 + block.call i.__svalue, n + end + end + + ## + # call-seq: + # e.each_with_index {|(*args), idx| ... } + # e.each_with_index + # + # Same as Enumerator#with_index(0), i.e. there is no starting offset. + # + # If no block is given, a new Enumerator is returned that includes the index. + # + def each_with_index(&block) + with_index(0, &block) + end + + ## + # call-seq: + # e.each_with_object(obj) {|(*args), obj| ... } + # e.each_with_object(obj) + # e.with_object(obj) {|(*args), obj| ... } + # e.with_object(obj) + # + # Iterates the given block for each element with an arbitrary object, +obj+, + # and returns +obj+ + # + # If no block is given, returns a new Enumerator. + # + # @example + # to_three = Enumerator.new do |y| + # 3.times do |x| + # y << x + # end + # end + # + # to_three_with_string = to_three.with_object("foo") + # to_three_with_string.each do |x,string| + # puts "#{string}: #{x}" + # end + # + # # => foo:0 + # # => foo:1 + # # => foo:2 + # + def with_object(object, &block) + return to_enum(:with_object, object) unless block + + enumerator_block_call do |i| + block.call [i,object] + end + object + end + + def inspect + return "#<#{self.class}: uninitialized>" unless @obj + + if @args && @args.size > 0 + args = @args.join(", ") + "#<#{self.class}: #{@obj}:#{@meth}(#{args})>" + else + "#<#{self.class}: #{@obj}:#{@meth}>" + end + end + + ## + # call-seq: + # enum.each { |elm| block } -> obj + # enum.each -> enum + # enum.each(*appending_args) { |elm| block } -> obj + # enum.each(*appending_args) -> an_enumerator + # + # Iterates over the block according to how this Enumerator was constructed. + # If no block and no arguments are given, returns self. + # + # === Examples + # + # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"] + # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"] + # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"] + # + # obj = Object.new + # + # def obj.each_arg(a, b=:b, *rest) + # yield a + # yield b + # yield rest + # :method_returned + # end + # + # enum = obj.to_enum :each_arg, :a, :x + # + # enum.each.to_a #=> [:a, :x, []] + # enum.each.equal?(enum) #=> true + # enum.each { |elm| elm } #=> :method_returned + # + # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]] + # enum.each(:y, :z).equal?(enum) #=> false + # enum.each(:y, :z) { |elm| elm } #=> :method_returned + # + def each(*argv, &block) + obj = self + if 0 < argv.length + obj = self.dup + args = obj.args + if !args.empty? + args = args.dup + args.concat argv + else + args = argv.dup + end + obj.args = args + end + return obj unless block + enumerator_block_call(&block) + end + + def enumerator_block_call(&block) + @obj.__send__ @meth, *@args, &block + end + private :enumerator_block_call + + ## + # call-seq: + # e.next -> object + # + # Returns the next object in the enumerator, and move the internal position + # forward. When the position reached at the end, StopIteration is raised. + # + # === Example + # + # a = [1,2,3] + # e = a.to_enum + # p e.next #=> 1 + # p e.next #=> 2 + # p e.next #=> 3 + # p e.next #raises StopIteration + # + # Note that enumeration sequence by +next+ does not affect other non-external + # enumeration methods, unless the underlying iteration methods itself has + # side-effect + # + def next + next_values.__svalue + end + + ## + # call-seq: + # e.next_values -> array + # + # Returns the next object as an array in the enumerator, and move the + # internal position forward. When the position reached at the end, + # StopIteration is raised. + # + # This method can be used to distinguish <code>yield</code> and <code>yield + # nil</code>. + # + # === Example + # + # o = Object.new + # def o.each + # yield + # yield 1 + # yield 1, 2 + # yield nil + # yield [1, 2] + # end + # e = o.to_enum + # p e.next_values + # p e.next_values + # p e.next_values + # p e.next_values + # p e.next_values + # e = o.to_enum + # p e.next + # p e.next + # p e.next + # p e.next + # p e.next + # + # ## yield args next_values next + # # yield [] nil + # # yield 1 [1] 1 + # # yield 1, 2 [1, 2] [1, 2] + # # yield nil [nil] nil + # # yield [1, 2] [[1, 2]] [1, 2] + # + # Note that +next_values+ does not affect other non-external enumeration + # methods unless underlying iteration method itself has side-effect + # + def next_values + if @lookahead + vs = @lookahead + @lookahead = nil + return vs + end + raise @stop_exc if @stop_exc + + curr = Fiber.current + + if !@fib || !@fib.alive? + @dst = curr + @fib = Fiber.new do + result = each do |*args| + feedvalue = nil + Fiber.yield args + if @feedvalue + feedvalue = @feedvalue + @feedvalue = nil + end + feedvalue + end + @stop_exc = StopIteration.new "iteration reached an end" + @stop_exc.result = result + Fiber.yield nil + end + @lookahead = nil + end + + vs = @fib.resume curr + if @stop_exc + @fib = nil + @dst = nil + @lookahead = nil + @feedvalue = nil + raise @stop_exc + end + vs + end + + ## + # call-seq: + # e.peek -> object + # + # Returns the next object in the enumerator, but doesn't move the internal + # position forward. If the position is already at the end, StopIteration + # is raised. + # + # === Example + # + # a = [1,2,3] + # e = a.to_enum + # p e.next #=> 1 + # p e.peek #=> 2 + # p e.peek #=> 2 + # p e.peek #=> 2 + # p e.next #=> 2 + # p e.next #=> 3 + # p e.next #raises StopIteration + # + def peek + peek_values.__svalue + end + + ## + # call-seq: + # e.peek_values -> array + # + # Returns the next object as an array, similar to Enumerator#next_values, but + # doesn't move the internal position forward. If the position is already at + # the end, StopIteration is raised. + # + # === Example + # + # o = Object.new + # def o.each + # yield + # yield 1 + # yield 1, 2 + # end + # e = o.to_enum + # p e.peek_values #=> [] + # e.next + # p e.peek_values #=> [1] + # p e.peek_values #=> [1] + # e.next + # p e.peek_values #=> [1, 2] + # e.next + # p e.peek_values # raises StopIteration + # + def peek_values + if @lookahead.nil? + @lookahead = next_values + end + @lookahead.dup + end + + ## + # call-seq: + # e.rewind -> e + # + # Rewinds the enumeration sequence to the beginning. + # + # If the enclosed object responds to a "rewind" method, it is called. + # + def rewind + @obj.rewind if @obj.respond_to? :rewind + @fib = nil + @dst = nil + @lookahead = nil + @feedvalue = nil + @stop_exc = false + self + end + + ## + # call-seq: + # e.feed obj -> nil + # + # Sets the value to be returned by the next yield inside +e+. + # + # If the value is not set, the yield returns nil. + # + # This value is cleared after being yielded. + # + # # Array#map passes the array's elements to "yield" and collects the + # # results of "yield" as an array. + # # Following example shows that "next" returns the passed elements and + # # values passed to "feed" are collected as an array which can be + # # obtained by StopIteration#result. + # e = [1,2,3].map + # p e.next #=> 1 + # e.feed "a" + # p e.next #=> 2 + # e.feed "b" + # p e.next #=> 3 + # e.feed "c" + # begin + # e.next + # rescue StopIteration + # p $!.result #=> ["a", "b", "c"] + # end + # + # o = Object.new + # def o.each + # x = yield # (2) blocks + # p x # (5) => "foo" + # x = yield # (6) blocks + # p x # (8) => nil + # x = yield # (9) blocks + # p x # not reached w/o another e.next + # end + # + # e = o.to_enum + # e.next # (1) + # e.feed "foo" # (3) + # e.next # (4) + # e.next # (7) + # # (10) + # + def feed(value) + raise TypeError, "feed value already set" if @feedvalue + @feedvalue = value + nil + end + + # just for internal + class Generator + include Enumerable + def initialize(&block) + raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc + + @proc = block + end + + def each(*args, &block) + args.unshift Yielder.new(&block) + @proc.call(*args) + end + end + + # just for internal + class Yielder + def initialize(&block) + raise LocalJumpError, "no block given" unless block + + @proc = block + end + + def yield(*args) + @proc.call(*args) + end + + def << *args + self.yield(*args) + self + end + end +end + +module Kernel + ## + # call-seq: + # obj.to_enum(method = :each, *args) -> enum + # obj.enum_for(method = :each, *args) -> enum + # obj.to_enum(method = :each, *args) {|*args| block} -> enum + # obj.enum_for(method = :each, *args){|*args| block} -> enum + # + # Creates a new Enumerator which will enumerate by calling +method+ on + # +obj+, passing +args+ if any. + # + # If a block is given, it will be used to calculate the size of + # the enumerator without the need to iterate it (see Enumerator#size). + # + # === Examples + # + # str = "xyz" + # + # enum = str.enum_for(:each_byte) + # enum.each { |b| puts b } + # # => 120 + # # => 121 + # # => 122 + # + # # protect an array from being modified by some_method + # a = [1, 2, 3] + # some_method(a.to_enum) + # + # It is typical to call to_enum when defining methods for + # a generic Enumerable, in case no block is passed. + # + # Here is such an example, with parameter passing and a sizing block: + # + # module Enumerable + # # a generic method to repeat the values of any enumerable + # def repeat(n) + # raise ArgumentError, "#{n} is negative!" if n < 0 + # unless block_given? + # return to_enum(__method__, n) do # __method__ is :repeat here + # sz = size # Call size and multiply by n... + # sz * n if sz # but return nil if size itself is nil + # end + # end + # each do |*val| + # n.times { yield *val } + # end + # end + # end + # + # %i[hello world].repeat(2) { |w| puts w } + # # => Prints 'hello', 'hello', 'world', 'world' + # enum = (1..14).repeat(3) + # # => returns an Enumerator when called without a block + # enum.first(4) # => [1, 1, 1, 2] + # + def to_enum(meth=:each, *args) + Enumerator.new self, meth, *args + end + alias enum_for to_enum +end + +module Enumerable + # use Enumerator to use infinite sequence + def zip(*arg) + ary = [] + arg = arg.map{|a|a.each} + i = 0 + self.each do |*val| + a = [] + a.push(val.__svalue) + idx = 0 + while idx < arg.size + begin + a.push(arg[idx].next) + rescue StopIteration + a.push(nil) + end + idx += 1 + end + ary.push(a) + i += 1 + end + ary + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb new file mode 100644 index 00000000..763cd36e --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb @@ -0,0 +1,546 @@ +@obj = Object.new +class << @obj + include Enumerable + def foo *a + a.each { |x| yield x } + end +end + +assert 'Enumerator' do + assert_equal Class, Enumerator.class +end + +assert 'Enumerator' do + assert_equal Object, Enumerator.superclass +end + +assert 'Enumerator.new' do + assert_equal [0,1,2], 3.times.map{|i| i}.sort + assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort + assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort + assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a + assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a + assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3) + assert_raise(ArgumentError) { Enumerator.new } + enum = @obj.to_enum + assert_raise(NoMethodError) { enum.each {} } + + # examples + fib = Enumerator.new do |y| + a = b = 1 + loop do + y << a + a, b = b, a + b + end + end + assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55] +end + +assert 'Enumerator#initialize_copy' do + assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a + e = @obj.to_enum :foo, 1, 2, 3 + assert_nothing_raised { assert_equal(1, e.next) } + assert_raise(TypeError) { e.dup } + + e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup + assert_nothing_raised { assert_equal(1, e.next) } + assert_raise(TypeError) { e.dup } +end + +assert 'Enumerator#with_index' do + assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a) + assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a) + a = [] + @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i } + assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a +end + +assert 'Enumerator#with_index nonnum offset' do + s = Object.new + def s.to_int; 1 end + assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a) +end + +assert 'Enumerator#with_index string offset' do + assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a } +end + +assert 'Enumerator#each_with_index' do + assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a) + a = [] + @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i} + assert_equal([[1, 0], [2, 1], [3, 2]], a) +end + +assert 'Enumerator#with_object' do + obj = [0, 1] + ret = (1..10).each.with_object(obj) {|i, memo| + memo[0] += i + memo[1] *= i + } + assert_true(obj.equal?(ret)) + assert_equal([55, 3628800], ret) +end + +assert 'Enumerator#with_object arguments' do + to_three = Enumerator.new do |y| + 3.times do |x| + y << x + end + end + + a = [] + to_three_with_string = to_three.with_object("foo") + to_three_with_string.each do |x,string| + a << "#{string}:#{x}" + end + assert_equal ["foo:0","foo:1","foo:2"], a +end + +assert 'Enumerator#inspect' do + e = (0..10).each + assert_equal("#<Enumerator: 0..10:each>", e.inspect) + e = Enumerator.new("FooObject", :foo, 1) + assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect) + e = Enumerator.new("FooObject", :foo, 1, 2, 3) + assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect) +end + +assert 'Enumerator#each' do + o = Object.new + def o.each(ary) + ary << 1 + yield + end + ary = [] + e = o.to_enum.each(ary) + e.next + assert_equal([1], ary) +end + +assert 'Enumerator#each arguments' do + obj = Object.new + + def obj.each_arg(a, b=:b, *rest) + yield a + yield b + yield rest + :method_returned + end + + enum = obj.to_enum :each_arg, :a, :x + + assert_equal [:a, :x, []], enum.each.to_a + assert_true enum.each.equal?(enum) + assert_equal :method_returned, enum.each { |elm| elm } + + assert_equal [:a, :x, [:y, :z]], enum.each(:y, :z).to_a + assert_false enum.each(:y, :z).equal?(enum) + assert_equal :method_returned, enum.each(:y, :z) { |elm| elm } +end + +assert 'Enumerator#next' do + e = 3.times + 3.times { |i| + assert_equal i, e.next + } + assert_raise(StopIteration) { e.next } +end + +assert 'Enumerator#next_values' do + o = Object.new + def o.each + yield + yield 1 + yield 1, 2 + end + e = o.to_enum + assert_equal nil, e.next + assert_equal 1, e.next + assert_equal [1,2], e.next + e = o.to_enum + assert_equal [], e.next_values + assert_equal [1], e.next_values + assert_equal [1,2], e.next_values +end + +assert 'Enumerator#peek' do + a = [1] + e = a.each + assert_equal 1, e.peek + assert_equal 1, e.peek + assert_equal 1, e.next + assert_raise(StopIteration) { e.peek } + assert_raise(StopIteration) { e.peek } +end + +assert 'Enumerator#peek modify' do + o = Object.new + def o.each + yield 1,2 + end + e = o.to_enum + a = e.peek + a << 3 + assert_equal([1,2], e.peek) +end + +assert 'Enumerator#peek_values' do + o = Object.new + def o.each + yield + yield 1 + yield 1, 2 + end + e = o.to_enum + assert_equal nil, e.peek + assert_equal nil, e.next + assert_equal 1, e.peek + assert_equal 1, e.next + assert_equal [1,2], e.peek + assert_equal [1,2], e.next + e = o.to_enum + assert_equal [], e.peek_values + assert_equal [], e.next_values + assert_equal [1], e.peek_values + assert_equal [1], e.next_values + assert_equal [1,2], e.peek_values + assert_equal [1,2], e.next_values + e = o.to_enum + assert_equal [], e.peek_values + assert_equal nil, e.next + assert_equal [1], e.peek_values + assert_equal 1, e.next + assert_equal [1,2], e.peek_values + assert_equal [1,2], e.next + e = o.to_enum + assert_equal nil, e.peek + assert_equal [], e.next_values + assert_equal 1, e.peek + assert_equal [1], e.next_values + assert_equal [1,2], e.peek + assert_equal [1,2], e.next_values +end + +assert 'Enumerator#peek_values modify' do + o = Object.new + def o.each + yield 1,2 + end + e = o.to_enum + a = e.peek_values + a << 3 + assert_equal [1,2], e.peek +end + +assert 'Enumerator#feed' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum :each, ary + e.next + e.feed 1 + e.next + e.feed 2 + e.next + e.feed 3 + assert_raise(StopIteration) { e.next } + assert_equal [1,2,3], ary +end + +assert 'Enumerator#feed mixed' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum :each, ary + e.next + e.feed 1 + e.next + e.next + e.feed 3 + assert_raise(StopIteration) { e.next } + assert_equal [1,nil,3], ary +end + +assert 'Enumerator#feed twice' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum :each, ary + e.feed 1 + assert_raise(TypeError) { e.feed 2 } +end + +assert 'Enumerator#feed before first next' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum :each, ary + e.feed 1 + e.next + e.next + assert_equal [1], ary +end + +assert 'Enumerator#feed yielder' do + x = nil + e = Enumerator.new {|y| x = y.yield; 10 } + e.next + e.feed 100 + assert_raise(StopIteration) { e.next } + assert_equal 100, x +end + +assert 'Enumerator#rewind' do + e = @obj.to_enum(:foo, 1, 2, 3) + assert_equal 1, e.next + assert_equal 2, e.next + e.rewind + assert_equal 1, e.next + assert_equal 2, e.next + assert_equal 3, e.next + assert_raise(StopIteration) { e.next } +end + +assert 'Enumerator#rewind clear feed' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum(:each, ary) + e.next + e.feed 1 + e.next + e.feed 2 + e.rewind + e.next + e.next + assert_equal([1,nil], ary) +end + +assert 'Enumerator#rewind clear' do + o = Object.new + def o.each(ary) + ary << yield + ary << yield + ary << yield + end + ary = [] + e = o.to_enum :each, ary + e.next + e.feed 1 + e.next + e.feed 2 + e.rewind + e.next + e.next + assert_equal [1,nil], ary +end + +assert 'Enumerator::Generator' do + # note: Enumerator::Generator is a class just for internal + g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo } + g2 = g.dup + a = [] + assert_equal(:foo, g.each {|x| a << x }) + assert_equal([1, 2, 3], a) + a = [] + assert_equal(:foo, g2.each {|x| a << x }) + assert_equal([1, 2, 3], a) +end + +assert 'Enumerator::Generator args' do + g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x } + a = [] + assert_equal(:bar, g.each(:bar) {|x| a << x }) + assert_equal([1, 2, 3], a) +end + +assert 'Enumerator::Yielder' do + # note: Enumerator::Yielder is a class just for internal + a = [] + y = Enumerator::Yielder.new {|x| a << x } + assert_equal(y, y << 1 << 2 << 3) + assert_equal([1, 2, 3], a) + + a = [] + y = Enumerator::Yielder.new {|x| a << x } + assert_equal([1], y.yield(1)) + assert_equal([1, 2], y.yield(2)) + assert_equal([1, 2, 3], y.yield(3)) + + assert_raise(LocalJumpError) { Enumerator::Yielder.new } +end + +assert 'next after StopIteration' do + a = [1] + e = a.each + assert_equal(1, e.next) + assert_raise(StopIteration) { e.next } + assert_raise(StopIteration) { e.next } + e.rewind + assert_equal(1, e.next) + assert_raise(StopIteration) { e.next } + assert_raise(StopIteration) { e.next } +end + +assert 'gc' do + assert_nothing_raised do + 1.times do + foo = [1,2,3].to_enum + GC.start + end + GC.start + end +end + +assert 'nested iteration' do + def (o = Object.new).each + yield :ok1 + yield [:ok2, :x].each.next + end + e = o.to_enum + assert_equal :ok1, e.next + assert_equal :ok2, e.next + assert_raise(StopIteration) { e.next } +end + +assert 'Kernel#to_enum' do + assert_equal Enumerator, [].to_enum.class + assert_raise(ArgumentError){ nil.to_enum } +end + +assert 'modifying existing methods' do + assert_equal Enumerator, loop.class + e = 3.times + i = 0 + loop_ret = loop { + assert_equal i, e.next + i += 1 + } +end + +assert 'Integral#times' do + a = 3 + b = a.times + c = [] + b.with_object(c) do |i, obj| + obj << i + end + assert_equal 3, a + assert_equal Enumerator, b.class + assert_equal [0,1,2], c +end + +assert 'Enumerable#each_with_index' do + assert_equal [['a',0],['b',1],['c',2]], ['a','b','c'].each_with_index.to_a +end + +assert 'Enumerable#map' do + a = [1,2,3] + b = a.map + c = b.with_index do |i, index| + [i*i, index*index] + end + assert_equal [1,2,3], a + assert_equal [[1,0],[4,1],[9,4]], c +end + +assert 'Enumerable#find_all' do + assert_equal [[3,4]], [[1,2],[3,4],[5,6]].find_all.each{ |i| i[1] == 4 } +end + +assert 'Array#each_index' do + a = [1,2,3] + b = a.each_index + c = [] + b.with_index do |index1,index2| + c << [index1+2,index2+5] + end + assert_equal [1,2,3], a + assert_equal [[2,5],[3,6],[4,7]], c +end + +assert 'Array#map!' do + a = [1,2,3] + b = a.map! + b.with_index do |i, index| + [i*i, index*index] + end + assert_equal [[1,0],[4,1],[9,4]], a +end + +assert 'Hash#each' do + a = {a:1,b:2} + b = a.each + c = [] + b.each do |k,v| + c << [k,v] + end + assert_equal [[:a,1], [:b,2]], c.sort +end + +assert 'Hash#each_key' do + assert_equal [:a,:b], {a:1,b:2}.each_key.to_a.sort +end + +assert 'Hash#each_value' do + assert_equal [1,2], {a:1,b:2}.each_value.to_a.sort +end + +assert 'Hash#select' do + h = {1=>2,3=>4,5=>6} + hret = h.select.with_index {|a,b| a[1] == 4} + assert_equal({3=>4}, hret) + assert_equal({1=>2,3=>4,5=>6}, h) +end + +assert 'Hash#select!' do + h = {1=>2,3=>4,5=>6} + hret = h.select!.with_index {|a,b| a[1] == 4} + assert_equal h, hret + assert_equal({3=>4}, h) +end + +assert 'Hash#reject' do + h = {1=>2,3=>4,5=>6} + hret = h.reject.with_index {|a,b| a[1] == 4} + assert_equal({1=>2,5=>6}, hret) + assert_equal({1=>2,3=>4,5=>6}, h) +end + +assert 'Hash#reject!' do + h = {1=>2,3=>4,5=>6} + hret = h.reject!.with_index {|a,b| a[1] == 4} + assert_equal h, hret + assert_equal({1=>2,5=>6}, h) +end + +assert 'Range#each' do + a = (1..5) + b = a.each + c = [] + b.each do |i| + c << i + end + assert_equal [1,2,3,4,5], c +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake new file mode 100644 index 00000000..30a4259a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake @@ -0,0 +1,10 @@ +MRuby::Gem::Specification.new('mruby-error') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'extensional error handling' + + if build.cxx_exception_enabled? + @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx") + @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") } + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c new file mode 100644 index 00000000..170abb69 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c @@ -0,0 +1,100 @@ +#include <mruby.h> +#include <mruby/throw.h> +#include <mruby/error.h> + +MRB_API mrb_value +mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state) +{ + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + mrb_value result = mrb_nil_value(); + + if (state) { *state = FALSE; } + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + result = body(mrb, data); + mrb->jmp = prev_jmp; + } MRB_CATCH(&c_jmp) { + mrb->jmp = prev_jmp; + result = mrb_obj_value(mrb->exc); + mrb->exc = NULL; + if (state) { *state = TRUE; } + } MRB_END_EXC(&c_jmp); + + mrb_gc_protect(mrb, result); + return result; +} + +MRB_API mrb_value +mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data) +{ + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + mrb_value result; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + result = body(mrb, b_data); + mrb->jmp = prev_jmp; + } MRB_CATCH(&c_jmp) { + mrb->jmp = prev_jmp; + ensure(mrb, e_data); + MRB_THROW(mrb->jmp); /* rethrow catched exceptions */ + } MRB_END_EXC(&c_jmp); + + ensure(mrb, e_data); + mrb_gc_protect(mrb, result); + return result; +} + +MRB_API mrb_value +mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, + mrb_func_t rescue, mrb_value r_data) +{ + return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class); +} + +MRB_API mrb_value +mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, + mrb_int len, struct RClass **classes) +{ + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + mrb_value result; + mrb_bool error_matched = FALSE; + mrb_int i; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + result = body(mrb, b_data); + mrb->jmp = prev_jmp; + } MRB_CATCH(&c_jmp) { + mrb->jmp = prev_jmp; + + for (i = 0; i < len; ++i) { + if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), classes[i])) { + error_matched = TRUE; + break; + } + } + + if (!error_matched) { MRB_THROW(mrb->jmp); } + + mrb->exc = NULL; + result = rescue(mrb, r_data); + } MRB_END_EXC(&c_jmp); + + mrb_gc_protect(mrb, result); + return result; +} + +void +mrb_mruby_error_gem_init(mrb_state *mrb) +{ +} + +void +mrb_mruby_error_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c new file mode 100644 index 00000000..4de0e960 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c @@ -0,0 +1,59 @@ +#include <mruby.h> +#include <mruby/error.h> +#include <mruby/array.h> + +static mrb_value +protect_cb(mrb_state *mrb, mrb_value b) +{ + return mrb_yield_argv(mrb, b, 0, NULL); +} + +static mrb_value +run_protect(mrb_state *mrb, mrb_value self) +{ + mrb_value b; + mrb_value ret[2]; + mrb_bool state; + mrb_get_args(mrb, "&", &b); + ret[0] = mrb_protect(mrb, protect_cb, b, &state); + ret[1] = mrb_bool_value(state); + return mrb_ary_new_from_values(mrb, 2, ret); +} + +static mrb_value +run_ensure(mrb_state *mrb, mrb_value self) +{ + mrb_value b, e; + mrb_get_args(mrb, "oo", &b, &e); + return mrb_ensure(mrb, protect_cb, b, protect_cb, e); +} + +static mrb_value +run_rescue(mrb_state *mrb, mrb_value self) +{ + mrb_value b, r; + mrb_get_args(mrb, "oo", &b, &r); + return mrb_rescue(mrb, protect_cb, b, protect_cb, r); +} + +static mrb_value +run_rescue_exceptions(mrb_state *mrb, mrb_value self) +{ + mrb_value b, r; + struct RClass *cls[1]; + mrb_get_args(mrb, "oo", &b, &r); + cls[0] = E_TYPE_ERROR; + return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls); +} + +void +mrb_mruby_error_gem_test(mrb_state *mrb) +{ + struct RClass *cls; + + cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class); + mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK()); + mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb new file mode 100644 index 00000000..90846504 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb @@ -0,0 +1,55 @@ +assert 'mrb_protect' do + # no failure in protect returns [result, false] + assert_equal ['test', false] do + ExceptionTest.mrb_protect { 'test' } + end + # failure in protect returns [exception, true] + result = ExceptionTest.mrb_protect { raise 'test' } + assert_kind_of RuntimeError, result[0] + assert_true result[1] +end + +assert 'mrb_ensure' do + a = false + assert_equal 'test' do + ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true } + end + assert_true a + + a = false + assert_raise RuntimeError do + ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true } + end + assert_true a +end + +assert 'mrb_rescue' do + assert_equal 'test' do + ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {} + end + + class CustomExp < Exception + end + + assert_raise CustomExp do + ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' } + end + + assert_equal 'rescue' do + ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' } + end +end + +assert 'mrb_rescue_exceptions' do + assert_equal 'test' do + ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {} + end + + assert_raise RangeError do + ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' } + end + + assert_equal 'rescue' do + ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' } + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake new file mode 100644 index 00000000..cb8835b3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake @@ -0,0 +1,7 @@ +MRuby::Gem::Specification.new('mruby-eval') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard Kernel#eval method' + + add_dependency 'mruby-compiler', :core => 'mruby-compiler' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c new file mode 100644 index 00000000..5c0ea82f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c @@ -0,0 +1,346 @@ +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/compile.h> +#include <mruby/irep.h> +#include <mruby/proc.h> +#include <mruby/opcode.h> +#include <mruby/error.h> + +mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p); +mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); + +static struct mrb_irep * +get_closure_irep(mrb_state *mrb, int level) +{ + struct mrb_context *c = mrb->c; + struct REnv *e = c->ci[-1].proc->env; + struct RProc *proc; + + if (level == 0) { + proc = c->ci[-1].proc; + if (MRB_PROC_CFUNC_P(proc)) { + return NULL; + } + return proc->body.irep; + } + + while (--level) { + e = (struct REnv*)e->c; + if (!e) return NULL; + } + + if (!e) return NULL; + if (!MRB_ENV_STACK_SHARED_P(e)) return NULL; + c = e->cxt.c; + proc = c->cibase[e->cioff].proc; + + if (!proc || MRB_PROC_CFUNC_P(proc)) { + return NULL; + } + return proc->body.irep; +} + +/* search for irep lev above the bottom */ +static mrb_irep* +search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom) +{ + int i; + + for (i=0; i<top->rlen; i++) { + mrb_irep* tmp = top->reps[i]; + + if (tmp == bottom) return top; + tmp = search_irep(tmp, bnest-1, lev, bottom); + if (tmp) { + if (bnest == lev) return top; + return tmp; + } + } + return NULL; +} + +static inline mrb_code +search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) +{ + mrb_irep *virep; + int level; + int pos; + + for (level = 0; (virep = get_closure_irep(mrb, level)); level++) { + if (!virep || virep->lv == NULL) { + continue; + } + for (pos = 0; pos < virep->nlocals - 1; pos++) { + if (vsym == virep->lv[pos].name) { + return (MKARG_B(pos + 1) | MKARG_C(level + bnest)); + } + } + } + + return 0; +} + +static int +irep_argc(mrb_irep *irep) +{ + mrb_code c; + + c = irep->iseq[0]; + if (GET_OPCODE(c) == OP_ENTER) { + mrb_aspec ax = GETARG_Ax(c); + /* extra 1 means a slot for block */ + return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; + } + return 0; +} + +static mrb_bool +potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) +{ + if (v >= nlocals) return FALSE; + /* skip arguments */ + if (v < argc+1) return FALSE; + return TRUE; +} + +static void +patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top) +{ + int i; + mrb_code c; + int argc = irep_argc(irep); + + for (i = 0; i < irep->ilen; i++) { + c = irep->iseq[i]; + switch(GET_OPCODE(c)){ + case OP_EPUSH: + patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top); + break; + + case OP_LAMBDA: + { + int arg_c = GETARG_c(c); + if (arg_c & OP_L_CAPTURE) { + patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top); + } + } + break; + + case OP_SEND: + if (GETARG_C(c) != 0) { + break; + } + { + mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + break; + + case OP_MOVE: + /* src part */ + if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) { + mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + /* dst part */ + if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) { + mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg; + } + } + break; + + case OP_GETUPVAR: + { + int lev = GETARG_C(c)+1; + mrb_irep *tmp = search_irep(top, bnest, lev, irep); + if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) { + mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + } + break; + + case OP_SETUPVAR: + { + int lev = GETARG_C(c)+1; + mrb_irep *tmp = search_irep(top, bnest, lev, irep); + if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) { + mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + } + break; + + case OP_STOP: + if (mrb->c->ci->acc >= 0) { + irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL); + } + break; + } + } +} + +void mrb_codedump_all(mrb_state*, struct RProc*); + +static struct RProc* +create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line) +{ + mrbc_context *cxt; + struct mrb_parser_state *p; + struct RProc *proc; + struct REnv *e; + struct mrb_context *c = mrb->c; + + if (!mrb_nil_p(binding)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil."); + } + + cxt = mrbc_context_new(mrb); + cxt->lineno = line; + + mrbc_filename(mrb, cxt, file ? file : "(eval)"); + cxt->capture_errors = TRUE; + cxt->no_optimize = TRUE; + + p = mrb_parse_nstring(mrb, s, len, cxt); + + /* only occur when memory ran out */ + if (!p) { + mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state."); + } + + if (0 < p->nerr) { + /* parse error */ + mrb_value str; + + if (file) { + str = mrb_format(mrb, " file %S line %S: %S", + mrb_str_new_cstr(mrb, file), + mrb_fixnum_value(p->error_buffer[0].lineno), + mrb_str_new_cstr(mrb, p->error_buffer[0].message)); + } + else { + str = mrb_format(mrb, " line %S: %S", + mrb_fixnum_value(p->error_buffer[0].lineno), + mrb_str_new_cstr(mrb, p->error_buffer[0].message)); + } + mrb_parser_free(p); + mrbc_context_free(mrb, cxt); + mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); + } + + proc = mrb_generate_code(mrb, p); + if (proc == NULL) { + /* codegen error */ + mrb_parser_free(p); + mrbc_context_free(mrb, cxt); + mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error"); + } + if (c->ci[-1].proc->target_class) { + proc->target_class = c->ci[-1].proc->target_class; + } + e = c->ci[-1].proc->env; + if (!e) e = c->ci[-1].env; + e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e); + e->cxt.c = c; + e->cioff = c->ci - c->cibase; + e->stack = c->ci->stackent; + MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals); + c->ci->target_class = proc->target_class; + c->ci->env = 0; + proc->env = e; + patch_irep(mrb, proc->body.irep, 0, proc->body.irep); + /* mrb_codedump_all(mrb, proc); */ + + mrb_parser_free(p); + mrbc_context_free(mrb, cxt); + + return proc; +} + +static mrb_value +exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc) +{ + if (mrb->c->ci->acc < 0) { + mrb_value ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0); + if (mrb->exc) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); + } + return ret; + } + return mrb_exec_irep(mrb, self, proc); +} + +static mrb_value +f_eval(mrb_state *mrb, mrb_value self) +{ + char *s; + mrb_int len; + mrb_value binding = mrb_nil_value(); + char *file = NULL; + mrb_int line = 1; + struct RProc *proc; + + mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line); + + proc = create_proc_from_string(mrb, s, len, binding, file, line); + mrb_assert(!MRB_PROC_CFUNC_P(proc)); + return exec_irep(mrb, self, proc); +} + +static mrb_value +f_instance_eval(mrb_state *mrb, mrb_value self) +{ + mrb_value b; + mrb_int argc; mrb_value *argv; + + mrb_get_args(mrb, "*!&", &argv, &argc, &b); + + if (mrb_nil_p(b)) { + char *s; + mrb_int len; + char *file = NULL; + mrb_int line = 1; + mrb_value cv; + struct RProc *proc; + + mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); + cv = mrb_singleton_class(mrb, self); + proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); + proc->target_class = mrb_class_ptr(cv); + mrb->c->ci->env = NULL; + mrb_assert(!MRB_PROC_CFUNC_P(proc)); + return exec_irep(mrb, self, proc); + } + else { + mrb_get_args(mrb, "&", &b); + return mrb_obj_instance_eval(mrb, self); + } +} + +void +mrb_mruby_eval_gem_init(mrb_state* mrb) +{ + mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3)); + mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2)); +} + +void +mrb_mruby_eval_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb new file mode 100644 index 00000000..66ca1fcd --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb @@ -0,0 +1,101 @@ +assert('Kernel.eval', '15.3.1.2.3') do + assert_equal(10) { Kernel.eval '1 * 10' } + assert_equal('aaa') { Kernel.eval "'a' * 3" } + assert_equal(10) { + a = 10 + Kernel.eval "a" + } + assert_equal(20) { + a = 10 + Kernel.eval "a = 20" + a + } + assert_equal(15) { + c = 5 + lambda { + a = 10 + Kernel.eval "c = a + c" + }.call + c + } + assert_equal(5) { + c = 5 + lambda { + Kernel.eval 'lambda { c }.call' + }.call + } + assert_equal(15) { + c = 5 + lambda { + a = 10 + Kernel.eval 'lambda { c = a + c }.call' + }.call + c + } + assert_equal(2) { + a = 10 + Kernel.eval 'def f(a); b=a.send(:+, 1); end' + f(1) + } +end + +assert('Kernel#eval', '15.3.1.3.12') do + assert_equal(10) { eval '1 * 10' } +end + +assert('rest arguments of eval') do + assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) } + assert_equal ['test', 'test.rb', 10] do + Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10) + end +end + +assert 'eval syntax error' do + assert_raise(SyntaxError) do + eval 'p "test' + end +end + +assert('String instance_eval') do + obj = Object.new + obj.instance_variable_set :@test, 'test' + assert_raise(ArgumentError) { obj.instance_eval(0) { } } + assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') } + assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)} + assert_equal('test') { obj.instance_eval('@test') } + assert_equal('test') { obj.instance_eval { @test } } + o = Object.new + assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }") +end + +assert('Kernel.#eval(string) context') do + class TestEvalConstScope + EVAL_CONST_CLASS = 'class' + def const_string + eval 'EVAL_CONST_CLASS' + end + end + obj = TestEvalConstScope.new + assert_raise(NameError) { eval 'EVAL_CONST_CLASS' } + assert_equal('class') { obj.const_string } +end + +assert('Object#instance_eval with begin-rescue-ensure execution order') do + class HellRaiser + def raise_hell + order = [:enter_raise_hell] + begin + order.push :begin + self.instance_eval("raise 'error'") + rescue + order.push :rescue + ensure + order.push :ensure + end + order + end + end + + hell_raiser = HellRaiser.new + assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake new file mode 100644 index 00000000..d193528d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-exit') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Kernel#exit method' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c new file mode 100644 index 00000000..3e147f80 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c @@ -0,0 +1,24 @@ +#include <stdlib.h> +#include <mruby.h> + +static mrb_value +f_exit(mrb_state *mrb, mrb_value self) +{ + mrb_int i = EXIT_SUCCESS; + + mrb_get_args(mrb, "|i", &i); + exit(i); + /* not reached */ + return mrb_nil_value(); +} + +void +mrb_mruby_exit_gem_init(mrb_state* mrb) +{ + mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit, MRB_ARGS_OPT(1)); +} + +void +mrb_mruby_exit_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake new file mode 100644 index 00000000..815cd3c4 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-fiber') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Fiber class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c new file mode 100644 index 00000000..9de175f3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c @@ -0,0 +1,420 @@ +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/proc.h> + +#define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o)) + +#define FIBER_STACK_INIT_SIZE 64 +#define FIBER_CI_INIT_SIZE 8 +#define CI_ACC_RESUMED -3 + +/* + * call-seq: + * Fiber.new{...} -> obj + * + * Creates a fiber, whose execution is suspend until it is explicitly + * resumed using <code>Fiber#resume</code> method. + * The code running inside the fiber can give up control by calling + * <code>Fiber.yield</code> in which case it yields control back to caller + * (the caller of the <code>Fiber#resume</code>). + * + * Upon yielding or termination the Fiber returns the value of the last + * executed expression + * + * For instance: + * + * fiber = Fiber.new do + * Fiber.yield 1 + * 2 + * end + * + * puts fiber.resume + * puts fiber.resume + * puts fiber.resume + * + * <em>produces</em> + * + * 1 + * 2 + * resuming dead fiber (FiberError) + * + * The <code>Fiber#resume</code> method accepts an arbitrary number of + * parameters, if it is the first call to <code>resume</code> then they + * will be passed as block arguments. Otherwise they will be the return + * value of the call to <code>Fiber.yield</code> + * + * Example: + * + * fiber = Fiber.new do |first| + * second = Fiber.yield first + 2 + * end + * + * puts fiber.resume 10 + * puts fiber.resume 14 + * puts fiber.resume 18 + * + * <em>produces</em> + * + * 12 + * 14 + * resuming dead fiber (FiberError) + * + */ +static mrb_value +fiber_init(mrb_state *mrb, mrb_value self) +{ + static const struct mrb_context mrb_context_zero = { 0 }; + struct RFiber *f = fiber_ptr(self); + struct mrb_context *c; + struct RProc *p; + mrb_callinfo *ci; + mrb_value blk; + size_t slen; + + mrb_get_args(mrb, "&", &blk); + + if (f->cxt) { + mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice"); + } + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block"); + } + p = mrb_proc_ptr(blk); + if (MRB_PROC_CFUNC_P(p)) { + mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method"); + } + + c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); + *c = mrb_context_zero; + f->cxt = c; + + /* initialize VM stack */ + slen = FIBER_STACK_INIT_SIZE; + if (p->body.irep->nregs > slen) { + slen += p->body.irep->nregs; + } + c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value)); + c->stend = c->stbase + slen; + c->stack = c->stbase; + +#ifdef MRB_NAN_BOXING + { + mrb_value *p = c->stbase; + mrb_value *pend = c->stend; + + while (p < pend) { + SET_NIL_VALUE(*p); + p++; + } + } +#else + memset(c->stbase, 0, slen * sizeof(mrb_value)); +#endif + + /* copy receiver from a block */ + c->stack[0] = mrb->c->stack[0]; + + /* initialize callinfo stack */ + c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo)); + c->ciend = c->cibase + FIBER_CI_INIT_SIZE; + c->ci = c->cibase; + c->ci->stackent = c->stack; + + /* adjust return callinfo */ + ci = c->ci; + ci->target_class = p->target_class; + ci->proc = p; + mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p); + ci->pc = p->body.irep->iseq; + ci->nregs = p->body.irep->nregs; + ci[1] = ci[0]; + c->ci++; /* push dummy callinfo */ + + c->fib = f; + c->status = MRB_FIBER_CREATED; + + return self; +} + +static struct mrb_context* +fiber_check(mrb_state *mrb, mrb_value fib) +{ + struct RFiber *f = fiber_ptr(fib); + + mrb_assert(f->tt == MRB_TT_FIBER); + if (!f->cxt) { + mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber"); + } + return f->cxt; +} + +static mrb_value +fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len) +{ + if (len == 0) return mrb_nil_value(); + if (len == 1) return a[0]; + return mrb_ary_new_from_values(mrb, len, a); +} + +/* mark return from context modifying method */ +#define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL + +static void +fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c) +{ + mrb_callinfo *ci; + + for (ci = c->ci; ci >= c->cibase; ci--) { + if (ci->acc < 0) { + mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); + } + } +} + +static void +fiber_switch_context(mrb_state *mrb, struct mrb_context *c) +{ + if (mrb->c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); + } + c->status = MRB_FIBER_RUNNING; + mrb->c = c; +} + +static mrb_value +fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) +{ + struct mrb_context *c = fiber_check(mrb, self); + struct mrb_context *old_c = mrb->c; + mrb_value value; + + fiber_check_cfunc(mrb, c); + if (resume && c->status == MRB_FIBER_TRANSFERRED) { + mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber"); + } + if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) { + mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)"); + } + if (c->status == MRB_FIBER_TERMINATED) { + mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); + } + mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED; + c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); + if (c->status == MRB_FIBER_CREATED) { + mrb_value *b, *e; + + if (len >= c->stend - c->stack) { + mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber"); + } + b = c->stack+1; + e = b + len; + while (b<e) { + *b++ = *a++; + } + c->cibase->argc = len; + value = c->stack[0] = c->ci->proc->env->stack[0]; + } + else { + value = fiber_result(mrb, a, len); + } + fiber_switch_context(mrb, c); + + if (vmexec) { + c->vmexec = TRUE; + value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc); + mrb->c = old_c; + } + else { + MARK_CONTEXT_MODIFY(c); + } + return value; +} + +/* + * call-seq: + * fiber.resume(args, ...) -> obj + * + * Resumes the fiber from the point at which the last <code>Fiber.yield</code> + * was called, or starts running it if it is the first call to + * <code>resume</code>. Arguments passed to resume will be the value of + * the <code>Fiber.yield</code> expression or will be passed as block + * parameters to the fiber's block if this is the first <code>resume</code>. + * + * Alternatively, when resume is called it evaluates to the arguments passed + * to the next <code>Fiber.yield</code> statement inside the fiber's block + * or to the block value if it runs to completion without any + * <code>Fiber.yield</code> + */ +static mrb_value +fiber_resume(mrb_state *mrb, mrb_value self) +{ + mrb_value *a; + mrb_int len; + mrb_bool vmexec = FALSE; + + mrb_get_args(mrb, "*!", &a, &len); + if (mrb->c->ci->acc < 0) { + vmexec = TRUE; + } + return fiber_switch(mrb, self, len, a, TRUE, vmexec); +} + +/* resume thread with given arguments */ +MRB_API mrb_value +mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a) +{ + return fiber_switch(mrb, fib, len, a, TRUE, TRUE); +} + +/* + * call-seq: + * fiber.alive? -> true or false + * + * Returns true if the fiber can still be resumed. After finishing + * execution of the fiber block this method will always return false. + */ +static mrb_value +fiber_alive_p(mrb_state *mrb, mrb_value self) +{ + struct mrb_context *c = fiber_check(mrb, self); + return mrb_bool_value(c->status != MRB_FIBER_TERMINATED); +} + +static mrb_value +fiber_eq(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + + if (mrb_type(other) != MRB_TT_FIBER) { + return mrb_false_value(); + } + return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); +} + +/* + * call-seq: + * fiber.transfer(args, ...) -> obj + * + * Transfers control to receiver fiber of the method call. + * Unlike <code>resume</code> the receiver wouldn't be pushed to call + * stack of fibers. Instead it will switch to the call stack of + * transferring fiber. + * When resuming a fiber that was transferred to another fiber it would + * cause double resume error. Though when the fiber is re-transferred + * and <code>Fiber.yield</code> is called, the fiber would be resumable. + */ +static mrb_value +fiber_transfer(mrb_state *mrb, mrb_value self) +{ + struct mrb_context *c = fiber_check(mrb, self); + mrb_value* a; + mrb_int len; + + fiber_check_cfunc(mrb, mrb->c); + mrb_get_args(mrb, "*!", &a, &len); + + if (c == mrb->root_c) { + mrb->c->status = MRB_FIBER_TRANSFERRED; + fiber_switch_context(mrb, c); + MARK_CONTEXT_MODIFY(c); + return fiber_result(mrb, a, len); + } + + if (c == mrb->c) { + return fiber_result(mrb, a, len); + } + + return fiber_switch(mrb, self, len, a, FALSE, FALSE); +} + +/* yield values to the caller fiber */ +/* mrb_fiber_yield() must be called as `return mrb_fiber_yield(...)` */ +MRB_API mrb_value +mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) +{ + struct mrb_context *c = mrb->c; + + if (!c->prev) { + mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber"); + } + + fiber_check_cfunc(mrb, c); + c->prev->status = MRB_FIBER_RUNNING; + c->status = MRB_FIBER_SUSPENDED; + fiber_switch_context(mrb, c->prev); + c->prev = NULL; + if (c->vmexec) { + c->vmexec = FALSE; + mrb->c->ci->acc = CI_ACC_RESUMED; + } + MARK_CONTEXT_MODIFY(mrb->c); + return fiber_result(mrb, a, len); +} + +/* + * call-seq: + * Fiber.yield(args, ...) -> obj + * + * Yields control back to the context that resumed the fiber, passing + * along any arguments that were passed to it. The fiber will resume + * processing at this point when <code>resume</code> is called next. + * Any arguments passed to the next <code>resume</code> will be the + * + * mruby limitation: Fiber resume/yield cannot cross C function boundary. + * thus you cannot yield from #initialize which is called by mrb_funcall(). + */ +static mrb_value +fiber_yield(mrb_state *mrb, mrb_value self) +{ + mrb_value *a; + mrb_int len; + + mrb_get_args(mrb, "*!", &a, &len); + return mrb_fiber_yield(mrb, len, a); +} + +/* + * call-seq: + * Fiber.current() -> fiber + * + * Returns the current fiber. If you are not running in the context of + * a fiber this method will return the root fiber. + */ +static mrb_value +fiber_current(mrb_state *mrb, mrb_value self) +{ + if (!mrb->c->fib) { + struct RFiber *f = (struct RFiber*)mrb_obj_alloc(mrb, MRB_TT_FIBER, mrb_class_ptr(self)); + + f->cxt = mrb->c; + mrb->c->fib = f; + } + return mrb_obj_value(mrb->c->fib); +} + +void +mrb_mruby_fiber_gem_init(mrb_state* mrb) +{ + struct RClass *c; + + c = mrb_define_class(mrb, "Fiber", mrb->object_class); + MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER); + + mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); + mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); + mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY()); + mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE()); + mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1)); + + mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE()); + + mrb_define_class(mrb, "FiberError", mrb->eStandardError_class); +} + +void +mrb_mruby_fiber_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb new file mode 100644 index 00000000..d063a0a6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb @@ -0,0 +1,208 @@ +assert('Fiber.new') do + f = Fiber.new{} + assert_kind_of Fiber, f +end + +assert('Fiber#resume') do + f = Fiber.new{|x| x } + assert_equal 2, f.resume(2) +end + +assert('Fiber#transfer') do + f2 = nil + f1 = Fiber.new do |v| + Fiber.yield v + f2.transfer + end + f2 = Fiber.new do + f1.transfer(1) + f1.transfer(1) + Fiber.yield 2 + end + assert_equal 1, f2.resume + assert_raise(FiberError) { f2.resume } + assert_equal 2, f2.transfer + assert_raise(FiberError) { f1.resume } + f1.transfer + f2.resume + assert_false f1.alive? + assert_false f2.alive? +end + +assert('Fiber#alive?') do + f = Fiber.new{ Fiber.yield } + f.resume + assert_true f.alive? + f.resume + assert_false f.alive? +end + +assert('Fiber#==') do + root = Fiber.current + assert_equal root, root + assert_equal root, Fiber.current + assert_false root != Fiber.current + f = Fiber.new { + assert_false root == Fiber.current + } + f.resume + assert_false f == root + assert_true f != root +end + +assert('Fiber.yield') do + f = Fiber.new{|x| Fiber.yield x } + assert_equal 3, f.resume(3) + assert_true f.alive? +end + +assert('FiberError') do + assert_equal StandardError, FiberError.superclass +end + +assert('Fiber iteration') do + f1 = Fiber.new{ + [1,2,3].each{|x| Fiber.yield(x)} + } + f2 = Fiber.new{ + [9,8,7].each{|x| Fiber.yield(x)} + } + a = [] + 3.times { + a << f1.resume + a << f2.resume + } + assert_equal [1,9,2,8,3,7], a +end + +assert('Fiber with splat in the block argument list') { + Fiber.new{|*x|x}.resume(1) == [1] +} + +assert('Fiber raises on resume when dead') do + assert_raise(FiberError) do + f = Fiber.new{} + f.resume + assert_false f.alive? + f.resume + end +end + +assert('Yield raises when called on root fiber') do + assert_raise(FiberError) { Fiber.yield } +end + +assert('Double resume of Fiber') do + f1 = Fiber.new {} + f2 = Fiber.new { + f1.resume + assert_raise(FiberError) { f2.resume } + Fiber.yield 0 + } + assert_equal 0, f2.resume + f2.resume + assert_false f1.alive? + assert_false f2.alive? +end + +assert('Recursive resume of Fiber') do + f1, f2 = nil, nil + f1 = Fiber.new { assert_raise(FiberError) { f2.resume } } + f2 = Fiber.new { + f1.resume + Fiber.yield 0 + } + f3 = Fiber.new { + f2.resume + } + assert_equal 0, f3.resume + f2.resume + assert_false f1.alive? + assert_false f2.alive? + assert_false f3.alive? +end + +assert('Root fiber resume') do + root = Fiber.current + assert_raise(FiberError) { root.resume } + f = Fiber.new { + assert_raise(FiberError) { root.resume } + } + f.resume + assert_false f.alive? +end + +assert('Fiber without block') do + assert_raise(ArgumentError) { Fiber.new } +end + + +assert('Transfer to self.') do + result = [] + f = Fiber.new { result << :start; f.transfer; result << :end } + f.transfer + assert_equal [:start, :end], result + + result = [] + f = Fiber.new { result << :start; f.transfer; result << :end } + f.resume + assert_equal [:start, :end], result +end + +assert('Resume transferred fiber') do + f = Fiber.new { + assert_raise(FiberError) { f.resume } + } + f.transfer +end + +assert('Root fiber transfer.') do + result = nil + root = Fiber.current + f = Fiber.new { + result = :ok + root.transfer + } + f.resume + assert_true f.alive? + assert_equal :ok, result +end + +assert('Break nested fiber with root fiber transfer') do + root = Fiber.current + + result = nil + f2 = nil + f1 = Fiber.new { + Fiber.yield f2.resume + result = :f1 + } + f2 = Fiber.new { + result = :to_root + root.transfer :from_f2 + result = :f2 + } + assert_equal :from_f2, f1.resume + assert_equal :to_root, result + assert_equal :f2, f2.transfer + assert_equal :f2, result + assert_false f2.alive? + assert_equal :f1, f1.resume + assert_equal :f1, result + assert_false f1.alive? +end + +assert('CRuby Fiber#transfer test.') do + ary = [] + f2 = nil + f1 = Fiber.new{ + ary << f2.transfer(:foo) + :ok + } + f2 = Fiber.new{ + ary << f1.transfer(:baz) + :ng + } + assert_equal :ok, f1.transfer + assert_equal [:baz], ary +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake new file mode 100644 index 00000000..103410ab --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake @@ -0,0 +1,8 @@ +MRuby::Gem::Specification.new('mruby-hash-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Hash class extension' + spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext' + spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext' + spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb new file mode 100644 index 00000000..73d1fbe6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -0,0 +1,477 @@ +class Hash + + # ISO does not define Hash#each_pair, so each_pair is defined in gem. + alias each_pair each + + ## + # call-seq: + # Hash[ key, value, ... ] -> new_hash + # Hash[ [ [key, value], ... ] ] -> new_hash + # Hash[ object ] -> new_hash + # + # Creates a new hash populated with the given objects. + # + # Similar to the literal `{ _key_ => _value_, ... }`. In the first + # form, keys and values occur in pairs, so there must be an even number of + # arguments. + # + # The second and third form take a single argument which is either an array + # of key-value pairs or an object convertible to a hash. + # + # Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200} + # Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200} + # Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200} + # + + def self.[](*object) + length = object.length + if length == 1 + o = object[0] + if o.respond_to?(:to_hash) + h = self.new + object[0].to_hash.each { |k, v| h[k] = v } + return h + elsif o.respond_to?(:to_a) + h = self.new + o.to_a.each do |i| + raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a) + k, v = nil + case i.size + when 2 + k = i[0] + v = i[1] + when 1 + k = i[0] + else + raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)" + end + h[k] = v + end + return h + end + end + unless length % 2 == 0 + raise ArgumentError, 'odd number of arguments for Hash' + end + h = self.new + 0.step(length - 2, 2) do |i| + h[object[i]] = object[i + 1] + end + h + end + + ## + # call-seq: + # Hash.try_convert(obj) -> hash or nil + # + # Try to convert <i>obj</i> into a hash, using to_hash method. + # Returns converted hash or nil if <i>obj</i> cannot be converted + # for any reason. + # + # Hash.try_convert({1=>2}) # => {1=>2} + # Hash.try_convert("1=>2") # => nil + # + def self.try_convert(obj) + if obj.respond_to?(:to_hash) + obj.to_hash + else + nil + end + end + + ## + # call-seq: + # hsh.merge!(other_hash) -> hsh + # hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh + # + # Adds the contents of _other_hash_ to _hsh_. If no block is specified, + # entries with duplicate keys are overwritten with the values from + # _other_hash_, otherwise the value of each duplicate key is determined by + # calling the block with the key, its value in _hsh_ and its value in + # _other_hash_. + # + # h1 = { "a" => 100, "b" => 200 } + # h2 = { "b" => 254, "c" => 300 } + # h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300} + # + # h1 = { "a" => 100, "b" => 200 } + # h2 = { "b" => 254, "c" => 300 } + # h1.merge!(h2) { |key, v1, v2| v1 } + # #=> {"a"=>100, "b"=>200, "c"=>300} + # + + def merge!(other, &block) + raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash) + if block + other.each_key{|k| + self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k] + } + else + other.each_key{|k| self[k] = other[k]} + end + self + end + + alias update merge! + + ## + # call-seq: + # hsh.compact -> new_hsh + # + # Returns a new hash with the nil values/key pairs removed + # + # h = { a: 1, b: false, c: nil } + # h.compact #=> { a: 1, b: false } + # h #=> { a: 1, b: false, c: nil } + # + def compact + result = self.dup + result.compact! + result + end + + ## + # call-seq: + # hsh.fetch(key [, default] ) -> obj + # hsh.fetch(key) {| key | block } -> obj + # + # Returns a value from the hash for the given key. If the key can't be + # found, there are several options: With no other arguments, it will + # raise an <code>KeyError</code> exception; if <i>default</i> is + # given, then that will be returned; if the optional code block is + # specified, then that will be run and its result returned. + # + # h = { "a" => 100, "b" => 200 } + # h.fetch("a") #=> 100 + # h.fetch("z", "go fish") #=> "go fish" + # h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z" + # + # The following example shows that an exception is raised if the key + # is not found and a default value is not supplied. + # + # h = { "a" => 100, "b" => 200 } + # h.fetch("z") + # + # <em>produces:</em> + # + # prog.rb:2:in 'fetch': key not found (KeyError) + # from prog.rb:2 + # + + def fetch(key, none=NONE, &block) + unless self.key?(key) + if block + block.call(key) + elsif none != NONE + none + else + raise KeyError, "Key not found: #{key}" + end + else + self[key] + end + end + + ## + # call-seq: + # hsh.delete_if {| key, value | block } -> hsh + # hsh.delete_if -> an_enumerator + # + # Deletes every key-value pair from <i>hsh</i> for which <i>block</i> + # evaluates to <code>true</code>. + # + # If no block is given, an enumerator is returned instead. + # + # h = { "a" => 100, "b" => 200, "c" => 300 } + # h.delete_if {|key, value| key >= "b" } #=> {"a"=>100} + # + + def delete_if(&block) + return to_enum :delete_if unless block + + self.each do |k, v| + self.delete(k) if block.call(k, v) + end + self + end + + ## + # call-seq: + # hash.flatten -> an_array + # hash.flatten(level) -> an_array + # + # Returns a new array that is a one-dimensional flattening of this + # hash. That is, for every key or value that is an array, extract + # its elements into the new array. Unlike Array#flatten, this + # method does not flatten recursively by default. The optional + # <i>level</i> argument determines the level of recursion to flatten. + # + # a = {1=> "one", 2 => [2,"two"], 3 => "three"} + # a.flatten # => [1, "one", 2, [2, "two"], 3, "three"] + # a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"] + # + + def flatten(level=1) + self.to_a.flatten(level) + end + + ## + # call-seq: + # hsh.invert -> new_hash + # + # Returns a new hash created by using <i>hsh</i>'s values as keys, and + # the keys as values. + # + # h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 } + # h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"} + # + + def invert + h = self.class.new + self.each {|k, v| h[v] = k } + h + end + + ## + # call-seq: + # hsh.keep_if {| key, value | block } -> hsh + # hsh.keep_if -> an_enumerator + # + # Deletes every key-value pair from <i>hsh</i> for which <i>block</i> + # evaluates to false. + # + # If no block is given, an enumerator is returned instead. + # + + def keep_if(&block) + return to_enum :keep_if unless block + + keys = [] + self.each do |k, v| + unless block.call([k, v]) + self.delete(k) + end + end + self + end + + ## + # call-seq: + # hsh.key(value) -> key + # + # Returns the key of an occurrence of a given value. If the value is + # not found, returns <code>nil</code>. + # + # h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 } + # h.key(200) #=> "b" + # h.key(300) #=> "c" + # h.key(999) #=> nil + # + + def key(val) + self.each do |k, v| + return k if v == val + end + nil + end + + ## + # call-seq: + # hsh.to_h -> hsh or new_hash + # + # Returns +self+. If called on a subclass of Hash, converts + # the receiver to a Hash object. + # + def to_h + self + end + + ## + # call-seq: + # hash < other -> true or false + # + # Returns <code>true</code> if <i>hash</i> is subset of + # <i>other</i>. + # + # h1 = {a:1, b:2} + # h2 = {a:1, b:2, c:3} + # h1 < h2 #=> true + # h2 < h1 #=> false + # h1 < h1 #=> false + # + def <(hash) + begin + hash = hash.to_hash + rescue NoMethodError + raise TypeError, "can't convert #{hash.class} to Hash" + end + size < hash.size and all? {|key, val| + hash.key?(key) and hash[key] == val + } + end + + ## + # call-seq: + # hash <= other -> true or false + # + # Returns <code>true</code> if <i>hash</i> is subset of + # <i>other</i> or equals to <i>other</i>. + # + # h1 = {a:1, b:2} + # h2 = {a:1, b:2, c:3} + # h1 <= h2 #=> true + # h2 <= h1 #=> false + # h1 <= h1 #=> true + # + def <=(hash) + begin + hash = hash.to_hash + rescue NoMethodError + raise TypeError, "can't convert #{hash.class} to Hash" + end + size <= hash.size and all? {|key, val| + hash.key?(key) and hash[key] == val + } + end + + ## + # call-seq: + # hash > other -> true or false + # + # Returns <code>true</code> if <i>other</i> is subset of + # <i>hash</i>. + # + # h1 = {a:1, b:2} + # h2 = {a:1, b:2, c:3} + # h1 > h2 #=> false + # h2 > h1 #=> true + # h1 > h1 #=> false + # + def >(hash) + begin + hash = hash.to_hash + rescue NoMethodError + raise TypeError, "can't convert #{hash.class} to Hash" + end + size > hash.size and hash.all? {|key, val| + key?(key) and self[key] == val + } + end + + ## + # call-seq: + # hash >= other -> true or false + # + # Returns <code>true</code> if <i>other</i> is subset of + # <i>hash</i> or equals to <i>hash</i>. + # + # h1 = {a:1, b:2} + # h2 = {a:1, b:2, c:3} + # h1 >= h2 #=> false + # h2 >= h1 #=> true + # h1 >= h1 #=> true + # + def >=(hash) + begin + hash = hash.to_hash + rescue NoMethodError + raise TypeError, "can't convert #{hash.class} to Hash" + end + size >= hash.size and hash.all? {|key, val| + key?(key) and self[key] == val + } + end + + ## + # call-seq: + # hsh.dig(key,...) -> object + # + # Extracts the nested value specified by the sequence of <i>key</i> + # objects by calling +dig+ at each step, returning +nil+ if any + # intermediate step is +nil+. + # + def dig(idx,*args) + n = self[idx] + if args.size > 0 + n&.dig(*args) + else + n + end + end + + ## + # call-seq: + # hsh.transform_keys {|key| block } -> new_hash + # hsh.transform_keys -> an_enumerator + # + # Returns a new hash, with the keys computed from running the block + # once for each key in the hash, and the values unchanged. + # + # If no block is given, an enumerator is returned instead. + # + def transform_keys(&block) + return to_enum :transform_keys unless block + hash = {} + self.keys.each do |k| + new_key = block.call(k) + hash[new_key] = self[k] + end + hash + end + ## + # call-seq: + # hsh.transform_keys! {|key| block } -> hsh + # hsh.transform_keys! -> an_enumerator + # + # Invokes the given block once for each key in <i>hsh</i>, replacing it + # with the new key returned by the block, and then returns <i>hsh</i>. + # + # If no block is given, an enumerator is returned instead. + # + def transform_keys!(&block) + return to_enum :transform_keys! unless block + self.keys.each do |k| + value = self[k] + new_key = block.call(k) + self.__delete(k) + self[new_key] = value + end + self + end + ## + # call-seq: + # hsh.transform_values {|value| block } -> new_hash + # hsh.transform_values -> an_enumerator + # + # Returns a new hash with the results of running the block once for + # every value. + # This method does not change the keys. + # + # If no block is given, an enumerator is returned instead. + # + def transform_values(&b) + return to_enum :transform_values unless block_given? + hash = {} + self.keys.each do |k| + hash[k] = yield(self[k]) + end + hash + end + ## + # call-seq: + # hsh.transform_values! {|key| block } -> hsh + # hsh.transform_values! -> an_enumerator + # + # Invokes the given block once for each value in the hash, replacing + # with the new value returned by the block, and then returns <i>hsh</i>. + # + # If no block is given, an enumerator is returned instead. + # + def transform_values!(&b) + return to_enum :transform_values! unless block_given? + self.keys.each do |k| + self[k] = yield(self[k]) + end + self + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c new file mode 100644 index 00000000..952f2eb6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c @@ -0,0 +1,88 @@ +/* +** hash.c - Hash class +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/hash.h> + +/* + * call-seq: + * hsh.values_at(key, ...) -> array + * + * Return an array containing the values associated with the given keys. + * Also see <code>Hash.select</code>. + * + * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } + * h.values_at("cow", "cat") #=> ["bovine", "feline"] + */ + +static mrb_value +hash_values_at(mrb_state *mrb, mrb_value hash) +{ + mrb_value *argv, result; + mrb_int argc, i; + int ai; + + mrb_get_args(mrb, "*", &argv, &argc); + result = mrb_ary_new_capa(mrb, argc); + ai = mrb_gc_arena_save(mrb); + for (i = 0; i < argc; i++) { + mrb_ary_push(mrb, result, mrb_hash_get(mrb, hash, argv[i])); + mrb_gc_arena_restore(mrb, ai); + } + return result; +} + +/* + * call-seq: + * hsh.compact! -> hsh + * + * Removes all nil values from the hash. Returns the hash. + * + * h = { a: 1, b: false, c: nil } + * h.compact! #=> { a: 1, b: false } + */ +static mrb_value +hash_compact_bang(mrb_state *mrb, mrb_value hash) +{ + khiter_t k; + khash_t(ht) *h = RHASH_TBL(hash); + mrb_int n = -1; + + if (!h) return mrb_nil_value(); + for (k = kh_begin(h); k != kh_end(h); k++) { + if (kh_exist(h, k)) { + mrb_value val = kh_value(h, k).v; + khiter_t k2; + + if (mrb_nil_p(val)) { + kh_del(ht, mrb, h, k); + n = kh_value(h, k).n; + for (k2 = kh_begin(h); k2 != kh_end(h); k2++) { + if (!kh_exist(h, k2)) continue; + if (kh_value(h, k2).n > n) kh_value(h, k2).n--; + } + } + } + } + if (n < 0) return mrb_nil_value(); + return hash; +} + +void +mrb_mruby_hash_ext_gem_init(mrb_state *mrb) +{ + struct RClass *h; + + h = mrb->hash_class; + mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY()); + mrb_define_method(mrb, h, "compact!", hash_compact_bang, MRB_ARGS_NONE()); +} + +void +mrb_mruby_hash_ext_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb new file mode 100644 index 00000000..ca4e346f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb @@ -0,0 +1,294 @@ +## +# Hash(Ext) Test + +assert('Hash.[] Hash') do + a = Hash['a_key' => 'a_value'] + + assert_equal({'a_key' => 'a_value'}, a) +end + +assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do + a = Hash[ [ ['b_key', 'b_value'] ] ] + + assert_equal({'b_key' => 'b_value'}, a) + + a = Hash[ [ ] ] + + assert_equal({}, a) + + assert_raise(ArgumentError) do + Hash[ [ ['b_key', 'b_value', 'b_over'] ] ] + end + + assert_raise(ArgumentError) do + Hash[ [ [] ] ] + end +end + +assert('Hash.[] "c_key", "c_value"') do + a = Hash['c_key', 'c_value', 'd_key', 1] + + assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a) + + a = Hash[] + + assert_equal({}, a) + + assert_raise(ArgumentError) do + Hash['d_key'] + end +end + +assert('Hash.[] for sub class') do + sub_hash_class = Class.new(Hash) + sub_hash = sub_hash_class[] + assert_equal(sub_hash_class, sub_hash.class) +end + +assert('Hash.try_convert') do + assert_nil Hash.try_convert(nil) + assert_nil Hash.try_convert("{1=>2}") + assert_equal({1=>2}, Hash.try_convert({1=>2})) +end + +assert('Hash#merge!') do + a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } + b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' } + + result_1 = a.merge! b + + a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } + result_2 = a.merge!(b) do |key, original, new| + original + end + + assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX', + 'xyz_key' => 'xyz_value' }, result_1) + assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value', + 'xyz_key' => 'xyz_value' }, result_2) + + assert_raise(TypeError) do + { 'abc_key' => 'abc_value' }.merge! "a" + end +end + +assert('Hash#values_at') do + h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } + assert_equal ["bovine", "feline"], h.values_at("cow", "cat") + + keys = [] + (0...1000).each { |v| keys.push "#{v}" } + h = Hash.new { |hash,k| hash[k] = k } + assert_equal keys, h.values_at(*keys) +end + +assert('Hash#compact') do + h = { "cat" => "feline", "dog" => nil, "cow" => false } + + assert_equal({ "cat" => "feline", "cow" => false }, h.compact) + assert_equal({ "cat" => "feline", "dog" => nil, "cow" => false }, h) +end + +assert('Hash#compact!') do + h = { "cat" => "feline", "dog" => nil, "cow" => false } + + h.compact! + assert_equal({ "cat" => "feline", "cow" => false }, h) +end + +assert('Hash#fetch') do + h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } + assert_equal "feline", h.fetch("cat") + assert_equal "mickey", h.fetch("mouse", "mickey") + assert_equal "minny", h.fetch("mouse"){"minny"} + assert_equal "mouse", h.fetch("mouse"){|k| k} + assert_raise(KeyError) do + h.fetch("gnu") + end +end + +assert("Hash#delete_if") do + base = { 1 => 'one', 2 => false, true => 'true', 'cat' => 99 } + h1 = { 1 => 'one', 2 => false, true => 'true' } + h2 = { 2 => false, 'cat' => 99 } + h3 = { 2 => false } + + h = base.dup + assert_equal(h, h.delete_if { false }) + assert_equal({}, h.delete_if { true }) + + h = base.dup + assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) }) + assert_equal(h1, h) + + h = base.dup + assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) }) + assert_equal(h2, h) + + h = base.dup + assert_equal(h3, h.delete_if {|k,v| v }) + assert_equal(h3, h) + + h = base.dup + n = 0 + h.delete_if {|*a| + n += 1 + assert_equal(2, a.size) + assert_equal(base[a[0]], a[1]) + h.shift + true + } + assert_equal(base.size, n) +end + +assert("Hash#flatten") do + a = {1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]} + assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten + assert_equal [[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0) + assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1) + assert_equal [1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2) + assert_equal [1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3) +end + +assert("Hash#invert") do + h = { 1 => 'one', 2 => 'two', 3 => 'three', + true => 'true', nil => 'nil' }.invert + assert_equal 1, h['one'] + assert_equal true, h['true'] + assert_equal nil, h['nil'] + + h = { 'a' => 1, 'b' => 2, 'c' => 1 }.invert + assert_equal(2, h.length) + assert_include(%w[a c], h[1]) + assert_equal('b', h[2]) +end + +assert("Hash#invert with sub class") do + sub_hash_class = Class.new(Hash) + sub_hash = sub_hash_class.new + assert_equal(sub_hash_class, sub_hash.invert.class) +end + +assert("Hash#keep_if") do + h = { 1 => 2, 3 => 4, 5 => 6 } + assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 }) + h = { 1 => 2, 3 => 4, 5 => 6 } + assert_equal({ 1 => 2, 3=> 4, 5 =>6} , h.keep_if { true }) +end + +assert("Hash#key") do + h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300, nil => 'nil', 'nil' => nil } + assert_equal "b", h.key(200) + assert_equal "c", h.key(300) + assert_nil h.key(999) + assert_nil h.key('nil') + assert_equal 'nil', h.key(nil) +end + +assert("Hash#to_h") do + h = { "a" => 100, "b" => 200 } + assert_equal Hash, h.to_h.class + assert_equal h, h.to_h +end + +assert('Hash#<') do + h1 = {a:1, b:2} + h2 = {a:1, b:2, c:3} + + assert_false(h1 < h1) + assert_true(h1 < h2) + assert_false(h2 < h1) + assert_false(h2 < h2) + + h1 = {a:1} + h2 = {a:2} + + assert_false(h1 < h1) + assert_false(h1 < h2) + assert_false(h2 < h1) + assert_false(h2 < h2) +end + +assert('Hash#<=') do + h1 = {a:1, b:2} + h2 = {a:1, b:2, c:3} + + assert_true(h1 <= h1) + assert_true(h1 <= h2) + assert_false(h2 <= h1) + assert_true(h2 <= h2) + + h1 = {a:1} + h2 = {a:2} + + assert_true(h1 <= h1) + assert_false(h1 <= h2) + assert_false(h2 <= h1) + assert_true(h2 <= h2) +end + +assert('Hash#>=') do + h1 = {a:1, b:2} + h2 = {a:1, b:2, c:3} + + assert_true(h1 >= h1) + assert_false(h1 >= h2) + assert_true(h2 >= h1) + assert_true(h2 >= h2) + + h1 = {a:1} + h2 = {a:2} + + assert_true(h1 >= h1) + assert_false(h1 >= h2) + assert_false(h2 >= h1) + assert_true(h2 >= h2) +end + +assert('Hash#>') do + h1 = {a:1, b:2} + h2 = {a:1, b:2, c:3} + + assert_false(h1 > h1) + assert_false(h1 > h2) + assert_true(h2 > h1) + assert_false(h2 > h2) + + h1 = {a:1} + h2 = {a:2} + + assert_false(h1 > h1) + assert_false(h1 > h2) + assert_false(h2 > h1) + assert_false(h2 > h2) +end + +assert("Hash#dig") do + h = {a:{b:{c:1}}} + assert_equal(1, h.dig(:a, :b, :c)) + assert_nil(h.dig(:d)) +end + +assert("Hash#transform_keys") do + h = {"1" => 100, "2" => 200} + assert_equal(h.transform_keys{|k| k+"!"}, + {"1!" => 100, "2!" => 200}) + assert_equal(h.transform_keys{|k|k.to_i}, + {1 => 100, 2 => 200}) + assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"}, + {"1.0" => 100, "2.1" => 200}) + assert_equal(h.transform_keys!{|k|k.to_i}, h) + assert_equal(h, {1 => 100, 2 => 200}) +end + +assert("Hash#transform_values") do + h = {a: 1, b: 2, c: 3} + assert_equal(h.transform_values{|v| v * v + 1}, + {a: 2, b: 5, c: 10}) + assert_equal(h.transform_values{|v|v.to_s}, + {a: "1", b: "2", c: "3"}) + assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"}, + {a: "1.0", b: "2.1", c: "3.2"}) + assert_equal(h.transform_values!{|v|v.to_s}, h) + assert_equal(h, {a: "1", b: "2", c: "3"}) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake new file mode 100644 index 00000000..91ad9f44 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-inline-struct') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'inline structure' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c new file mode 100644 index 00000000..0baaab61 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c @@ -0,0 +1,83 @@ +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/string.h> +#include <mruby/istruct.h> + +static mrb_value +istruct_test_initialize(mrb_state *mrb, mrb_value self) +{ + char *string = (char*)mrb_istruct_ptr(self); + mrb_int size = mrb_istruct_size(); + mrb_value object; + mrb_get_args(mrb, "o", &object); + + if (mrb_float_p(object)) + { + snprintf(string, size, "float(%.3f)", mrb_float(object)); + } + else if (mrb_fixnum_p(object)) + { + snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object)); + } + else if (mrb_string_p(object)) + { + snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object)); + } + + string[size - 1] = 0; // force NULL at the end + return self; +} + +static mrb_value +istruct_test_to_s(mrb_state *mrb, mrb_value self) +{ + return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self)); +} + +static mrb_value +istruct_test_length(mrb_state *mrb, mrb_value self) +{ + return mrb_fixnum_value(mrb_istruct_size()); +} + +static mrb_value +istruct_test_test_receive(mrb_state *mrb, mrb_value self) +{ + mrb_value object; + mrb_get_args(mrb, "o", &object); + if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest")) + { + mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest"); + } + return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's'); +} + +static mrb_value +istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self) +{ + char *ptr; + mrb_get_args(mrb, "I", &ptr); + return mrb_bool_value(ptr[0] == 's'); +} + +static mrb_value +istruct_test_mutate(mrb_state *mrb, mrb_value self) +{ + char *ptr = (char*)mrb_istruct_ptr(self); + memcpy(ptr, "mutate", 6); + return mrb_nil_value(); +} + +void mrb_mruby_inline_struct_gem_test(mrb_state *mrb) +{ + struct RClass *cls; + + cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class); + MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT); + mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb new file mode 100644 index 00000000..49585923 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb @@ -0,0 +1,151 @@ +## +# InlineStruct Test + +class InlineStructTest + def extra_method + :ok + end + + def test_ivar_set + @var = :ivar + end + + def test_ivar_get + @vat + end +end + +assert('InlineStructTest#dup') do + obj = InlineStructTest.new(1) + assert_equal obj.to_s, 'fixnum(1)' + assert_equal obj.dup.to_s, 'fixnum(1)' +end + +assert('InlineStructTest#clone') do + obj = InlineStructTest.new(1) + assert_equal obj.to_s, 'fixnum(1)' + assert_equal obj.clone.to_s, 'fixnum(1)' +end + +assert('InlineStruct#object_id') do + obj1 = InlineStructTest.new(1) + obj2 = InlineStructTest.new(1) + assert_not_equal obj1, obj2 + assert_not_equal obj1.object_id, obj2.object_id + assert_not_equal obj1.object_id, obj1.dup.object_id + assert_not_equal obj1.object_id, obj1.clone.object_id +end + +assert('InlineStructTest#mutate (dup)') do + obj1 = InlineStructTest.new("foo") + assert_equal obj1.to_s, "string(foo)" + obj2 = obj1.dup + assert_equal obj2.to_s, "string(foo)" + obj1.mutate + assert_equal obj1.to_s, "mutate(foo)" + assert_equal obj2.to_s, "string(foo)" +end + +assert('InlineStructTest#mutate (clone)') do + obj1 = InlineStructTest.new("foo") + assert_equal obj1.to_s, "string(foo)" + obj2 = obj1.clone + assert_equal obj2.to_s, "string(foo)" + obj1.mutate + assert_equal obj1.to_s, "mutate(foo)" + assert_equal obj2.to_s, "string(foo)" +end + +assert('InlineStructTest#test_receive(string)') do + assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true +end + +assert('InlineStructTest#test_receive(float)') do + assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false +end + +assert('InlineStructTest#test_receive(invalid object)') do + assert_raise(TypeError) do + InlineStructTest.test_receive([]) + end +end + +assert('InlineStructTest#test_receive(string)') do + assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true +end + +assert('InlineStructTest#test_receive(float)') do + assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false +end + +assert('InlineStructTest#test_receive(invalid object)') do + assert_raise(TypeError) do + InlineStructTest.test_receive_direct([]) + end +end + +assert('InlineStructTest#extra_method') do + assert_equal InlineStructTest.new(1).extra_method, :ok +end + +assert('InlineStructTest instance variable') do + obj = InlineStructTest.new(1) + assert_raise(ArgumentError) do + obj.test_ivar_set + end + assert_equal obj.test_ivar_get, nil +end + +# 64-bit mode +if InlineStructTest.length == 24 + assert('InlineStructTest length [64 bit]') do + assert_equal InlineStructTest.length, 3 * 8 + end + + assert('InlineStructTest w/float [64 bit]') do + obj = InlineStructTest.new(1.25) + assert_equal obj.to_s, "float(1.250)" + end + + assert('InlineStructTest w/fixnum [64 bit]') do + obj = InlineStructTest.new(42) + assert_equal obj.to_s, "fixnum(42)" + end + + assert('InlineStructTest w/string [64 bit]') do + obj = InlineStructTest.new("hello") + assert_equal obj.to_s, "string(hello)" + end + + assert('InlineStructTest w/long string [64 bit]') do + obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure") + assert_equal obj.to_s, "string(this won't fit i" + end +end + +# 32-bit mode +if InlineStructTest.length == 12 + assert('InlineStructTest length [32 bit]') do + assert_equal InlineStructTest.length, 3 * 4 + end + + assert('InlineStructTest w/float [32 bit]') do + obj = InlineStructTest.new(1.25) + assert_equal obj.to_s, "float(1.250" + end + + assert('InlineStructTest w/fixnum [32 bit]') do + obj = InlineStructTest.new(42) + assert_equal obj.to_s, "fixnum(42)" + end + + assert('InlineStructTest w/string [32 bit]') do + obj = InlineStructTest.new("hello") + assert_equal obj.to_s, "string(hell" + end + + assert('InlineStructTest w/long string [32 bit]') do + obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure") + assert_equal obj.to_s, "string(this" + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake new file mode 100644 index 00000000..fcb3a83b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Kernel module extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c new file mode 100644 index 00000000..7e6fa28b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c @@ -0,0 +1,243 @@ +#include <mruby.h> +#include <mruby/error.h> +#include <mruby/array.h> +#include <mruby/hash.h> +#include <mruby/range.h> + +static mrb_value +mrb_f_caller(mrb_state *mrb, mrb_value self) +{ + mrb_value bt, v, length; + mrb_int bt_len, argc, lev, n; + + bt = mrb_get_backtrace(mrb); + bt_len = RARRAY_LEN(bt); + argc = mrb_get_args(mrb, "|oo", &v, &length); + + switch (argc) { + case 0: + lev = 1; + n = bt_len - lev; + break; + case 1: + if (mrb_type(v) == MRB_TT_RANGE) { + mrb_int beg, len; + if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) { + lev = beg; + n = len; + } + else { + return mrb_nil_value(); + } + } + else { + v = mrb_to_int(mrb, v); + lev = mrb_fixnum(v); + if (lev < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); + } + n = bt_len - lev; + } + break; + case 2: + lev = mrb_fixnum(mrb_to_int(mrb, v)); + n = mrb_fixnum(mrb_to_int(mrb, length)); + if (lev < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); + } + if (n < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length); + } + break; + default: + lev = n = 0; + break; + } + + if (n == 0) { + return mrb_ary_new(mrb); + } + + return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n)); +} + +/* + * call-seq: + * __method__ -> symbol + * + * Returns the name at the definition of the current method as a + * Symbol. + * If called outside of a method, it returns <code>nil</code>. + * + */ +static mrb_value +mrb_f_method(mrb_state *mrb, mrb_value self) +{ + mrb_callinfo *ci = mrb->c->ci; + ci--; + if (ci->mid) + return mrb_symbol_value(ci->mid); + else + return mrb_nil_value(); +} + +/* + * call-seq: + * Integer(arg,base=0) -> integer + * + * Converts <i>arg</i> to a <code>Fixnum</code>. + * Numeric types are converted directly (with floating point numbers + * being truncated). <i>base</i> (0, or between 2 and 36) is a base for + * integer string representation. If <i>arg</i> is a <code>String</code>, + * when <i>base</i> is omitted or equals to zero, radix indicators + * (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored. + * In any case, strings should be strictly conformed to numeric + * representation. This behavior is different from that of + * <code>String#to_i</code>. Non string values will be converted using + * <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code> + * raises a TypeError. + * + * Integer(123.999) #=> 123 + * Integer("0x1a") #=> 26 + * Integer(Time.new) #=> 1204973019 + * Integer("0930", 10) #=> 930 + * Integer("111", 2) #=> 7 + * Integer(nil) #=> TypeError + */ +static mrb_value +mrb_f_integer(mrb_state *mrb, mrb_value self) +{ + mrb_value arg; + mrb_int base = 0; + + mrb_get_args(mrb, "o|i", &arg, &base); + return mrb_convert_to_integer(mrb, arg, base); +} + +/* + * call-seq: + * Float(arg) -> float + * + * Returns <i>arg</i> converted to a float. Numeric types are converted + * directly, the rest are converted using <i>arg</i>.to_f. + * + * Float(1) #=> 1.0 + * Float(123.456) #=> 123.456 + * Float("123.456") #=> 123.456 + * Float(nil) #=> TypeError + */ +static mrb_value +mrb_f_float(mrb_state *mrb, mrb_value self) +{ + mrb_value arg; + + mrb_get_args(mrb, "o", &arg); + return mrb_Float(mrb, arg); +} + +/* + * call-seq: + * String(arg) -> string + * + * Returns <i>arg</i> as an <code>String</code>. + * + * First tries to call its <code>to_str</code> method, then its to_s method. + * + * String(self) #=> "main" + * String(self.class) #=> "Object" + * String(123456) #=> "123456" + */ +static mrb_value +mrb_f_string(mrb_state *mrb, mrb_value self) +{ + mrb_value arg, tmp; + + mrb_get_args(mrb, "o", &arg); + tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str"); + if (mrb_nil_p(tmp)) { + tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s"); + } + return tmp; +} + +/* + * call-seq: + * Array(arg) -> array + * + * Returns +arg+ as an Array. + * + * First tries to call Array#to_ary on +arg+, then Array#to_a. + * + * Array(1..5) #=> [1, 2, 3, 4, 5] + * + */ +static mrb_value +mrb_f_array(mrb_state *mrb, mrb_value self) +{ + mrb_value arg, tmp; + + mrb_get_args(mrb, "o", &arg); + tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary"); + if (mrb_nil_p(tmp)) { + tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a"); + } + if (mrb_nil_p(tmp)) { + return mrb_ary_new_from_values(mrb, 1, &arg); + } + + return tmp; +} + +/* + * call-seq: + * Hash(arg) -> hash + * + * Converts <i>arg</i> to a <code>Hash</code> by calling + * <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when + * <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>. + * + * Hash([]) #=> {} + * Hash(nil) #=> {} + * Hash(key: :value) #=> {:key => :value} + * Hash([1, 2, 3]) #=> TypeError + * + */ +static mrb_value +mrb_f_hash(mrb_state *mrb, mrb_value self) +{ + mrb_value arg, tmp; + + mrb_get_args(mrb, "o", &arg); + if (mrb_nil_p(arg)) { + return mrb_hash_new(mrb); + } + tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash"); + if (mrb_nil_p(tmp)) { + if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) { + return mrb_hash_new(mrb); + } + mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash", + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg))); + } + return tmp; +} + +void +mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) +{ + struct RClass *krn = mrb->kernel_module; + + mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2)); + mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2)); + mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE()); + mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY()); + mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1)); +} + +void +mrb_mruby_kernel_ext_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb new file mode 100644 index 00000000..89cedaf2 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb @@ -0,0 +1,86 @@ +assert('Kernel.fail, Kernel#fail') do + assert_raise(RuntimeError) { fail } + assert_raise(RuntimeError) { Kernel.fail } +end + +assert('Kernel.caller, Kernel#caller') do + skip "backtrace isn't available" if caller(0).empty? + + caller_lineno = __LINE__ + 3 + c = Class.new do + def foo(*args) + caller(*args) + end + + def bar(*args) + foo(*args) + end + + def baz(*args) + bar(*args) + end + end + assert_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1] + assert_equal "bar", c.new.baz[0][-3..-1] + assert_equal "foo", c.new.baz(0)[0][-3..-1] + assert_equal "bar", c.new.baz(1)[0][-3..-1] + assert_equal "baz", c.new.baz(2)[0][-3..-1] + assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] } + assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] } + assert_nil c.new.baz(10..20) + assert_raise(ArgumentError) { c.new.baz(-1) } + assert_raise(ArgumentError) { c.new.baz(-1, 1) } + assert_raise(ArgumentError) { c.new.baz(1, -1) } + assert_raise(TypeError) { c.new.baz(nil) } +end + +assert('Kernel#__method__') do + assert_equal(:m, Class.new {def m; __method__; end}.new.m) + assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m) + c = Class.new do + [:m1, :m2].each do |m| + define_method(m) do + __method__ + end + end + end + assert_equal(:m1, c.new.m1) + assert_equal(:m2, c.new.m2) +end + +assert('Kernel#Integer') do + assert_equal(123, Integer(123.999)) + assert_equal(26, Integer("0x1a")) + assert_equal(930, Integer("0930", 10)) + assert_equal(7, Integer("111", 2)) + assert_equal(0, Integer("0")) + assert_equal(0, Integer("00000")) + assert_raise(TypeError) { Integer(nil) } +end + +assert('Kernel#Float') do + assert_equal(1.0, Float(1)) + assert_equal(123.456, Float(123.456)) + assert_equal(123.456, Float("123.456")) + assert_raise(TypeError) { Float(nil) } +end + +assert('Kernel#String') do + assert_equal("main", String(self)) + assert_equal("Object", String(self.class)) + assert_equal("123456", String(123456)) +end + +assert('Kernel#Array') do + assert_equal([1], Kernel.Array(1)) + assert_equal([1, 2, 3, 4, 5], Kernel.Array([1, 2, 3, 4, 5])) + assert_equal([1, 2, 3, 4, 5], Kernel.Array(1..5)) + assert_equal([[:a, 1], [:b, 2], [:c, 3]], Kernel.Array({a:1, b:2, c:3})) +end + +assert('Kernel#Hash') do + assert_equal({}, Hash([])) + assert_equal({}, Hash(nil)) + assert_equal({:key => :value}, Hash(key: :value)) + assert_raise(TypeError) { Hash([1, 2, 3]) } +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake new file mode 100644 index 00000000..66eb5c8d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-math') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard Math module' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c new file mode 100644 index 00000000..7302b92d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c @@ -0,0 +1,783 @@ +/* +** math.c - Math module +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/array.h> + +#include <math.h> + +static void +domain_error(mrb_state *mrb, const char *func) +{ + struct RClass *math = mrb_module_get(mrb, "Math"); + struct RClass *domainerror = mrb_class_get_under(mrb, math, "DomainError"); + mrb_value str = mrb_str_new_cstr(mrb, func); + mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %S", str); +} + +/* math functions not provided by Microsoft Visual C++ 2012 or older */ +#if defined _MSC_VER && _MSC_VER <= 1700 + +#include <float.h> + +#define MATH_TOLERANCE 1E-12 + +double +asinh(double x) +{ + double xa, ya, y; + + /* Basic formula loses precision for x < 0, but asinh is an odd function */ + xa = fabs(x); + if (xa > 3.16227E+18) { + /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ + ya = log(xa) + 0.69314718055994530942; + } + else { + /* Basic formula for asinh */ + ya = log(xa + sqrt(xa*xa + 1.0)); + } + + y = _copysign(ya, x); + return y; +} + +double +acosh(double x) +{ + double y; + + if (x > 3.16227E+18) { + /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ + y = log(x) + 0.69314718055994530942; + } + else { + /* Basic formula for acosh */ + y = log(x + sqrt(x*x - 1.0)); + } + + return y; +} + +double +atanh(double x) +{ + double y; + + if (fabs(x) < 1E-2) { + /* The sums 1+x and 1-x lose precision for small x. Use the polynomial + instead. */ + double x2 = x * x; + y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0)))); + } + else { + /* Basic formula for atanh */ + y = 0.5 * (log(1.0+x) - log(1.0-x)); + } + + return y; +} + +double +cbrt(double x) +{ + double xa, ya, y; + + /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an + odd function */ + xa = fabs(x); + ya = pow(xa, 1.0/3.0); + y = _copysign(ya, x); + return y; +} + +/* Declaration of complementary Error function */ +double +erfc(double x); + +/* +** Implementations of error functions +** credits to http://www.digitalmars.com/archives/cplusplus/3634.html +*/ + +/* Implementation of Error function */ +double +erf(double x) +{ + static const double two_sqrtpi = 1.128379167095512574; + double sum = x; + double term = x; + double xsqr = x*x; + int j= 1; + if (fabs(x) > 2.2) { + return 1.0 - erfc(x); + } + do { + term *= xsqr/j; + sum -= term/(2*j+1); + ++j; + term *= xsqr/j; + sum += term/(2*j+1); + ++j; + } while (fabs(term/sum) > MATH_TOLERANCE); + return two_sqrtpi*sum; +} + +/* Implementation of complementary Error function */ +double +erfc(double x) +{ + static const double one_sqrtpi= 0.564189583547756287; + double a = 1; + double b = x; + double c = x; + double d = x*x+0.5; + double q1; + double q2 = b/d; + double n = 1.0; + double t; + if (fabs(x) < 2.2) { + return 1.0 - erf(x); + } + if (x < 0.0) { /*signbit(x)*/ + return 2.0 - erfc(-x); + } + do { + t = a*n+b*x; + a = b; + b = t; + t = c*n+d*x; + c = d; + d = t; + n += 0.5; + q1 = q2; + q2 = b/d; + } while (fabs(q1-q2)/q2 > MATH_TOLERANCE); + return one_sqrtpi*exp(-x*x)*q2; +} + +#endif + +#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000) + +double +log2(double x) +{ + return log10(x)/log10(2.0); +} + +#endif + +/* + TRIGONOMETRIC FUNCTIONS +*/ + +/* + * call-seq: + * Math.sin(x) -> float + * + * Computes the sine of <i>x</i> (expressed in radians). Returns + * -1..1. + */ +static mrb_value +math_sin(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = sin(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.cos(x) -> float + * + * Computes the cosine of <i>x</i> (expressed in radians). Returns + * -1..1. + */ +static mrb_value +math_cos(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = cos(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.tan(x) -> float + * + * Returns the tangent of <i>x</i> (expressed in radians). + */ +static mrb_value +math_tan(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = tan(x); + + return mrb_float_value(mrb, x); +} + +/* + INVERSE TRIGONOMETRIC FUNCTIONS +*/ + +/* + * call-seq: + * Math.asin(x) -> float + * + * Computes the arc sine of <i>x</i>. + * @return computed value between `-(PI/2)` and `(PI/2)`. + */ +static mrb_value +math_asin(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < -1.0 || x > 1.0) { + domain_error(mrb, "asin"); + } + x = asin(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.acos(x) -> float + * + * Computes the arc cosine of <i>x</i>. Returns 0..PI. + */ +static mrb_value +math_acos(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < -1.0 || x > 1.0) { + domain_error(mrb, "acos"); + } + x = acos(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.atan(x) -> float + * + * Computes the arc tangent of <i>x</i>. Returns `-(PI/2) .. (PI/2)`. + */ +static mrb_value +math_atan(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = atan(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.atan2(y, x) -> float + * + * Computes the arc tangent given <i>y</i> and <i>x</i>. Returns + * -PI..PI. + * + * Math.atan2(-0.0, -1.0) #=> -3.141592653589793 + * Math.atan2(-1.0, -1.0) #=> -2.356194490192345 + * Math.atan2(-1.0, 0.0) #=> -1.5707963267948966 + * Math.atan2(-1.0, 1.0) #=> -0.7853981633974483 + * Math.atan2(-0.0, 1.0) #=> -0.0 + * Math.atan2(0.0, 1.0) #=> 0.0 + * Math.atan2(1.0, 1.0) #=> 0.7853981633974483 + * Math.atan2(1.0, 0.0) #=> 1.5707963267948966 + * Math.atan2(1.0, -1.0) #=> 2.356194490192345 + * Math.atan2(0.0, -1.0) #=> 3.141592653589793 + * + */ +static mrb_value +math_atan2(mrb_state *mrb, mrb_value obj) +{ + mrb_float x, y; + + mrb_get_args(mrb, "ff", &x, &y); + x = atan2(x, y); + + return mrb_float_value(mrb, x); +} + + + +/* + HYPERBOLIC TRIG FUNCTIONS +*/ +/* + * call-seq: + * Math.sinh(x) -> float + * + * Computes the hyperbolic sine of <i>x</i> (expressed in + * radians). + */ +static mrb_value +math_sinh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = sinh(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.cosh(x) -> float + * + * Computes the hyperbolic cosine of <i>x</i> (expressed in radians). + */ +static mrb_value +math_cosh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = cosh(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.tanh() -> float + * + * Computes the hyperbolic tangent of <i>x</i> (expressed in + * radians). + */ +static mrb_value +math_tanh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = tanh(x); + + return mrb_float_value(mrb, x); +} + + +/* + INVERSE HYPERBOLIC TRIG FUNCTIONS +*/ + +/* + * call-seq: + * Math.asinh(x) -> float + * + * Computes the inverse hyperbolic sine of <i>x</i>. + */ +static mrb_value +math_asinh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + + x = asinh(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.acosh(x) -> float + * + * Computes the inverse hyperbolic cosine of <i>x</i>. + */ +static mrb_value +math_acosh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < 1.0) { + domain_error(mrb, "acosh"); + } + x = acosh(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.atanh(x) -> float + * + * Computes the inverse hyperbolic tangent of <i>x</i>. + */ +static mrb_value +math_atanh(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < -1.0 || x > 1.0) { + domain_error(mrb, "atanh"); + } + x = atanh(x); + + return mrb_float_value(mrb, x); +} + +/* + EXPONENTIALS AND LOGARITHMS +*/ + +/* + * call-seq: + * Math.exp(x) -> float + * + * Returns e**x. + * + * Math.exp(0) #=> 1.0 + * Math.exp(1) #=> 2.718281828459045 + * Math.exp(1.5) #=> 4.4816890703380645 + * + */ +static mrb_value +math_exp(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = exp(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.log(numeric) -> float + * Math.log(num,base) -> float + * + * Returns the natural logarithm of <i>numeric</i>. + * If additional second argument is given, it will be the base + * of logarithm. + * + * Math.log(1) #=> 0.0 + * Math.log(Math::E) #=> 1.0 + * Math.log(Math::E**3) #=> 3.0 + * Math.log(12,3) #=> 2.2618595071429146 + * + */ +static mrb_value +math_log(mrb_state *mrb, mrb_value obj) +{ + mrb_float x, base; + int argc; + + argc = mrb_get_args(mrb, "f|f", &x, &base); + if (x < 0.0) { + domain_error(mrb, "log"); + } + x = log(x); + if (argc == 2) { + if (base < 0.0) { + domain_error(mrb, "log"); + } + x /= log(base); + } + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.log2(numeric) -> float + * + * Returns the base 2 logarithm of <i>numeric</i>. + * + * Math.log2(1) #=> 0.0 + * Math.log2(2) #=> 1.0 + * Math.log2(32768) #=> 15.0 + * Math.log2(65536) #=> 16.0 + * + */ +static mrb_value +math_log2(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < 0.0) { + domain_error(mrb, "log2"); + } + x = log2(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.log10(numeric) -> float + * + * Returns the base 10 logarithm of <i>numeric</i>. + * + * Math.log10(1) #=> 0.0 + * Math.log10(10) #=> 1.0 + * Math.log10(10**100) #=> 100.0 + * + */ +static mrb_value +math_log10(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < 0.0) { + domain_error(mrb, "log10"); + } + x = log10(x); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.sqrt(numeric) -> float + * + * Returns the square root of <i>numeric</i>. + * + */ +static mrb_value +math_sqrt(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + if (x < 0.0) { + domain_error(mrb, "sqrt"); + } + x = sqrt(x); + + return mrb_float_value(mrb, x); +} + + +/* + * call-seq: + * Math.cbrt(numeric) -> float + * + * Returns the cube root of <i>numeric</i>. + * + * -9.upto(9) {|x| + * p [x, Math.cbrt(x), Math.cbrt(x)**3] + * } + * #=> + * [-9, -2.0800838230519, -9.0] + * [-8, -2.0, -8.0] + * [-7, -1.91293118277239, -7.0] + * [-6, -1.81712059283214, -6.0] + * [-5, -1.7099759466767, -5.0] + * [-4, -1.5874010519682, -4.0] + * [-3, -1.44224957030741, -3.0] + * [-2, -1.25992104989487, -2.0] + * [-1, -1.0, -1.0] + * [0, 0.0, 0.0] + * [1, 1.0, 1.0] + * [2, 1.25992104989487, 2.0] + * [3, 1.44224957030741, 3.0] + * [4, 1.5874010519682, 4.0] + * [5, 1.7099759466767, 5.0] + * [6, 1.81712059283214, 6.0] + * [7, 1.91293118277239, 7.0] + * [8, 2.0, 8.0] + * [9, 2.0800838230519, 9.0] + * + */ +static mrb_value +math_cbrt(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = cbrt(x); + + return mrb_float_value(mrb, x); +} + + +/* + * call-seq: + * Math.frexp(numeric) -> [ fraction, exponent ] + * + * Returns a two-element array containing the normalized fraction (a + * <code>Float</code>) and exponent (a <code>Fixnum</code>) of + * <i>numeric</i>. + * + * fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11] + * fraction * 2**exponent #=> 1234.0 + */ +static mrb_value +math_frexp(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + int exp; + + mrb_get_args(mrb, "f", &x); + x = frexp(x, &exp); + + return mrb_assoc_new(mrb, mrb_float_value(mrb, x), mrb_fixnum_value(exp)); +} + +/* + * call-seq: + * Math.ldexp(flt, int) -> float + * + * Returns the value of <i>flt</i>*(2**<i>int</i>). + * + * fraction, exponent = Math.frexp(1234) + * Math.ldexp(fraction, exponent) #=> 1234.0 + */ +static mrb_value +math_ldexp(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + mrb_int i; + + mrb_get_args(mrb, "fi", &x, &i); + x = ldexp(x, i); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.hypot(x, y) -> float + * + * Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle + * with sides <i>x</i> and <i>y</i>. + * + * Math.hypot(3, 4) #=> 5.0 + */ +static mrb_value +math_hypot(mrb_state *mrb, mrb_value obj) +{ + mrb_float x, y; + + mrb_get_args(mrb, "ff", &x, &y); + x = hypot(x, y); + + return mrb_float_value(mrb, x); +} + +/* + * call-seq: + * Math.erf(x) -> float + * + * Calculates the error function of x. + */ +static mrb_value +math_erf(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = erf(x); + + return mrb_float_value(mrb, x); +} + + +/* + * call-seq: + * Math.erfc(x) -> float + * + * Calculates the complementary error function of x. + */ +static mrb_value +math_erfc(mrb_state *mrb, mrb_value obj) +{ + mrb_float x; + + mrb_get_args(mrb, "f", &x); + x = erfc(x); + + return mrb_float_value(mrb, x); +} + +/* ------------------------------------------------------------------------*/ +void +mrb_mruby_math_gem_init(mrb_state* mrb) +{ + struct RClass *mrb_math; + mrb_math = mrb_define_module(mrb, "Math"); + + mrb_define_class_under(mrb, mrb_math, "DomainError", mrb->eStandardError_class); + +#ifdef M_PI + mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, M_PI)); +#else + mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, atan(1.0)*4.0)); +#endif + +#ifdef M_E + mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, M_E)); +#else + mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0))); +#endif + +#ifdef MRB_USE_FLOAT + mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5)); +#else + mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12)); +#endif + + mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1)); + + mrb_define_module_function(mrb, mrb_math, "asin", math_asin, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "acos", math_acos, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "atan", math_atan, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "atan2", math_atan2, MRB_ARGS_REQ(2)); + + mrb_define_module_function(mrb, mrb_math, "sinh", math_sinh, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "cosh", math_cosh, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "tanh", math_tanh, MRB_ARGS_REQ(1)); + + mrb_define_module_function(mrb, mrb_math, "asinh", math_asinh, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "acosh", math_acosh, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "atanh", math_atanh, MRB_ARGS_REQ(1)); + + mrb_define_module_function(mrb, mrb_math, "exp", math_exp, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "log", math_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); + mrb_define_module_function(mrb, mrb_math, "log2", math_log2, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "log10", math_log10, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "sqrt", math_sqrt, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "cbrt", math_cbrt, MRB_ARGS_REQ(1)); + + mrb_define_module_function(mrb, mrb_math, "frexp", math_frexp, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "ldexp", math_ldexp, MRB_ARGS_REQ(2)); + + mrb_define_module_function(mrb, mrb_math, "hypot", math_hypot, MRB_ARGS_REQ(2)); + + mrb_define_module_function(mrb, mrb_math, "erf", math_erf, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, mrb_math, "erfc", math_erfc, MRB_ARGS_REQ(1)); +} + +void +mrb_mruby_math_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb new file mode 100644 index 00000000..e9ea07cc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb @@ -0,0 +1,152 @@ +## +# Math Test + +## +# Performs fuzzy check for equality on methods returning floats +# on the basis of the Math::TOLERANCE constant. +def check_float(a, b) + tolerance = Math::TOLERANCE + a = a.to_f + b = b.to_f + if a.finite? and b.finite? + (a-b).abs < tolerance + else + true + end +end + +assert('Math.sin 0') do + check_float(Math.sin(0), 0) +end + +assert('Math.sin PI/2') do + check_float(Math.sin(Math::PI / 2), 1) +end + +assert('Math.cos 0') do + check_float(Math.cos(0), 1) +end + +assert('Math.cos PI/2') do + check_float(Math.cos(Math::PI / 2), 0) +end + +assert('Math.tan 0') do + check_float(Math.tan(0), 0) +end + +assert('Math.tan PI/4') do + check_float(Math.tan(Math::PI / 4), 1) +end + +assert('Fundamental trig identities') do + result = true + N = 13 + N.times do |i| + a = Math::PI / N * i + ca = Math::PI / 2 - a + s = Math.sin(a) + c = Math.cos(a) + t = Math.tan(a) + result &= check_float(s, Math.cos(ca)) + result &= check_float(t, 1 / Math.tan(ca)) + result &= check_float(s ** 2 + c ** 2, 1) + result &= check_float(t ** 2 + 1, (1/c) ** 2) + result &= check_float((1/t) ** 2 + 1, (1/s) ** 2) + end + result +end + +assert('Math.erf 0') do + check_float(Math.erf(0), 0) +end + +assert('Math.exp 0') do + check_float(Math.exp(0), 1.0) +end + +assert('Math.exp 1') do + check_float(Math.exp(1), 2.718281828459045) +end + +assert('Math.exp 1.5') do + check_float(Math.exp(1.5), 4.4816890703380645) +end + +assert('Math.log 1') do + check_float(Math.log(1), 0) +end + +assert('Math.log E') do + check_float(Math.log(Math::E), 1.0) +end + +assert('Math.log E**3') do + check_float(Math.log(Math::E**3), 3.0) +end + +assert('Math.log2 1') do + check_float(Math.log2(1), 0.0) +end + +assert('Math.log2 2') do + check_float(Math.log2(2), 1.0) +end + +assert('Math.log10 1') do + check_float(Math.log10(1), 0.0) +end + +assert('Math.log10 10') do + check_float(Math.log10(10), 1.0) +end + +assert('Math.log10 10**100') do + check_float(Math.log10(10**100), 100.0) +end + +assert('Math.sqrt') do + num = [0.0, 1.0, 2.0, 3.0, 4.0] + sqr = [0, 1, 4, 9, 16] + result = true + sqr.each_with_index do |v,i| + result &= check_float(Math.sqrt(v), num[i]) + end + result +end + +assert('Math.cbrt') do + num = [-2.0, -1.0, 0.0, 1.0, 2.0] + cub = [-8, -1, 0, 1, 8] + result = true + cub.each_with_index do |v,i| + result &= check_float(Math.cbrt(v), num[i]) + end + result +end + +assert('Math.hypot') do + check_float(Math.hypot(3, 4), 5.0) +end + +assert('Math.frexp 1234') do + n = 1234 + fraction, exponent = Math.frexp(n) + check_float(Math.ldexp(fraction, exponent), n) +end + +assert('Math.erf 1') do + check_float(Math.erf(1), 0.842700792949715) +end + +assert('Math.erfc 1') do + check_float(Math.erfc(1), 0.157299207050285) +end + +assert('Math.erf -1') do + check_float(Math.erf(-1), -0.8427007929497148) +end + +assert('Math.erfc -1') do + check_float(Math.erfc(-1), 1.8427007929497148) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake new file mode 100644 index 00000000..6db7e589 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Numeric class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb new file mode 100644 index 00000000..0bf3c6ab --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb @@ -0,0 +1,17 @@ +module Integral + def div(other) + self.divmod(other)[0] + end + + def zero? + self == 0 + end + + def nonzero? + if self == 0 + nil + else + self + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c new file mode 100644 index 00000000..f71236a3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c @@ -0,0 +1,30 @@ +#include <limits.h> +#include <mruby.h> + +static mrb_value +mrb_int_chr(mrb_state *mrb, mrb_value x) +{ + mrb_int chr; + char c; + + chr = mrb_fixnum(x); + if (chr >= (1 << CHAR_BIT)) { + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x); + } + c = (char)chr; + + return mrb_str_new(mrb, &c, 1); +} + +void +mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) +{ + struct RClass *i = mrb_class_get(mrb, "Integer"); + + mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE()); +} + +void +mrb_mruby_numeric_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb new file mode 100644 index 00000000..8bead248 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb @@ -0,0 +1,28 @@ +## +# Numeric(Ext) Test + +assert('Integer#chr') do + assert_equal("A", 65.chr) + assert_equal("B", 0x42.chr) + + # multibyte encoding (not support yet) + assert_raise(RangeError) { 256.chr } +end + +assert('Integer#div') do + assert_equal 52, 365.div(7) +end + +assert('Float#div') do + assert_float 52, 365.2425.div(7) +end + +assert('Integer#zero?') do + assert_equal true, 0.zero? + assert_equal false, 1.zero? +end + +assert('Integer#nonzero?') do + assert_equal nil, 0.nonzero? + assert_equal 1000, 1000.nonzero? +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake new file mode 100644 index 00000000..6d14b4a5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-object-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Object class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb new file mode 100644 index 00000000..581156cb --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb @@ -0,0 +1,19 @@ +class Object + ## + # call-seq: + # obj.tap{|x|...} -> obj + # + # Yields <code>x</code> to the block, and then returns <code>x</code>. + # 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 +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c new file mode 100644 index 00000000..35a07b58 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c @@ -0,0 +1,106 @@ +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/proc.h> + +/* + * call-seq: + * nil.to_a -> [] + * + * Always returns an empty array. + */ + +static mrb_value +nil_to_a(mrb_state *mrb, mrb_value obj) +{ + return mrb_ary_new(mrb); +} + +/* + * call-seq: + * nil.to_f -> 0.0 + * + * Always returns zero. + */ + +static mrb_value +nil_to_f(mrb_state *mrb, mrb_value obj) +{ + return mrb_float_value(mrb, 0.0); +} + +/* + * call-seq: + * nil.to_i -> 0 + * + * Always returns zero. + */ + +static mrb_value +nil_to_i(mrb_state *mrb, mrb_value obj) +{ + return mrb_fixnum_value(0); +} + +/* + * call-seq: + * obj.instance_exec(arg...) {|var...| block } -> obj + * + * Executes the given block within the context of the receiver + * (_obj_). In order to set the context, the variable +self+ is set + * to _obj_ while the code is executing, giving the code access to + * _obj_'s instance variables. Arguments are passed as block parameters. + * + * class KlassWithSecret + * def initialize + * @secret = 99 + * end + * end + * k = KlassWithSecret.new + * k.instance_exec(5) {|x| @secret+x } #=> 104 + */ + +static mrb_value +mrb_obj_instance_exec(mrb_state *mrb, mrb_value self) +{ + const mrb_value *argv; + mrb_int argc; + mrb_value blk; + struct RClass *c; + + mrb_get_args(mrb, "*&", &argv, &argc, &blk); + + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + + switch (mrb_type(self)) { + case MRB_TT_SYMBOL: + case MRB_TT_FIXNUM: + case MRB_TT_FLOAT: + c = NULL; + break; + default: + c = mrb_class_ptr(mrb_singleton_class(mrb, self)); + break; + } + mrb->c->ci->target_class = c; + return mrb_yield_cont(mrb, blk, self, argc, argv); +} + +void +mrb_mruby_object_ext_gem_init(mrb_state* mrb) +{ + struct RClass * n = mrb->nil_class; + + mrb_define_method(mrb, n, "to_a", nil_to_a, MRB_ARGS_NONE()); + mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE()); + mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE()); + + mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); +} + +void +mrb_mruby_object_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb new file mode 100644 index 00000000..5cd1cf4e --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb @@ -0,0 +1,11 @@ +assert('NilClass#to_a') do + assert_equal [], nil.to_a +end + +assert('NilClass#to_f') do + assert_equal 0.0, nil.to_f +end + +assert('NilClass#to_i') do + assert_equal 0, nil.to_i +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb new file mode 100644 index 00000000..f0742f8c --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb @@ -0,0 +1,53 @@ +assert('Object#instance_exec') do + class KlassWithSecret + def initialize + @secret = 99 + end + end + k = KlassWithSecret.new + assert_equal 104, k.instance_exec(5) {|x| @secret+x } +end + +assert('Object#tap') do + ret = [] + (1..10) .tap {|x| ret << "original: #{x.inspect}"} + .to_a .tap {|x| ret << "array: #{x.inspect}"} + .select {|x| x%2==0} .tap {|x| ret << "evens: #{x.inspect}"} + .map { |x| x*x } .tap {|x| ret << "squares: #{x.inspect}"} + + assert_equal [ + "original: 1..10", + "array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", + "evens: [2, 4, 6, 8, 10]", + "squares: [4, 16, 36, 64, 100]" + ], ret + assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m) +end + +assert('instance_exec on primitives with class and module definition') do + begin + class A + 1.instance_exec do + class B + end + end + end + + assert_kind_of Class, A::B + ensure + Object.remove_const :A + end + + begin + class A + 1.instance_exec do + module B + end + end + end + + assert_kind_of Module, A::B + ensure + Object.remove_const :A + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake new file mode 100644 index 00000000..fa35136a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-objectspace') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'ObjectSpace class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c new file mode 100644 index 00000000..3887091d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -0,0 +1,187 @@ +#include <mruby.h> +#include <mruby/gc.h> +#include <mruby/hash.h> +#include <mruby/class.h> + +struct os_count_struct { + mrb_int total; + mrb_int freed; + mrb_int counts[MRB_TT_MAXDEFINE+1]; +}; + +static int +os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data) +{ + struct os_count_struct *obj_count; + obj_count = (struct os_count_struct*)data; + + obj_count->total++; + + if (mrb_object_dead_p(mrb, obj)) { + obj_count->freed++; + } + else { + obj_count->counts[obj->tt]++; + } + return MRB_EACH_OBJ_OK; +} + +/* + * call-seq: + * ObjectSpace.count_objects([result_hash]) -> hash + * + * Counts objects for each type. + * + * It returns a hash, such as: + * { + * :TOTAL=>10000, + * :FREE=>3011, + * :T_OBJECT=>6, + * :T_CLASS=>404, + * # ... + * } + * + * If the optional argument +result_hash+ is given, + * it is overwritten and returned. This is intended to avoid probe effect. + * + */ + +static mrb_value +os_count_objects(mrb_state *mrb, mrb_value self) +{ + struct os_count_struct obj_count = { 0 }; + mrb_int i; + mrb_value hash; + + if (mrb_get_args(mrb, "|H", &hash) == 0) { + hash = mrb_hash_new(mrb); + } + + if (!mrb_test(mrb_hash_empty_p(mrb, hash))) { + mrb_hash_clear(mrb, hash); + } + + mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count); + + mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total)); + mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "FREE")), mrb_fixnum_value(obj_count.freed)); + + for (i = MRB_TT_FALSE; i < MRB_TT_MAXDEFINE; i++) { + mrb_value type; + switch (i) { +#define COUNT_TYPE(t) case (MRB_T ## t): type = mrb_symbol_value(mrb_intern_lit(mrb, #t)); break; + COUNT_TYPE(T_FALSE); + COUNT_TYPE(T_FREE); + COUNT_TYPE(T_TRUE); + COUNT_TYPE(T_FIXNUM); + COUNT_TYPE(T_SYMBOL); + COUNT_TYPE(T_UNDEF); + COUNT_TYPE(T_FLOAT); + COUNT_TYPE(T_CPTR); + COUNT_TYPE(T_OBJECT); + COUNT_TYPE(T_CLASS); + COUNT_TYPE(T_MODULE); + COUNT_TYPE(T_ICLASS); + COUNT_TYPE(T_SCLASS); + COUNT_TYPE(T_PROC); + COUNT_TYPE(T_ARRAY); + COUNT_TYPE(T_HASH); + COUNT_TYPE(T_STRING); + COUNT_TYPE(T_RANGE); + COUNT_TYPE(T_EXCEPTION); + COUNT_TYPE(T_FILE); + COUNT_TYPE(T_ENV); + COUNT_TYPE(T_DATA); + COUNT_TYPE(T_FIBER); +#undef COUNT_TYPE + default: + type = mrb_fixnum_value(i); break; + } + if (obj_count.counts[i]) + mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i])); + } + + return hash; +} + +struct os_each_object_data { + mrb_value block; + struct RClass *target_module; + mrb_int count; +}; + +static int +os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud) +{ + struct os_each_object_data *d = (struct os_each_object_data*)ud; + + /* filter dead objects */ + if (mrb_object_dead_p(mrb, obj)) { + return MRB_EACH_OBJ_OK; + } + + /* filter internal objects */ + switch (obj->tt) { + case MRB_TT_ENV: + case MRB_TT_ICLASS: + return MRB_EACH_OBJ_OK; + default: + break; + } + + /* filter half baked (or internal) objects */ + if (!obj->c) return MRB_EACH_OBJ_OK; + + /* filter class kind if target module defined */ + if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) { + return MRB_EACH_OBJ_OK; + } + + mrb_yield(mrb, d->block, mrb_obj_value(obj)); + ++d->count; + return MRB_EACH_OBJ_OK; +} + +/* + * call-seq: + * ObjectSpace.each_object([module]) {|obj| ... } -> fixnum + * + * Calls the block once for each object in this Ruby process. + * Returns the number of objects found. + * If the optional argument +module+ is given, + * calls the block for only those classes or modules + * that match (or are a subclass of) +module+. + * + * If no block is given, ArgumentError is raised. + * + */ + +static mrb_value +os_each_object(mrb_state *mrb, mrb_value self) +{ + mrb_value cls = mrb_nil_value(); + struct os_each_object_data d; + mrb_get_args(mrb, "&|C", &d.block, &cls); + + if (mrb_nil_p(d.block)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Expected block in ObjectSpace.each_object."); + } + + d.target_module = mrb_nil_p(cls) ? NULL : mrb_class_ptr(cls); + d.count = 0; + mrb_objspace_each_objects(mrb, os_each_object_cb, &d); + return mrb_fixnum_value(d.count); +} + +void +mrb_mruby_objectspace_gem_init(mrb_state *mrb) +{ + struct RClass *os = mrb_define_module(mrb, "ObjectSpace"); + mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1)); + mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1)); +} + +void +mrb_mruby_objectspace_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb new file mode 100644 index 00000000..4731d53b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb @@ -0,0 +1,60 @@ +assert('ObjectSpace.count_objects') do + h = {} + f = Fiber.new {} if Object.const_defined? :Fiber + ObjectSpace.count_objects(h) + assert_kind_of(Hash, h) + assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) + assert_true(h.values.all? {|x| x.is_a?(Integer) }) + + assert_true(h.has_key?(:TOTAL)) + assert_true(h.has_key?(:FREE)) + assert_true(h.has_key?(:T_FIBER)) if Object.const_defined? :Fiber + + assert_equal(h[:TOTAL] * 2, h.values.reduce(:+)) + + h = ObjectSpace.count_objects + assert_kind_of(Hash, h) + assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) + assert_true(h.values.all? {|x| x.is_a?(Integer) }) + + assert_raise(TypeError) { ObjectSpace.count_objects(1) } + + h0 = {:T_FOO=>1000} + h = ObjectSpace.count_objects(h0) + assert_false(h0.has_key?(:T_FOO)) + + GC.start + h_after = {} + h_before = ObjectSpace.count_objects + + objs = [] + 1000.times do + objs << {} + end + objs = nil + ObjectSpace.count_objects(h) + GC.start + ObjectSpace.count_objects(h_after) + + assert_equal(h[:T_HASH], h_before[:T_HASH] + 1000) + assert_equal(h_after[:T_HASH], h_before[:T_HASH]) +end + +assert('ObjectSpace.each_object') do + objs = [] + objs_count = ObjectSpace.each_object { |obj| + objs << obj + } + assert_equal objs.length, objs_count + + arys = [] + arys_count = ObjectSpace.each_object(Array) { |obj| + arys << obj + } + assert_equal arys.length, arys_count + assert_true arys.length < objs.length +end + +assert 'Check class pointer of ObjectSpace.each_object.' do + ObjectSpace.each_object { |obj| !obj } +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake new file mode 100644 index 00000000..2ea6e312 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-print') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard print/puts/p' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb new file mode 100644 index 00000000..38a10661 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb @@ -0,0 +1,64 @@ +## +# Kernel +# +# ISO 15.3.1 +module Kernel + ## + # Invoke method +print+ on STDOUT and passing +*args+ + # + # ISO 15.3.1.2.10 + def print(*args) + i = 0 + len = args.size + while i < len + __printstr__ args[i].to_s + i += 1 + end + end + + ## + # Invoke method +puts+ on STDOUT and passing +*args*+ + # + # ISO 15.3.1.2.11 + def puts(*args) + i = 0 + len = args.size + while i < len + s = args[i].to_s + __printstr__ s + __printstr__ "\n" if (s[-1] != "\n") + i += 1 + end + __printstr__ "\n" if len == 0 + nil + end + + ## + # Print human readable object description + # + # ISO 15.3.1.3.34 + def p(*args) + i = 0 + len = args.size + while i < len + __printstr__ args[i].inspect + __printstr__ "\n" + i += 1 + end + args[0] + end + + unless Kernel.respond_to?(:sprintf) + def printf(*args) + raise NotImplementedError.new('printf not available') + end + def sprintf(*args) + raise NotImplementedError.new('sprintf not available') + end + else + def printf(*args) + __printstr__(sprintf(*args)) + nil + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c new file mode 100644 index 00000000..d0c522d2 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c @@ -0,0 +1,64 @@ +#include <mruby.h> +#include <mruby/string.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#if defined(_WIN32) +# include <windows.h> +# include <io.h> +#ifdef _MSC_VER +# define isatty(x) _isatty(x) +# define fileno(x) _fileno(x) +#endif +#endif + +static void +printstr(mrb_state *mrb, mrb_value obj) +{ + if (mrb_string_p(obj)) { +#if defined(_WIN32) + if (isatty(fileno(stdout))) { + DWORD written; + int mlen = RSTRING_LEN(obj); + char* utf8 = RSTRING_PTR(obj); + int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0); + wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); + if (utf16 == NULL) return; + if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) { + utf16[wlen] = 0; + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), + utf16, wlen, &written, NULL); + } + mrb_free(mrb, utf16); + } else +#endif + fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); + fflush(stdout); + } +} + +/* 15.3.1.2.9 */ +/* 15.3.1.3.34 */ +mrb_value +mrb_printstr(mrb_state *mrb, mrb_value self) +{ + mrb_value argv; + + mrb_get_args(mrb, "o", &argv); + printstr(mrb, argv); + + return argv; +} + +void +mrb_mruby_print_gem_init(mrb_state* mrb) +{ + struct RClass *krn; + krn = mrb->kernel_module; + mrb_define_method(mrb, krn, "__printstr__", mrb_printstr, MRB_ARGS_REQ(1)); +} + +void +mrb_mruby_print_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake new file mode 100644 index 00000000..e4d15a14 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-proc-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Proc class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb new file mode 100644 index 00000000..b7166393 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb @@ -0,0 +1,42 @@ +class Proc + + def ===(*args) + call(*args) + end + + def yield(*args) + call(*args) + end + + def to_proc + self + end + + def curry(arity=self.arity) + type = :proc + abs = lambda {|a| a < 0 ? -a - 1 : a} + arity = abs[arity] + if lambda? + type = :lambda + self_arity = self.arity + if (self_arity >= 0 && arity != self_arity) || + (self_arity < 0 && abs[self_arity] > arity) + raise ArgumentError, "wrong number of arguments (#{arity} for #{abs[self_arity]})" + end + end + + pproc = self + make_curry = proc do |given_args=[]| + send(type) do |*args| + new_args = given_args + args + if new_args.size >= arity + pproc[*new_args] + else + make_curry[new_args] + end + end + end + make_curry.call + end + +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c new file mode 100644 index 00000000..b13606f5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c @@ -0,0 +1,173 @@ +#include <mruby.h> +#include <mruby/proc.h> +#include <mruby/opcode.h> +#include <mruby/array.h> +#include <mruby/string.h> +#include <mruby/debug.h> + +static mrb_value +mrb_proc_lambda(mrb_state *mrb, mrb_value self) +{ + struct RProc *p = mrb_proc_ptr(self); + return mrb_bool_value(MRB_PROC_STRICT_P(p)); +} + +static mrb_value +mrb_proc_source_location(mrb_state *mrb, mrb_value self) +{ + struct RProc *p = mrb_proc_ptr(self); + + if (MRB_PROC_CFUNC_P(p)) { + return mrb_nil_value(); + } + else { + mrb_irep *irep = p->body.irep; + int32_t line; + const char *filename; + + filename = mrb_debug_get_filename(irep, 0); + line = mrb_debug_get_line(irep, 0); + + return (!filename && line == -1)? mrb_nil_value() + : mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line)); + } +} + +static mrb_value +mrb_proc_inspect(mrb_state *mrb, mrb_value self) +{ + struct RProc *p = mrb_proc_ptr(self); + mrb_value str = mrb_str_new_lit(mrb, "#<Proc:"); + mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self))); + + if (!MRB_PROC_CFUNC_P(p)) { + mrb_irep *irep = p->body.irep; + const char *filename; + int32_t line; + mrb_str_cat_lit(mrb, str, "@"); + + filename = mrb_debug_get_filename(irep, 0); + mrb_str_cat_cstr(mrb, str, filename ? filename : "-"); + mrb_str_cat_lit(mrb, str, ":"); + + line = mrb_debug_get_line(irep, 0); + if (line != -1) { + str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line)); + } + else { + mrb_str_cat_lit(mrb, str, "-"); + } + } + + if (MRB_PROC_STRICT_P(p)) { + mrb_str_cat_lit(mrb, str, " (lambda)"); + } + + mrb_str_cat_lit(mrb, str, ">"); + return str; +} + +static mrb_value +mrb_kernel_proc(mrb_state *mrb, mrb_value self) +{ + mrb_value blk; + + mrb_get_args(mrb, "&", &blk); + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); + } + + return blk; +} + +/* + * call-seq: + * prc.parameters -> array + * + * Returns the parameter information of this proc. + * + * prc = lambda{|x, y=42, *other|} + * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] + */ + +static mrb_value +mrb_proc_parameters(mrb_state *mrb, mrb_value self) +{ + struct parameters_type { + int size; + const char *name; + } *p, parameters_list [] = { + {0, "req"}, + {0, "opt"}, + {0, "rest"}, + {0, "req"}, + {0, "block"}, + {0, NULL} + }; + const struct RProc *proc = mrb_proc_ptr(self); + const struct mrb_irep *irep = proc->body.irep; + mrb_aspec aspec; + mrb_value sname, parameters; + int i, j; + + if (MRB_PROC_CFUNC_P(proc)) { + // TODO cfunc aspec is not implemented yet + return mrb_ary_new(mrb); + } + if (!irep) { + return mrb_ary_new(mrb); + } + if (!irep->lv) { + return mrb_ary_new(mrb); + } + if (GET_OPCODE(*irep->iseq) != OP_ENTER) { + return mrb_ary_new(mrb); + } + + if (!MRB_PROC_STRICT_P(proc)) { + parameters_list[0].name = "opt"; + parameters_list[3].name = "opt"; + } + + aspec = GETARG_Ax(*irep->iseq); + parameters_list[0].size = MRB_ASPEC_REQ(aspec); + parameters_list[1].size = MRB_ASPEC_OPT(aspec); + parameters_list[2].size = MRB_ASPEC_REST(aspec); + parameters_list[3].size = MRB_ASPEC_POST(aspec); + parameters_list[4].size = MRB_ASPEC_BLOCK(aspec); + + parameters = mrb_ary_new_capa(mrb, irep->nlocals-1); + + for (i = 0, p = parameters_list; p->name; p++) { + if (p->size <= 0) continue; + sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name)); + for (j = 0; j < p->size; i++, j++) { + mrb_value a = mrb_ary_new(mrb); + mrb_ary_push(mrb, a, sname); + if (irep->lv[i].name) { + mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name)); + } + mrb_ary_push(mrb, parameters, a); + } + } + return parameters; +} + +void +mrb_mruby_proc_ext_gem_init(mrb_state* mrb) +{ + struct RClass *p = mrb->proc_class; + mrb_define_method(mrb, p, "lambda?", mrb_proc_lambda, MRB_ARGS_NONE()); + mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE()); + mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE()); + mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE()); + mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE()); + + mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()); + mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()); +} + +void +mrb_mruby_proc_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c new file mode 100644 index 00000000..a77b68e9 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c @@ -0,0 +1,56 @@ +#include <mruby.h> +#include <mruby/proc.h> +#include <mruby/class.h> + +static mrb_value +return_func_name(mrb_state *mrb, mrb_value self) +{ + return mrb_cfunc_env_get(mrb, 0); +} + +static mrb_value +proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self) +{ + mrb_sym n; + mrb_value n_val; + mrb_get_args(mrb, "n", &n); + n_val = mrb_symbol_value(n); + mrb_define_method_raw(mrb, mrb_class_ptr(self), n, + mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val)); + return self; +} + +static mrb_value +return_env(mrb_state *mrb, mrb_value self) +{ + mrb_int idx; + mrb_get_args(mrb, "i", &idx); + return mrb_cfunc_env_get(mrb, idx); +} + +static mrb_value +cfunc_env_get(mrb_state *mrb, mrb_value self) +{ + mrb_sym n; + mrb_value *argv; mrb_int argc; + mrb_get_args(mrb, "na", &n, &argv, &argc); + mrb_define_method_raw(mrb, mrb_class_ptr(self), n, + mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv)); + return self; +} + +static mrb_value +cfunc_without_env(mrb_state *mrb, mrb_value self) +{ + return mrb_cfunc_env_get(mrb, 0); +} + +void mrb_mruby_proc_ext_gem_test(mrb_state *mrb) +{ + struct RClass *cls; + + cls = mrb_define_class(mrb, "ProcExtTest", mrb->object_class); + mrb_define_module_function(mrb, cls, "mrb_proc_new_cfunc_with_env", proc_new_cfunc_with_env, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, cls, "mrb_cfunc_env_get", cfunc_env_get, MRB_ARGS_REQ(2)); + mrb_define_module_function(mrb, cls, "cfunc_without_env", cfunc_without_env, MRB_ARGS_NONE()); +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb new file mode 100644 index 00000000..037d8d12 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb @@ -0,0 +1,96 @@ +## +# Proc(Ext) Test + +assert('Proc#source_location') do + loc = Proc.new {}.source_location + next true if loc.nil? + assert_equal loc[0][-7, 7], 'proc.rb' + assert_equal loc[1], 5 +end + +assert('Proc#inspect') do + ins = Proc.new{}.inspect + assert_kind_of String, ins +end + +assert('Proc#lambda?') do + assert_true lambda{}.lambda? + assert_true !Proc.new{}.lambda? +end + +assert('Proc#===') do + proc = Proc.new {|a| a * 2} + assert_equal 20, (proc === 10) +end + +assert('Proc#yield') do + proc = Proc.new {|a| a * 2} + assert_equal 20, proc.yield(10) +end + +assert('Proc#curry') do + b = proc {|x, y, z| (x||0) + (y||0) + (z||0) } + assert_equal 6, b.curry[1][2][3] + assert_equal 6, b.curry[1, 2][3, 4] + assert_equal 6, b.curry(5)[1][2][3][4][5] + assert_equal 6, b.curry(5)[1, 2][3, 4][5] + assert_equal 1, b.curry(1)[1] + + b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) } + assert_equal 6, b.curry[1][2][3] + assert_raise(ArgumentError) { b.curry[1, 2][3, 4] } + assert_raise(ArgumentError) { b.curry(5) } + assert_raise(ArgumentError) { b.curry(1) } + + assert_false(proc{}.curry.lambda?) + assert_true(lambda{}.curry.lambda?) +end + +assert('Proc#parameters') do + assert_equal([], Proc.new {}.parameters) + assert_equal([], Proc.new {||}.parameters) + assert_equal([[:opt, :a]], Proc.new {|a|}.parameters) + assert_equal([[:req, :a]], lambda {|a|}.parameters) + assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters) + assert_equal([[:req, :a]], ->(a){}.parameters) + assert_equal([[:rest]], lambda { |*| }.parameters) + assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters) + assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) + assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) +end + +assert('Proc#to_proc') do + proc = Proc.new {} + assert_equal proc, proc.to_proc +end + +assert('Kernel#proc') do + assert_true !proc{|a|}.lambda? + + assert_raise LocalJumpError do + proc{ break }.call + end +end + +assert('mrb_proc_new_cfunc_with_env') do + ProcExtTest.mrb_proc_new_cfunc_with_env(:test) + ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby) + + t = ProcExtTest.new + + assert_equal :test, t.test + assert_equal :mruby, t.mruby +end + +assert('mrb_cfunc_env_get') do + ProcExtTest.mrb_cfunc_env_get :get_int, [0, 1, 2] + + t = ProcExtTest.new + + assert_raise(TypeError) { t.cfunc_without_env } + + assert_raise(IndexError) { t.get_int(-1) } + assert_raise(IndexError) { t.get_int(3) } + + assert_equal 1, t.get_int(1) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake new file mode 100644 index 00000000..3a3fd2bd --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-random') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Random class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c new file mode 100644 index 00000000..405bd5c2 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c @@ -0,0 +1,224 @@ +/* +** mt19937ar.c - MT Random functions +** +** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura, +** All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] +** +** Any feedback is very welcome. +** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html +** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +** +** This version is modified by mruby developers. If you see any problem, +** contact us first at https://github.com/mruby/mruby/issues +*/ + +#include <mruby.h> +#include "mt19937ar.h" + +/* Period parameters */ +/* #define N 624 */ +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +#if 0 /* dead_code */ +static unsigned long mt[N]; /* the array for the state vector */ +static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ +#endif /* dead_code */ + +void mrb_random_init_genrand(mt_state *t, unsigned long s) +{ + t->mt[0]= s & 0xffffffffUL; + for (t->mti=1; t->mti<N; t->mti++) { + t->mt[t->mti] = (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti); + t->mt[t->mti] &= 0xffffffffUL; + } +} + +unsigned long mrb_random_genrand_int32(mt_state *t) +{ + unsigned long y; + static const unsigned long mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (t->mti >= N) { /* generate N words at one time */ + int kk; + + if (t->mti == N+1) /* if init_genrand() has not been called, */ + mrb_random_init_genrand(t, 5489UL); /* a default initial seed is used */ + + for (kk=0;kk<N-M;kk++) { + y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK); + t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (;kk<N-1;kk++) { + y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK); + t->mt[kk] = t->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (t->mt[N-1]&UPPER_MASK)|(t->mt[0]&LOWER_MASK); + t->mt[N-1] = t->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + t->mti = 0; + } + + y = t->mt[t->mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + t->gen.int_ = y; + + return y; +} + +double mrb_random_genrand_real1(mt_state *t) +{ + mrb_random_genrand_int32(t); + t->gen.double_ = t->gen.int_*(1.0/4294967295.0); + return t->gen.double_; + /* divided by 2^32-1 */ +} + +#if 0 /* dead_code */ +/* initializes mt[N] with a seed */ +void init_genrand(unsigned long s) +{ + mt[0]= s & 0xffffffffUL; + for (mti=1; mti<N; mti++) { + mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +void init_by_array(unsigned long init_key[], int key_length) +{ + int i, j, k; + init_genrand(19650218UL); + i=1; j=0; + k = (N>key_length ? N : key_length); + for (; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + + init_key[j] + j; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=N-1; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) + - i; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + } + + mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0,0xffffffff]-interval */ +unsigned long genrand_int32(void) +{ + unsigned long y; + static const unsigned long mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (mti >= N) { /* generate N words at one time */ + int kk; + + if (mti == N+1) /* if init_genrand() has not been called, */ + init_genrand(5489UL); /* a default initial seed is used */ + + for (kk=0;kk<N-M;kk++) { + y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); + mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (;kk<N-1;kk++) { + y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); + mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + + y = mt[mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +/* generates a random number on [0,0x7fffffff]-interval */ +long genrand_int31(void) +{ + return (long)(genrand_int32()>>1); +} + +/* generates a random number on [0,1]-real-interval */ +double genrand_real1(void) +{ + return genrand_int32()*(1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/* generates a random number on [0,1)-real-interval */ +double genrand_real2(void) +{ + return genrand_int32()*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on (0,1)-real-interval */ +double genrand_real3(void) +{ + return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on [0,1) with 53-bit resolution*/ +double genrand_res53(void) +{ + unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; + return(a*67108864.0+b)*(1.0/9007199254740992.0); +} +/* These real versions are due to Isaku Wada, 2002/01/09 added */ +#endif /* dead_code */ diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h new file mode 100644 index 00000000..7d382320 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h @@ -0,0 +1,80 @@ +/* +** mt19937ar.h - MT Random functions +** +** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura, +** All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] +** +** Any feedback is very welcome. +** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html +** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +** +** This version is modified by mruby developers. If you see any problem, +** contact us first at https://github.com/mruby/mruby/issues +*/ + +#define N 624 + +typedef struct { + unsigned long mt[N]; + int mti; + union { + unsigned long int_; + double double_; + } gen; + + mrb_int seed; + mrb_bool has_seed : 1; +} mt_state; + +void mrb_random_init_genrand(mt_state *, unsigned long); +unsigned long mrb_random_genrand_int32(mt_state *); +double mrb_random_genrand_real1(mt_state *t); + +/* initializes mt[N] with a seed */ +void init_genrand(unsigned long s); + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +void init_by_array(unsigned long init_key[], int key_length); + +/* generates a random number on [0,0xffffffff]-interval */ +unsigned long genrand_int32(void); + +/* generates a random number on [0,0x7fffffff]-interval */ +long genrand_int31(void); + +/* These real versions are due to Isaku Wada, 2002/01/09 added */ +/* generates a random number on [0,1]-real-interval */ +double genrand_real1(void); + +/* generates a random number on [0,1)-real-interval */ +double genrand_real2(void); + +/* generates a random number on (0,1)-real-interval */ +double genrand_real3(void); + +/* generates a random number on [0,1) with 53-bit resolution*/ +double genrand_res53(void); diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c new file mode 100644 index 00000000..b865244c --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c @@ -0,0 +1,349 @@ +/* +** random.c - Random module +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/variable.h> +#include <mruby/class.h> +#include <mruby/data.h> +#include <mruby/array.h> +#include "mt19937ar.h" + +#include <time.h> + +static char const MT_STATE_KEY[] = "$mrb_i_mt_state"; + +static const struct mrb_data_type mt_state_type = { + MT_STATE_KEY, mrb_free, +}; + +static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self); +static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self); + +static void +mt_srand(mt_state *t, unsigned long seed) +{ + mrb_random_init_genrand(t, seed); +} + +static unsigned long +mt_rand(mt_state *t) +{ + return mrb_random_genrand_int32(t); +} + +static double +mt_rand_real(mt_state *t) +{ + return mrb_random_genrand_real1(t); +} + +static mrb_value +mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed) +{ + if (mrb_nil_p(seed)) { + seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t))); + if (mrb_fixnum(seed) < 0) { + seed = mrb_fixnum_value(0 - mrb_fixnum(seed)); + } + } + + mt_srand(t, (unsigned) mrb_fixnum(seed)); + + return seed; +} + +static mrb_value +mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max) +{ + mrb_value value; + + if (mrb_fixnum(max) == 0) { + value = mrb_float_value(mrb, mt_rand_real(t)); + } + else { + value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max)); + } + + return value; +} + +static mrb_value +get_opt(mrb_state* mrb) +{ + mrb_value arg; + + arg = mrb_nil_value(); + mrb_get_args(mrb, "|o", &arg); + + if (!mrb_nil_p(arg)) { + arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int"); + if (mrb_nil_p(arg)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type"); + } + if (mrb_fixnum(arg) < 0) { + arg = mrb_fixnum_value(0 - mrb_fixnum(arg)); + } + } + return arg; +} + +static mrb_value +get_random(mrb_state *mrb) { + return mrb_const_get(mrb, + mrb_obj_value(mrb_class_get(mrb, "Random")), + mrb_intern_lit(mrb, "DEFAULT")); +} + +static mt_state * +get_random_state(mrb_state *mrb) +{ + mrb_value random_val = get_random(mrb); + return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state); +} + +static mrb_value +mrb_random_g_rand(mrb_state *mrb, mrb_value self) +{ + mrb_value random = get_random(mrb); + return mrb_random_rand(mrb, random); +} + +static mrb_value +mrb_random_g_srand(mrb_state *mrb, mrb_value self) +{ + mrb_value random = get_random(mrb); + return mrb_random_srand(mrb, random); +} + +static mrb_value +mrb_random_init(mrb_state *mrb, mrb_value self) +{ + mrb_value seed; + mt_state *t; + + seed = get_opt(mrb); + + /* avoid memory leaks */ + t = (mt_state*)DATA_PTR(self); + if (t) { + mrb_free(mrb, t); + } + mrb_data_init(self, NULL, &mt_state_type); + + t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state)); + t->mti = N + 1; + + seed = mrb_random_mt_srand(mrb, t, seed); + if (mrb_nil_p(seed)) { + t->has_seed = FALSE; + } + else { + mrb_assert(mrb_fixnum_p(seed)); + t->has_seed = TRUE; + t->seed = mrb_fixnum(seed); + } + + mrb_data_init(self, t, &mt_state_type); + + return self; +} + +static void +mrb_random_rand_seed(mrb_state *mrb, mt_state *t) +{ + if (!t->has_seed) { + mrb_random_mt_srand(mrb, t, mrb_nil_value()); + } +} + +static mrb_value +mrb_random_rand(mrb_state *mrb, mrb_value self) +{ + mrb_value max; + mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); + + max = get_opt(mrb); + mrb_random_rand_seed(mrb, t); + return mrb_random_mt_rand(mrb, t, max); +} + +static mrb_value +mrb_random_srand(mrb_state *mrb, mrb_value self) +{ + mrb_value seed; + mrb_value old_seed; + mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); + + seed = get_opt(mrb); + seed = mrb_random_mt_srand(mrb, t, seed); + old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value(); + if (mrb_nil_p(seed)) { + t->has_seed = FALSE; + } + else { + mrb_assert(mrb_fixnum_p(seed)); + t->has_seed = TRUE; + t->seed = mrb_fixnum(seed); + } + + return old_seed; +} + +/* + * call-seq: + * ary.shuffle! -> ary + * + * Shuffles elements in self in place. + */ + +static mrb_value +mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mt_state *random = NULL; + + if (RARRAY_LEN(ary) > 1) { + mrb_get_args(mrb, "|d", &random, &mt_state_type); + + if (random == NULL) { + random = get_random_state(mrb); + } + mrb_random_rand_seed(mrb, random); + + mrb_ary_modify(mrb, mrb_ary_ptr(ary)); + + for (i = RARRAY_LEN(ary) - 1; i > 0; i--) { + mrb_int j; + mrb_value *ptr = RARRAY_PTR(ary); + mrb_value tmp; + + + j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary)))); + + tmp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = tmp; + } + } + + return ary; +} + +/* + * call-seq: + * ary.shuffle -> new_ary + * + * Returns a new array with elements of self shuffled. + */ + +static mrb_value +mrb_ary_shuffle(mrb_state *mrb, mrb_value ary) +{ + mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); + mrb_ary_shuffle_bang(mrb, new_ary); + + return new_ary; +} + +/* + * call-seq: + * ary.sample -> obj + * ary.sample(n) -> new_ary + * + * Choose a random element or +n+ random elements from the array. + * + * The elements are chosen by using random and unique indices into the array + * in order to ensure that an element doesn't repeat itself unless the array + * already contained duplicate elements. + * + * If the array is empty the first form returns +nil+ and the second form + * returns an empty array. + */ + +static mrb_value +mrb_ary_sample(mrb_state *mrb, mrb_value ary) +{ + mrb_int n = 0; + mrb_bool given; + mt_state *random = NULL; + mrb_int len; + + mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type); + if (random == NULL) { + random = get_random_state(mrb); + } + mrb_random_rand_seed(mrb, random); + mt_rand(random); + len = RARRAY_LEN(ary); + if (!given) { /* pick one element */ + switch (len) { + case 0: + return mrb_nil_value(); + case 1: + return RARRAY_PTR(ary)[0]; + default: + return RARRAY_PTR(ary)[mt_rand(random) % len]; + } + } + else { + mrb_value result; + mrb_int i, j; + + if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number"); + if (n > len) n = len; + result = mrb_ary_new_capa(mrb, n); + for (i=0; i<n; i++) { + mrb_int r; + + for (;;) { + retry: + r = mt_rand(random) % len; + + for (j=0; j<i; j++) { + if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) { + goto retry; /* retry if duplicate */ + } + } + break; + } + mrb_ary_push(mrb, result, mrb_fixnum_value(r)); + } + for (i=0; i<n; i++) { + mrb_ary_set(mrb, result, i, RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])]); + } + return result; + } +} + + +void mrb_mruby_random_gem_init(mrb_state *mrb) +{ + struct RClass *random; + struct RClass *array = mrb->array_class; + + mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); + + random = mrb_define_class(mrb, "Random", mrb->object_class); + MRB_SET_INSTANCE_TT(random, MRB_TT_DATA); + mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); + mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); + + mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1)); + + mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, array, "sample", mrb_ary_sample, MRB_ARGS_OPT(2)); + + mrb_const_set(mrb, mrb_obj_value(random), mrb_intern_lit(mrb, "DEFAULT"), + mrb_obj_new(mrb, random, 0, NULL)); +} + +void mrb_mruby_random_gem_final(mrb_state *mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h new file mode 100644 index 00000000..a4785ae5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h @@ -0,0 +1,12 @@ +/* +** random.h - Random module +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_RANDOM_H +#define MRUBY_RANDOM_H + +void mrb_mruby_random_gem_init(mrb_state *mrb); + +#endif diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb new file mode 100644 index 00000000..1c59be3a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb @@ -0,0 +1,88 @@ +## +# Random Test + +assert("Random#srand") do + r1 = Random.new(123) + r2 = Random.new(123) + r1.rand == r2.rand +end + +assert("Kernel::srand") do + srand(234) + r1 = rand + srand(234) + r2 = rand + r1 == r2 +end + +assert("Random::srand") do + Random.srand(345) + r1 = rand + srand(345) + r2 = Random.rand + r1 == r2 +end + +assert("fixnum") do + rand(3).class == Fixnum +end + +assert("float") do + rand.class == Float +end + +assert("Array#shuffle") do + ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shuffled = ary.shuffle + + ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x } +end + +assert('Array#shuffle!') do + ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ary.shuffle! + + ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x } +end + +assert("Array#shuffle(random)") do + assert_raise(TypeError) do + # this will cause an exception due to the wrong argument + [1, 2].shuffle "Not a Random instance" + end + + # verify that the same seed causes the same results + ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shuffle1 = ary1.shuffle Random.new 345 + ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shuffle2 = ary2.shuffle Random.new 345 + + ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2 +end + +assert('Array#shuffle!(random)') do + assert_raise(TypeError) do + # this will cause an exception due to the wrong argument + [1, 2].shuffle! "Not a Random instance" + end + + # verify that the same seed causes the same results + ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ary1.shuffle! Random.new 345 + ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ary2.shuffle! Random.new 345 + + ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2 +end + +assert('Array#sample checks input length after reading arguments') do + $ary = [1, 2, 3] + class ArrayChange + def to_i + $ary << 4 + 4 + end + end + + assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake new file mode 100644 index 00000000..e6fa7df5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-range-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Range class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb new file mode 100644 index 00000000..e5d1fb07 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb @@ -0,0 +1,31 @@ +class Range + ## + # call-seq: + # rng.first -> obj + # rng.first(n) -> an_array + # + # Returns the first object in the range, or an array of the first +n+ + # elements. + # + # (10..20).first #=> 10 + # (10..20).first(3) #=> [10, 11, 12] + # + def first(*args) + return self.begin if args.empty? + + raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1 + nv = args[0] + raise TypeError, "no implicit conversion from nil to integer" if nv.nil? + raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int) + n = nv.to_int + raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer) + raise ArgumentError, "negative array size (or size too big)" unless 0 <= n + ary = [] + each do |i| + break if n <= 0 + ary.push(i) + n -= 1 + end + ary + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c new file mode 100644 index 00000000..aca71cc0 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c @@ -0,0 +1,176 @@ +#include <mruby.h> +#include <mruby/range.h> +#include <math.h> + +static mrb_bool +r_le(mrb_state *mrb, mrb_value a, mrb_value b) +{ + mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ + /* output :a < b => -1, a = b => 0, a > b => +1 */ + + if (mrb_fixnum_p(r)) { + mrb_int c = mrb_fixnum(r); + if (c == 0 || c == -1) return TRUE; + } + + return FALSE; +} + +static mrb_bool +r_lt(mrb_state *mrb, mrb_value a, mrb_value b) +{ + mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); + /* output :a < b => -1, a = b => 0, a > b => +1 */ + + return mrb_fixnum_p(r) && mrb_fixnum(r) == -1; +} + +/* + * call-seq: + * rng.cover?(obj) -> true or false + * + * Returns <code>true</code> if +obj+ is between the begin and end of + * the range. + * + * This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+ + * and <code>begin <= obj < end</code> when #exclude_end? is +true+. + * + * ("a".."z").cover?("c") #=> true + * ("a".."z").cover?("5") #=> false + * ("a".."z").cover?("cc") #=> true + */ +static mrb_value +mrb_range_cover(mrb_state *mrb, mrb_value range) +{ + mrb_value val; + struct RRange *r = mrb_range_ptr(mrb, range); + mrb_value beg, end; + + mrb_get_args(mrb, "o", &val); + + beg = r->edges->beg; + end = r->edges->end; + + if (r_le(mrb, beg, val)) { + if (r->excl) { + if (r_lt(mrb, val, end)) + return mrb_true_value(); + } + else { + if (r_le(mrb, val, end)) + return mrb_true_value(); + } + } + + return mrb_false_value(); +} + +/* + * call-seq: + * rng.last -> obj + * rng.last(n) -> an_array + * + * Returns the last object in the range, + * or an array of the last +n+ elements. + * + * Note that with no arguments +last+ will return the object that defines + * the end of the range even if #exclude_end? is +true+. + * + * (10..20).last #=> 20 + * (10...20).last #=> 20 + * (10..20).last(3) #=> [18, 19, 20] + * (10...20).last(3) #=> [17, 18, 19] + */ +static mrb_value +mrb_range_last(mrb_state *mrb, mrb_value range) +{ + mrb_value num; + mrb_value array; + struct RRange *r = mrb_range_ptr(mrb, range); + + if (mrb_get_args(mrb, "|o", &num) == 0) { + return r->edges->end; + } + + array = mrb_funcall(mrb, range, "to_a", 0); + return mrb_funcall(mrb, array, "last", 1, mrb_to_int(mrb, num)); +} + +/* + * call-seq: + * rng.size -> num + * + * Returns the number of elements in the range. Both the begin and the end of + * the Range must be Numeric, otherwise nil is returned. + * + * (10..20).size #=> 11 + * ('a'..'z').size #=> nil + */ + +static mrb_value +mrb_range_size(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_ptr(mrb, range); + mrb_value beg, end; + mrb_float beg_f, end_f; + mrb_bool num_p = TRUE; + mrb_bool excl; + + beg = r->edges->beg; + end = r->edges->end; + excl = r->excl; + if (mrb_fixnum_p(beg)) { + beg_f = (mrb_float)mrb_fixnum(beg); + } + else if (mrb_float_p(beg)) { + beg_f = mrb_float(beg); + } + else { + num_p = FALSE; + } + if (mrb_fixnum_p(end)) { + end_f = (mrb_float)mrb_fixnum(end); + } + else if (mrb_float_p(end)) { + end_f = mrb_float(end); + } + else { + num_p = FALSE; + } + if (num_p) { + mrb_float n = end_f - beg_f; + mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON; + + if (err>0.5) err=0.5; + if (excl) { + if (n<=0) return mrb_fixnum_value(0); + if (n<1) + n = 0; + else + n = floor(n - err); + } + else { + if (n<0) return mrb_fixnum_value(0); + n = floor(n + err); + } + if (isinf(n+1)) + return mrb_float_value(mrb, INFINITY); + return mrb_fixnum_value((mrb_int)n+1); + } + return mrb_nil_value(); +} + +void +mrb_mruby_range_ext_gem_init(mrb_state* mrb) +{ + struct RClass * s = mrb_class_get(mrb, "Range"); + + mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE()); +} + +void +mrb_mruby_range_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb new file mode 100644 index 00000000..efcbdabe --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb @@ -0,0 +1,32 @@ +## +# Range(Ext) Test + +assert('Range#cover?') do + assert_true ("a".."z").cover?("c") + assert_true !("a".."z").cover?("5") + assert_true ("a".."z").cover?("cc") +end + +assert('Range#first') do + assert_equal 10, (10..20).first + assert_equal [10, 11, 12], (10..20).first(3) + assert_equal [0, 1, 2], (0..Float::INFINITY).first(3) +end + +assert('Range#last') do + assert_equal 20, (10..20).last + assert_equal 20, (10...20).last + assert_equal [18, 19, 20], (10..20).last(3) + assert_equal [17, 18, 19], (10...20).last(3) +end + +assert('Range#size') do + assert_equal 42, (1..42).size + assert_equal 41, (1...42).size + assert_equal 6, (1...6.3).size + assert_equal 5, (1...6.0).size + assert_equal 5, (1.1...6).size + assert_equal 15, (1.0..15.9).size + assert_equal Float::INFINITY, (0..Float::INFINITY).size + assert_nil ('a'..'z').size +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake new file mode 100644 index 00000000..bc897243 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-sprintf') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard Kernel#sprintf method' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb new file mode 100644 index 00000000..d7e55536 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb @@ -0,0 +1,9 @@ +class String + def %(args) + if args.is_a? Array + sprintf(self, *args) + else + sprintf(self, args) + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c new file mode 100644 index 00000000..946b43a8 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c @@ -0,0 +1,30 @@ +/* +** kernel.c - Kernel module suppliment +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> + +mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */ + +void +mrb_mruby_sprintf_gem_init(mrb_state* mrb) +{ + struct RClass *krn; + + if (mrb->kernel_module == NULL) { + mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* Might be PARANOID. */ + } + krn = mrb->kernel_module; + + mrb_define_method(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY()); + mrb_define_method(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY()); +} + +void +mrb_mruby_sprintf_gem_final(mrb_state* mrb) +{ + /* nothing to do. */ +} + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c new file mode 100644 index 00000000..d2069924 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c @@ -0,0 +1,1113 @@ +/* +** sprintf.c - Kernel.#sprintf +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <mruby/string.h> +#include <mruby/hash.h> +#include <mruby/numeric.h> +#include <math.h> +#include <ctype.h> + +#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ +#define BITSPERDIG MRB_INT_BIT +#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n))) + +mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value); +static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int); + +static char* +remove_sign_bits(char *str, int base) +{ + char *t; + + t = str; + if (base == 16) { + while (*t == 'f') { + t++; + } + } + else if (base == 8) { + *t |= EXTENDSIGN(3, strlen(t)); + while (*t == '7') { + t++; + } + } + else if (base == 2) { + while (*t == '1') { + t++; + } + } + + return t; +} + +static char +sign_bits(int base, const char *p) +{ + char c; + + switch (base) { + case 16: + if (*p == 'X') c = 'F'; + else c = 'f'; + break; + case 8: + c = '7'; break; + case 2: + c = '1'; break; + default: + c = '.'; break; + } + return c; +} + +static mrb_value +mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) +{ + char buf[66], *b = buf + sizeof buf; + mrb_int num = mrb_fixnum(x); + uint64_t val = (uint64_t)num; + char d; + + if (base != 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); + } + if (val == 0) { + return mrb_str_new_lit(mrb, "0"); + } + *--b = '\0'; + do { + *--b = mrb_digitmap[(int)(val % base)]; + } while (val /= base); + + if (num < 0) { + b = remove_sign_bits(b, base); + switch (base) { + case 16: d = 'f'; break; + case 8: d = '7'; break; + case 2: d = '1'; break; + default: d = 0; break; + } + + if (d && *b != d) { + *--b = d; + } + } + + return mrb_str_new_cstr(mrb, b); +} + +#define FNONE 0 +#define FSHARP 1 +#define FMINUS 2 +#define FPLUS 4 +#define FZERO 8 +#define FSPACE 16 +#define FWIDTH 32 +#define FPREC 64 +#define FPREC0 128 + +#define CHECK(l) do {\ +/* int cr = ENC_CODERANGE(result);*/\ + while ((l) >= bsiz - blen) {\ + bsiz*=2;\ + if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \ + }\ + mrb_str_resize(mrb, result, bsiz);\ +/* ENC_CODERANGE_SET(result, cr);*/\ + buf = RSTRING_PTR(result);\ +} while (0) + +#define PUSH(s, l) do { \ + CHECK(l);\ + memcpy(&buf[blen], s, l);\ + blen += (mrb_int)(l);\ +} while (0) + +#define FILL(c, l) do { \ + CHECK(l);\ + memset(&buf[blen], c, l);\ + blen += (l);\ +} while (0) + +static void +check_next_arg(mrb_state *mrb, int posarg, int nextarg) +{ + switch (posarg) { + case -1: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)); + break; + case -2: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)); + break; + default: + break; + } +} + +static void +check_pos_arg(mrb_state *mrb, int posarg, int n) +{ + if (posarg > 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", + mrb_fixnum_value(n), mrb_fixnum_value(posarg)); + } + if (posarg == -2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)); + } + if (n < 1) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)); + } +} + +static void +check_name_arg(mrb_state *mrb, int posarg, const char *name, int len) +{ + if (posarg > 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", + mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)); + } + if (posarg == -1) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))); + } +} + +#define GETNEXTARG() (\ + check_next_arg(mrb, posarg, nextarg),\ + (posarg = nextarg++, GETNTHARG(posarg))) + +#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG()) + +#define GETPOSARG(n) (\ + check_pos_arg(mrb, posarg, n),\ + (posarg = -1, GETNTHARG(n))) + +#define GETNTHARG(nth) \ + ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth]) + +#define GETNAMEARG(id, name, len) (\ + check_name_arg(mrb, posarg, name, len),\ + (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value()))) + +#define GETNUM(n, val) \ + for (; p < end && ISDIGIT(*p); p++) {\ + int next_n = 10 * n + (*p - '0'); \ + if (next_n / 10 != n) {\ + mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \ + } \ + n = next_n; \ + } \ + if (p >= end) { \ + mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \ + } + +#define GETASTER(num) do { \ + mrb_value tmp_v; \ + t = p++; \ + n = 0; \ + GETNUM(n, val); \ + if (*p == '$') { \ + tmp_v = GETPOSARG(n); \ + } \ + else { \ + tmp_v = GETNEXTARG(); \ + p = t; \ + } \ + num = mrb_int(mrb, tmp_v); \ +} while (0) + +static mrb_value +get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) +{ + mrb_value tmp; + + if (!mrb_undef_p(*hash)) return *hash; + if (argc != 2) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); + } + tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash"); + if (mrb_nil_p(tmp)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); + } + return (*hash = tmp); +} + +/* + * call-seq: + * format(format_string [, arguments...] ) -> string + * sprintf(format_string [, arguments...] ) -> string + * + * Returns the string resulting from applying <i>format_string</i> to + * any additional arguments. Within the format string, any characters + * other than format sequences are copied to the result. + * + * The syntax of a format sequence is follows. + * + * %[flags][width][.precision]type + * + * A format + * sequence consists of a percent sign, followed by optional flags, + * width, and precision indicators, then terminated with a field type + * character. The field type controls how the corresponding + * <code>sprintf</code> argument is to be interpreted, while the flags + * modify that interpretation. + * + * The field type characters are: + * + * Field | Integer Format + * ------+-------------------------------------------------------------- + * b | Convert argument as a binary number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with '..1'. + * B | Equivalent to 'b', but uses an uppercase 0B for prefix + * | in the alternative format by #. + * d | Convert argument as a decimal number. + * i | Identical to 'd'. + * o | Convert argument as an octal number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with '..7'. + * u | Identical to 'd'. + * x | Convert argument as a hexadecimal number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with '..f' (representing an infinite string of + * | leading 'ff's). + * X | Equivalent to 'x', but uses uppercase letters. + * + * Field | Float Format + * ------+-------------------------------------------------------------- + * e | Convert floating point argument into exponential notation + * | with one digit before the decimal point as [-]d.dddddde[+-]dd. + * | The precision specifies the number of digits after the decimal + * | point (defaulting to six). + * E | Equivalent to 'e', but uses an uppercase E to indicate + * | the exponent. + * f | Convert floating point argument as [-]ddd.dddddd, + * | where the precision specifies the number of digits after + * | the decimal point. + * g | Convert a floating point number using exponential form + * | if the exponent is less than -4 or greater than or + * | equal to the precision, or in dd.dddd form otherwise. + * | The precision specifies the number of significant digits. + * G | Equivalent to 'g', but use an uppercase 'E' in exponent form. + * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd, + * | which is consisted from optional sign, "0x", fraction part + * | as hexadecimal, "p", and exponential part as decimal. + * A | Equivalent to 'a', but use uppercase 'X' and 'P'. + * + * Field | Other Format + * ------+-------------------------------------------------------------- + * c | Argument is the numeric code for a single character or + * | a single character string itself. + * p | The valuing of argument.inspect. + * s | Argument is a string to be substituted. If the format + * | sequence contains a precision, at most that many characters + * | will be copied. + * % | A percent sign itself will be displayed. No argument taken. + * + * The flags modifies the behavior of the formats. + * The flag characters are: + * + * Flag | Applies to | Meaning + * ---------+---------------+----------------------------------------- + * space | bBdiouxX | Leave a space at the start of + * | aAeEfgG | non-negative numbers. + * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use + * | | a minus sign with absolute value for + * | | negative values. + * ---------+---------------+----------------------------------------- + * (digit)$ | all | Specifies the absolute argument number + * | | for this field. Absolute and relative + * | | argument numbers cannot be mixed in a + * | | sprintf string. + * ---------+---------------+----------------------------------------- + * # | bBoxX | Use an alternative format. + * | aAeEfgG | For the conversions 'o', increase the precision + * | | until the first digit will be '0' if + * | | it is not formatted as complements. + * | | For the conversions 'x', 'X', 'b' and 'B' + * | | on non-zero, prefix the result with "0x", + * | | "0X", "0b" and "0B", respectively. + * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G', + * | | force a decimal point to be added, + * | | even if no digits follow. + * | | For 'g' and 'G', do not remove trailing zeros. + * ---------+---------------+----------------------------------------- + * + | bBdiouxX | Add a leading plus sign to non-negative + * | aAeEfgG | numbers. + * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use + * | | a minus sign with absolute value for + * | | negative values. + * ---------+---------------+----------------------------------------- + * - | all | Left-justify the result of this conversion. + * ---------+---------------+----------------------------------------- + * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. + * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1 + * | (numeric fmt) | is used for negative numbers formatted as + * | | complements. + * ---------+---------------+----------------------------------------- + * * | all | Use the next argument as the field width. + * | | If negative, left-justify the result. If the + * | | asterisk is followed by a number and a dollar + * | | sign, use the indicated argument as the width. + * + * Examples of flags: + * + * # '+' and space flag specifies the sign of non-negative numbers. + * sprintf("%d", 123) #=> "123" + * sprintf("%+d", 123) #=> "+123" + * sprintf("% d", 123) #=> " 123" + * + * # '#' flag for 'o' increases number of digits to show '0'. + * # '+' and space flag changes format of negative numbers. + * sprintf("%o", 123) #=> "173" + * sprintf("%#o", 123) #=> "0173" + * sprintf("%+o", -123) #=> "-173" + * sprintf("%o", -123) #=> "..7605" + * sprintf("%#o", -123) #=> "..7605" + * + * # '#' flag for 'x' add a prefix '0x' for non-zero numbers. + * # '+' and space flag disables complements for negative numbers. + * sprintf("%x", 123) #=> "7b" + * sprintf("%#x", 123) #=> "0x7b" + * sprintf("%+x", -123) #=> "-7b" + * sprintf("%x", -123) #=> "..f85" + * sprintf("%#x", -123) #=> "0x..f85" + * sprintf("%#x", 0) #=> "0" + * + * # '#' for 'X' uses the prefix '0X'. + * sprintf("%X", 123) #=> "7B" + * sprintf("%#X", 123) #=> "0X7B" + * + * # '#' flag for 'b' add a prefix '0b' for non-zero numbers. + * # '+' and space flag disables complements for negative numbers. + * sprintf("%b", 123) #=> "1111011" + * sprintf("%#b", 123) #=> "0b1111011" + * sprintf("%+b", -123) #=> "-1111011" + * sprintf("%b", -123) #=> "..10000101" + * sprintf("%#b", -123) #=> "0b..10000101" + * sprintf("%#b", 0) #=> "0" + * + * # '#' for 'B' uses the prefix '0B'. + * sprintf("%B", 123) #=> "1111011" + * sprintf("%#B", 123) #=> "0B1111011" + * + * # '#' for 'e' forces to show the decimal point. + * sprintf("%.0e", 1) #=> "1e+00" + * sprintf("%#.0e", 1) #=> "1.e+00" + * + * # '#' for 'f' forces to show the decimal point. + * sprintf("%.0f", 1234) #=> "1234" + * sprintf("%#.0f", 1234) #=> "1234." + * + * # '#' for 'g' forces to show the decimal point. + * # It also disables stripping lowest zeros. + * sprintf("%g", 123.4) #=> "123.4" + * sprintf("%#g", 123.4) #=> "123.400" + * sprintf("%g", 123456) #=> "123456" + * sprintf("%#g", 123456) #=> "123456." + * + * The field width is an optional integer, followed optionally by a + * period and a precision. The width specifies the minimum number of + * characters that will be written to the result for this field. + * + * Examples of width: + * + * # padding is done by spaces, width=20 + * # 0 or radix-1. <------------------> + * sprintf("%20d", 123) #=> " 123" + * sprintf("%+20d", 123) #=> " +123" + * sprintf("%020d", 123) #=> "00000000000000000123" + * sprintf("%+020d", 123) #=> "+0000000000000000123" + * sprintf("% 020d", 123) #=> " 0000000000000000123" + * sprintf("%-20d", 123) #=> "123 " + * sprintf("%-+20d", 123) #=> "+123 " + * sprintf("%- 20d", 123) #=> " 123 " + * sprintf("%020x", -123) #=> "..ffffffffffffffff85" + * + * For + * numeric fields, the precision controls the number of decimal places + * displayed. For string fields, the precision determines the maximum + * number of characters to be copied from the string. (Thus, the format + * sequence <code>%10.10s</code> will always contribute exactly ten + * characters to the result.) + * + * Examples of precisions: + * + * # precision for 'd', 'o', 'x' and 'b' is + * # minimum number of digits <------> + * sprintf("%20.8d", 123) #=> " 00000123" + * sprintf("%20.8o", 123) #=> " 00000173" + * sprintf("%20.8x", 123) #=> " 0000007b" + * sprintf("%20.8b", 123) #=> " 01111011" + * sprintf("%20.8d", -123) #=> " -00000123" + * sprintf("%20.8o", -123) #=> " ..777605" + * sprintf("%20.8x", -123) #=> " ..ffff85" + * sprintf("%20.8b", -11) #=> " ..110101" + * + * # "0x" and "0b" for '#x' and '#b' is not counted for + * # precision but "0" for '#o' is counted. <------> + * sprintf("%#20.8d", 123) #=> " 00000123" + * sprintf("%#20.8o", 123) #=> " 00000173" + * sprintf("%#20.8x", 123) #=> " 0x0000007b" + * sprintf("%#20.8b", 123) #=> " 0b01111011" + * sprintf("%#20.8d", -123) #=> " -00000123" + * sprintf("%#20.8o", -123) #=> " ..777605" + * sprintf("%#20.8x", -123) #=> " 0x..ffff85" + * sprintf("%#20.8b", -11) #=> " 0b..110101" + * + * # precision for 'e' is number of + * # digits after the decimal point <------> + * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03" + * + * # precision for 'f' is number of + * # digits after the decimal point <------> + * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" + * + * # precision for 'g' is number of + * # significant digits <-------> + * sprintf("%20.8g", 1234.56789) #=> " 1234.5679" + * + * # <-------> + * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08" + * + * # precision for 's' is + * # maximum number of characters <------> + * sprintf("%20.8s", "string test") #=> " string t" + * + * Examples: + * + * sprintf("%d %04x", 123, 123) #=> "123 007b" + * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'" + * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello" + * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8" + * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23" + * sprintf("%u", -123) #=> "-123" + * + * For more complex formatting, Ruby supports a reference by name. + * %<name>s style uses format style, but %{name} style doesn't. + * + * Exapmles: + * sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 }) + * #=> 1 : 2.000000 + * sprintf("%{foo}f", { :foo => 1 }) + * # => "1f" + */ + +mrb_value +mrb_f_sprintf(mrb_state *mrb, mrb_value obj) +{ + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + + if (argc <= 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); + return mrb_nil_value(); + } + else { + return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]); + } +} + +mrb_value +mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) +{ + const char *p, *end; + char *buf; + mrb_int blen; + mrb_int bsiz; + mrb_value result; + mrb_int n; + mrb_int width; + mrb_int prec; + int nextarg = 1; + int posarg = 0; + mrb_value nextvalue; + mrb_value str; + mrb_value hash = mrb_undef_value(); + +#define CHECK_FOR_WIDTH(f) \ + if ((f) & FWIDTH) { \ + mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ + } \ + if ((f) & FPREC0) { \ + mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ + } +#define CHECK_FOR_FLAGS(f) \ + if ((f) & FWIDTH) { \ + mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ + } \ + if ((f) & FPREC0) { \ + mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ + } + + ++argc; + --argv; + fmt = mrb_str_to_str(mrb, fmt); + p = RSTRING_PTR(fmt); + end = p + RSTRING_LEN(fmt); + blen = 0; + bsiz = 120; + result = mrb_str_new_capa(mrb, bsiz); + buf = RSTRING_PTR(result); + memset(buf, 0, bsiz); + + for (; p < end; p++) { + const char *t; + mrb_sym id = 0; + int flags = FNONE; + + for (t = p; t < end && *t != '%'; t++) ; + if (t + 1 == end) ++t; + PUSH(p, t - p); + if (t >= end) + goto sprint_exit; /* end of fmt string */ + + p = t + 1; /* skip '%' */ + + width = prec = -1; + nextvalue = mrb_undef_value(); + +retry: + switch (*p) { + default: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); + break; + + case ' ': + CHECK_FOR_FLAGS(flags); + flags |= FSPACE; + p++; + goto retry; + + case '#': + CHECK_FOR_FLAGS(flags); + flags |= FSHARP; + p++; + goto retry; + + case '+': + CHECK_FOR_FLAGS(flags); + flags |= FPLUS; + p++; + goto retry; + + case '-': + CHECK_FOR_FLAGS(flags); + flags |= FMINUS; + p++; + goto retry; + + case '0': + CHECK_FOR_FLAGS(flags); + flags |= FZERO; + p++; + goto retry; + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + GETNUM(n, width); + if (*p == '$') { + if (!mrb_undef_p(nextvalue)) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n)); + } + nextvalue = GETPOSARG(n); + p++; + goto retry; + } + CHECK_FOR_WIDTH(flags); + width = n; + flags |= FWIDTH; + goto retry; + + case '<': + case '{': { + const char *start = p; + char term = (*p == '<') ? '>' : '}'; + mrb_value symname; + + for (; p < end && *p != term; ) + p++; + if (id) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", + mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); + } + symname = mrb_str_new(mrb, start + 1, p - start - 1); + id = mrb_intern_str(mrb, symname); + nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1)); + if (mrb_undef_p(nextvalue)) { + mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); + } + if (term == '}') goto format_s; + p++; + goto retry; + } + + case '*': + CHECK_FOR_WIDTH(flags); + flags |= FWIDTH; + GETASTER(width); + if (width < 0) { + flags |= FMINUS; + width = -width; + } + p++; + goto retry; + + case '.': + if (flags & FPREC0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice"); + } + flags |= FPREC|FPREC0; + + prec = 0; + p++; + if (*p == '*') { + GETASTER(prec); + if (prec < 0) { /* ignore negative precision */ + flags &= ~FPREC; + } + p++; + goto retry; + } + + GETNUM(prec, precision); + goto retry; + + case '\n': + case '\0': + p--; + /* fallthrough */ + case '%': + if (flags != FNONE) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); + } + PUSH("%", 1); + break; + + case 'c': { + mrb_value val = GETARG(); + mrb_value tmp; + char *c; + + tmp = mrb_check_string_type(mrb, val); + if (!mrb_nil_p(tmp)) { + if (RSTRING_LEN(tmp) != 1) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); + } + } + else if (mrb_fixnum_p(val)) { + mrb_int n = mrb_fixnum(val); + + if (n < 0x80) { + char buf[1]; + + buf[0] = (char)n; + tmp = mrb_str_new(mrb, buf, 1); + } + else { + tmp = mrb_funcall(mrb, val, "chr", 0); + mrb_check_type(mrb, tmp, MRB_TT_STRING); + } + } + else { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); + } + c = RSTRING_PTR(tmp); + n = RSTRING_LEN(tmp); + if (!(flags & FWIDTH)) { + PUSH(c, n); + } + else if ((flags & FMINUS)) { + PUSH(c, n); + if (width>0) FILL(' ', width-1); + } + else { + if (width>0) FILL(' ', width-1); + PUSH(c, n); + } + } + break; + + case 's': + case 'p': + format_s: + { + mrb_value arg = GETARG(); + mrb_int len; + mrb_int slen; + + if (*p == 'p') arg = mrb_inspect(mrb, arg); + str = mrb_obj_as_string(mrb, arg); + len = RSTRING_LEN(str); + if (RSTRING(result)->flags & MRB_STR_EMBED) { + mrb_int tmp_n = len; + RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK; + RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT; + } + else { + RSTRING(result)->as.heap.len = blen; + } + if (flags&(FPREC|FWIDTH)) { + slen = RSTRING_LEN(str); + if (slen < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); + } + if ((flags&FPREC) && (prec < slen)) { + char *p = RSTRING_PTR(str) + prec; + slen = prec; + len = (mrb_int)(p - RSTRING_PTR(str)); + } + /* need to adjust multi-byte string pos */ + if ((flags&FWIDTH) && (width > slen)) { + width -= (int)slen; + if (!(flags&FMINUS)) { + FILL(' ', width); + } + PUSH(RSTRING_PTR(str), len); + if (flags&FMINUS) { + FILL(' ', width); + } + break; + } + } + PUSH(RSTRING_PTR(str), len); + } + break; + + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'b': + case 'B': + case 'u': { + mrb_value val = GETARG(); + char nbuf[68], *s; + const char *prefix = NULL; + int sign = 0, dots = 0; + char sc = 0; + mrb_int v = 0; + int base; + mrb_int len; + + if (flags & FSHARP) { + switch (*p) { + case 'o': prefix = "0"; break; + case 'x': prefix = "0x"; break; + case 'X': prefix = "0X"; break; + case 'b': prefix = "0b"; break; + case 'B': prefix = "0B"; break; + default: break; + } + } + + bin_retry: + switch (mrb_type(val)) { + case MRB_TT_FLOAT: + val = mrb_flo_to_fixnum(mrb, val); + if (mrb_fixnum_p(val)) goto bin_retry; + break; + case MRB_TT_STRING: + val = mrb_str_to_inum(mrb, val, 0, TRUE); + goto bin_retry; + case MRB_TT_FIXNUM: + v = mrb_fixnum(val); + break; + default: + val = mrb_Integer(mrb, val); + goto bin_retry; + } + + switch (*p) { + case 'o': + base = 8; break; + case 'x': + case 'X': + base = 16; break; + case 'b': + case 'B': + base = 2; break; + case 'u': + case 'd': + case 'i': + sign = 1; + default: + base = 10; break; + } + + if (sign) { + if (v >= 0) { + if (flags & FPLUS) { + sc = '+'; + width--; + } + else if (flags & FSPACE) { + sc = ' '; + width--; + } + } + else { + sc = '-'; + width--; + } + mrb_assert(base == 10); + snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); + s = nbuf; + if (v < 0) s++; /* skip minus sign */ + } + else { + s = nbuf; + if (v < 0) { + dots = 1; + } + switch (base) { + case 2: + if (v < 0) { + val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); + } + else { + val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); + } + strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); + break; + case 8: + snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); + break; + case 16: + snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); + break; + } + if (v < 0) { + char d; + + s = remove_sign_bits(s, base); + switch (base) { + case 16: d = 'f'; break; + case 8: d = '7'; break; + case 2: d = '1'; break; + default: d = 0; break; + } + + if (d && *s != d) { + *--s = d; + } + } + } + { + size_t size; + size = strlen(s); + /* PARANOID: assert(size <= MRB_INT_MAX) */ + len = (mrb_int)size; + } + + if (*p == 'X') { + char *pp = s; + int c; + while ((c = (int)(unsigned char)*pp) != 0) { + *pp = toupper(c); + pp++; + } + } + + if (prefix && !prefix[1]) { /* octal */ + if (dots) { + prefix = NULL; + } + else if (len == 1 && *s == '0') { + len = 0; + if (flags & FPREC) prec--; + } + else if ((flags & FPREC) && (prec > len)) { + prefix = NULL; + } + } + else if (len == 1 && *s == '0') { + prefix = NULL; + } + + if (prefix) { + size_t size; + size = strlen(prefix); + /* PARANOID: assert(size <= MRB_INT_MAX). + * this check is absolutely paranoid. */ + width -= (mrb_int)size; + } + + if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { + prec = width; + width = 0; + } + else { + if (prec < len) { + if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; + prec = len; + } + width -= prec; + } + + if (!(flags&FMINUS) && width > 0) { + FILL(' ', width); + width = 0; + } + + if (sc) PUSH(&sc, 1); + + if (prefix) { + int plen = (int)strlen(prefix); + PUSH(prefix, plen); + } + if (dots) { + prec -= 2; + width -= 2; + PUSH("..", 2); + } + + if (prec > len) { + CHECK(prec - len); + if ((flags & (FMINUS|FPREC)) != FMINUS) { + char c = '0'; + FILL(c, prec - len); + } else if (v < 0) { + char c = sign_bits(base, p); + FILL(c, prec - len); + } + } + PUSH(s, len); + if (width > 0) { + FILL(' ', width); + } + } + break; + + case 'f': + case 'g': + case 'G': + case 'e': + case 'E': + case 'a': + case 'A': { + mrb_value val = GETARG(); + double fval; + int i, need = 6; + char fbuf[32]; + + fval = mrb_float(mrb_Float(mrb, val)); + if (!isfinite(fval)) { + const char *expr; + const int elen = 3; + char sign = '\0'; + + if (isnan(fval)) { + expr = "NaN"; + } + else { + expr = "Inf"; + } + need = elen; + if (!isnan(fval) && fval < 0.0) + sign = '-'; + else if (flags & (FPLUS|FSPACE)) + sign = (flags & FPLUS) ? '+' : ' '; + if (sign) + ++need; + if ((flags & FWIDTH) && need < width) + need = width; + + if (need < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); + } + FILL(' ', need); + if (flags & FMINUS) { + if (sign) + buf[blen - need--] = sign; + memcpy(&buf[blen - need], expr, elen); + } + else { + if (sign) + buf[blen - elen - 1] = sign; + memcpy(&buf[blen - elen], expr, elen); + } + break; + } + + fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); + need = 0; + if (*p != 'e' && *p != 'E') { + i = INT_MIN; + frexp(fval, &i); + if (i > 0) + need = BIT_DIGITS(i); + } + need += (flags&FPREC) ? prec : 6; + if ((flags&FWIDTH) && need < width) + need = width; + need += 20; + if (need <= 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, + (width > prec ? "width too big" : "prec too big")); + } + + CHECK(need); + n = snprintf(&buf[blen], need, fbuf, fval); + if (n < 0) { + mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); + } + blen += n; + } + break; + } + flags = FNONE; + } + + sprint_exit: +#if 0 + /* XXX - We cannot validate the number of arguments if (digit)$ style used. + */ + if (posarg >= 0 && nextarg < argc) { + const char *mesg = "too many arguments for format string"; + if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); + if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg)); + } +#endif + mrb_str_resize(mrb, result, blen); + + return result; +} + +static void +fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec) +{ + char *end = buf + size; + int n; + + *buf++ = '%'; + if (flags & FSHARP) *buf++ = '#'; + if (flags & FPLUS) *buf++ = '+'; + if (flags & FMINUS) *buf++ = '-'; + if (flags & FZERO) *buf++ = '0'; + if (flags & FSPACE) *buf++ = ' '; + + if (flags & FWIDTH) { + n = snprintf(buf, end - buf, "%d", (int)width); + buf += n; + } + + if (flags & FPREC) { + n = snprintf(buf, end - buf, ".%d", (int)prec); + buf += n; + } + + *buf++ = c; + *buf = '\0'; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb new file mode 100644 index 00000000..e3b66ef9 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb @@ -0,0 +1,109 @@ +#assert('Kernel.sprintf') do +#end + +assert('String#%') do + assert_equal "one=1", "one=%d" % 1 + assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ] + assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" } + assert_equal 15, ("%b" % (1<<14)).size +end + +assert('String#% with inf') do + inf = Float::INFINITY + + assert_equal "Inf", "%f" % inf + assert_equal "Inf", "%2f" % inf + assert_equal "Inf", "%3f" % inf + assert_equal " Inf", "%4f" % inf + assert_equal " Inf", "%5f" % inf + + assert_equal "+Inf", "%+f" % inf + assert_equal "+Inf", "%+2f" % inf + assert_equal "+Inf", "%+3f" % inf + assert_equal "+Inf", "%+4f" % inf + assert_equal " +Inf", "%+5f" % inf + + assert_equal "Inf", "%-f" % inf + assert_equal "Inf", "%-2f" % inf + assert_equal "Inf", "%-3f" % inf + assert_equal "Inf ", "%-4f" % inf + assert_equal "Inf ", "%-5f" % inf + + assert_equal " Inf", "% f" % inf + assert_equal " Inf", "% 2f" % inf + assert_equal " Inf", "% 3f" % inf + assert_equal " Inf", "% 4f" % inf + assert_equal " Inf", "% 5f" % inf +end + +assert('String#% with nan') do + nan = Float::NAN + + assert_equal "NaN", "%f" % nan + assert_equal "NaN", "%2f" % nan + assert_equal "NaN", "%3f" % nan + assert_equal " NaN", "%4f" % nan + assert_equal " NaN", "%5f" % nan + + assert_equal "+NaN", "%+f" % nan + assert_equal "+NaN", "%+2f" % nan + assert_equal "+NaN", "%+3f" % nan + assert_equal "+NaN", "%+4f" % nan + assert_equal " +NaN", "%+5f" % nan + + assert_equal "NaN", "%-f" % nan + assert_equal "NaN", "%-2f" % nan + assert_equal "NaN", "%-3f" % nan + assert_equal "NaN ", "%-4f" % nan + assert_equal "NaN ", "%-5f" % nan + + assert_equal " NaN", "% f" % nan + assert_equal " NaN", "% 2f" % nan + assert_equal " NaN", "% 3f" % nan + assert_equal " NaN", "% 4f" % nan + assert_equal " NaN", "% 5f" % nan +end + +assert("String#% with invalid chr") do + begin + class Fixnum + alias_method :chr_, :chr if method_defined?(:chr) + + def chr + nil + end + end + + assert_raise TypeError do + "%c" % 0x80 + end + ensure + class Fixnum + if method_defined?(:chr_) + alias_method :chr, :chr_ + remove_method :chr_ + end + end + end +end + +assert("String#% %b") do + assert_equal("..10115", "%0b5" % -5) +end + +assert("String#% %d") do + assert_equal(" 10", "%4d" % 10) + assert_equal("1000", "%4d" % 1000) + assert_equal("10000", "%4d" % 10000) +end + +assert("String#% invalid format") do + assert_raise ArgumentError do + "%?" % "" + end +end + +assert("String#% invalid format shared substring") do + fmt = ("x"*30+"%!")[0...-1] + assert_equal fmt, sprintf(fmt, "") +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake new file mode 100644 index 00000000..9812f2cc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake @@ -0,0 +1,6 @@ +MRuby::Gem::Specification.new('mruby-string-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'String class extension' + spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb new file mode 100644 index 00000000..c3a7eb38 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb @@ -0,0 +1,355 @@ +class String + + ## + # call-seq: + # String.try_convert(obj) -> string or nil + # + # Try to convert <i>obj</i> into a String, using to_str method. + # Returns converted string or nil if <i>obj</i> cannot be converted + # for any reason. + # + # String.try_convert("str") #=> "str" + # String.try_convert(/re/) #=> nil + # + def self.try_convert(obj) + if obj.respond_to?(:to_str) + obj.to_str + else + nil + end + end + + ## + # call-seq: + # string.clear -> string + # + # Makes string empty. + # + # a = "abcde" + # a.clear #=> "" + # + def clear + self.replace("") + end + + ## + # call-seq: + # str.lstrip -> new_str + # + # Returns a copy of <i>str</i> with leading whitespace removed. See also + # <code>String#rstrip</code> and <code>String#strip</code>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip #=> "hello" + # + def lstrip + a = 0 + z = self.size - 1 + a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) + (z >= 0) ? self[a..z] : "" + end + + ## + # call-seq: + # str.rstrip -> new_str + # + # Returns a copy of <i>str</i> with trailing whitespace removed. See also + # <code>String#lstrip</code> and <code>String#strip</code>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip #=> "hello" + # + def rstrip + a = 0 + z = self.size - 1 + z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) + (z >= 0) ? self[a..z] : "" + end + + ## + # call-seq: + # str.strip -> new_str + # + # Returns a copy of <i>str</i> with leading and trailing whitespace removed. + # + # " hello ".strip #=> "hello" + # "\tgoodbye\r\n".strip #=> "goodbye" + # + def strip + a = 0 + z = self.size - 1 + a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) + z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) + (z >= 0) ? self[a..z] : "" + end + + ## + # call-seq: + # str.lstrip! -> self or nil + # + # Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no + # change was made. See also <code>String#rstrip!</code> and + # <code>String#strip!</code>. + # + # " hello ".lstrip #=> "hello " + # "hello".lstrip! #=> nil + # + def lstrip! + raise RuntimeError, "can't modify frozen String" if frozen? + s = self.lstrip + (s == self) ? nil : self.replace(s) + end + + ## + # call-seq: + # str.rstrip! -> self or nil + # + # Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if + # no change was made. See also <code>String#lstrip!</code> and + # <code>String#strip!</code>. + # + # " hello ".rstrip #=> " hello" + # "hello".rstrip! #=> nil + # + def rstrip! + raise RuntimeError, "can't modify frozen String" if frozen? + s = self.rstrip + (s == self) ? nil : self.replace(s) + end + + ## + # call-seq: + # str.strip! -> str or nil + # + # Removes leading and trailing whitespace from <i>str</i>. Returns + # <code>nil</code> if <i>str</i> was not altered. + # + def strip! + raise RuntimeError, "can't modify frozen String" if frozen? + s = self.strip + (s == self) ? nil : self.replace(s) + end + + ## + # call-seq: + # str.casecmp(other_str) -> -1, 0, +1 or nil + # + # Case-insensitive version of <code>String#<=></code>. + # + # "abcdef".casecmp("abcde") #=> 1 + # "aBcDeF".casecmp("abcdef") #=> 0 + # "abcdef".casecmp("abcdefg") #=> -1 + # "abcdef".casecmp("ABCDEF") #=> 0 + # + def casecmp(str) + self.downcase <=> str.to_str.downcase + rescue NoMethodError + raise TypeError, "no implicit conversion of #{str.class} into String" + end + + def partition(sep) + raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String + n = index(sep) + unless n.nil? + m = n + sep.size + [ slice(0, n), sep, slice(m, size - m) ] + else + [ self, "", "" ] + end + end + + def rpartition(sep) + raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String + n = rindex(sep) + unless n.nil? + m = n + sep.size + [ slice(0, n), sep, slice(m, size - m) ] + else + [ "", "", self ] + end + end + + ## + # call-seq: + # str.slice!(fixnum) -> new_str or nil + # str.slice!(fixnum, fixnum) -> new_str or nil + # str.slice!(range) -> new_str or nil + # str.slice!(other_str) -> new_str or nil + # + # Deletes the specified portion from <i>str</i>, and returns the portion + # deleted. + # + # string = "this is a string" + # string.slice!(2) #=> "i" + # string.slice!(3..6) #=> " is " + # string.slice!("r") #=> "r" + # string #=> "thsa sting" + # + def slice!(arg1, arg2=nil) + raise RuntimeError, "can't modify frozen String" if frozen? + raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil? + + if !arg1.nil? && !arg2.nil? + idx = arg1 + idx += self.size if arg1 < 0 + if idx >= 0 && idx <= self.size && arg2 > 0 + str = self[idx, arg2] + else + return nil + end + else + validated = false + if arg1.kind_of?(Range) + beg = arg1.begin + ed = arg1.end + beg += self.size if beg < 0 + ed += self.size if ed < 0 + ed -= 1 if arg1.exclude_end? + validated = true + elsif arg1.kind_of?(String) + validated = true + else + idx = arg1 + idx += self.size if arg1 < 0 + validated = true if idx >=0 && arg1 < self.size + end + if validated + str = self[arg1] + else + return nil + end + end + unless str.nil? || str == "" + if !arg1.nil? && !arg2.nil? + idx = arg1 >= 0 ? arg1 : self.size+arg1 + str2 = self[0...idx] + self[idx+arg2..-1].to_s + else + if arg1.kind_of?(Range) + idx = beg >= 0 ? beg : self.size+beg + idx2 = ed>= 0 ? ed : self.size+ed + str2 = self[0...idx] + self[idx2+1..-1].to_s + elsif arg1.kind_of?(String) + idx = self.index(arg1) + str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil? + else + idx = arg1 >= 0 ? arg1 : self.size+arg1 + str2 = self[0...idx] + self[idx+1..-1].to_s + end + end + self.replace(str2) unless str2.nil? + end + str + end + + ## + # call-seq: + # str.insert(index, other_str) -> str + # + # Inserts <i>other_str</i> before the character at the given + # <i>index</i>, modifying <i>str</i>. Negative indices count from the + # end of the string, and insert <em>after</em> the given character. + # The intent is insert <i>aString</i> so that it starts at the given + # <i>index</i>. + # + # "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" + # + def insert(idx, str) + if idx == -1 + return self << str + elsif idx < 0 + idx += 1 + end + self[idx, 0] = str + self + end + + ## + # call-seq: + # str.ljust(integer, padstr=' ') -> new_str + # + # If <i>integer</i> is greater than the length of <i>str</i>, returns a new + # <code>String</code> of length <i>integer</i> with <i>str</i> left justified + # and padded with <i>padstr</i>; otherwise, returns <i>str</i>. + # + # "hello".ljust(4) #=> "hello" + # "hello".ljust(20) #=> "hello " + # "hello".ljust(20, '1234') #=> "hello123412341234123" + def ljust(idx, padstr = ' ') + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + self + padding + end + + ## + # call-seq: + # str.rjust(integer, padstr=' ') -> new_str + # + # If <i>integer</i> is greater than the length of <i>str</i>, returns a new + # <code>String</code> of length <i>integer</i> with <i>str</i> right justified + # and padded with <i>padstr</i>; otherwise, returns <i>str</i>. + # + # "hello".rjust(4) #=> "hello" + # "hello".rjust(20) #=> " hello" + # "hello".rjust(20, '1234') #=> "123412341234123hello" + def rjust(idx, padstr = ' ') + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + padding + self + end + + def chars(&block) + if block_given? + self.split('').each do |i| + block.call(i) + end + self + else + self.split('') + end + end + + def each_char(&block) + return to_enum :each_char unless block + + split('').each do |i| + block.call(i) + end + self + end + + def codepoints(&block) + len = self.size + + if block_given? + self.split('').each do|x| + block.call(x.ord) + end + self + else + self.split('').map{|x| x.ord} + end + end + alias each_codepoint codepoints + + ## + # call-seq: + # str.prepend(other_str) -> str + # + # Prepend---Prepend the given string to <i>str</i>. + # + # a = "world" + # a.prepend("hello ") #=> "hello world" + # a #=> "hello world" + def prepend(arg) + self[0, 0] = arg + self + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c new file mode 100644 index 00000000..6bc035d6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c @@ -0,0 +1,685 @@ +#include <string.h> +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/string.h> +#include <mruby/range.h> + +static mrb_value +mrb_str_getbyte(mrb_state *mrb, mrb_value str) +{ + mrb_int pos; + mrb_get_args(mrb, "i", &pos); + + if (pos < 0) + pos += RSTRING_LEN(str); + if (pos < 0 || RSTRING_LEN(str) <= pos) + return mrb_nil_value(); + + return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]); +} + +static mrb_value +mrb_str_setbyte(mrb_state *mrb, mrb_value str) +{ + mrb_int pos, byte; + long len; + + mrb_get_args(mrb, "ii", &pos, &byte); + + len = RSTRING_LEN(str); + if (pos < -len || len <= pos) + mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos)); + if (pos < 0) + pos += len; + + mrb_str_modify(mrb, mrb_str_ptr(str)); + byte &= 0xff; + RSTRING_PTR(str)[pos] = byte; + return mrb_fixnum_value((unsigned char)byte); +} + +static mrb_value +mrb_str_byteslice(mrb_state *mrb, mrb_value str) +{ + mrb_value a1; + mrb_int len; + int argc; + + argc = mrb_get_args(mrb, "o|i", &a1, &len); + if (argc == 2) { + return mrb_str_substr(mrb, str, mrb_fixnum(a1), len); + } + switch (mrb_type(a1)) { + case MRB_TT_RANGE: + { + mrb_int beg; + + len = RSTRING_LEN(str); + switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) { + case 0: /* not range */ + break; + case 1: /* range */ + return mrb_str_substr(mrb, str, beg, len); + case 2: /* out of range */ + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1); + break; + } + return mrb_nil_value(); + } + case MRB_TT_FLOAT: + a1 = mrb_fixnum_value((mrb_int)mrb_float(a1)); + /* fall through */ + case MRB_TT_FIXNUM: + return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1); + default: + mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument"); + } + /* not reached */ + return mrb_nil_value(); +} + +/* + * call-seq: + * str.swapcase! -> str or nil + * + * Equivalent to <code>String#swapcase</code>, but modifies the receiver in + * place, returning <i>str</i>, or <code>nil</code> if no changes were made. + * Note: case conversion is effective only in ASCII region. + */ +static mrb_value +mrb_str_swapcase_bang(mrb_state *mrb, mrb_value str) +{ + char *p, *pend; + int modify = 0; + struct RString *s = mrb_str_ptr(str); + + mrb_str_modify(mrb, s); + p = RSTRING_PTR(str); + pend = p + RSTRING_LEN(str); + while (p < pend) { + if (ISUPPER(*p)) { + *p = TOLOWER(*p); + modify = 1; + } + else if (ISLOWER(*p)) { + *p = TOUPPER(*p); + modify = 1; + } + p++; + } + + if (modify) return str; + return mrb_nil_value(); +} + +/* + * call-seq: + * str.swapcase -> new_str + * + * Returns a copy of <i>str</i> with uppercase alphabetic characters converted + * to lowercase and lowercase characters converted to uppercase. + * Note: case conversion is effective only in ASCII region. + * + * "Hello".swapcase #=> "hELLO" + * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11" + */ +static mrb_value +mrb_str_swapcase(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_swapcase_bang(mrb, str); + return str; +} + +static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num); + +/* + * call-seq: + * str << integer -> str + * str.concat(integer) -> str + * str << obj -> str + * str.concat(obj) -> str + * + * Append---Concatenates the given object to <i>str</i>. If the object is a + * <code>Integer</code>, it is considered as a codepoint, and is converted + * to a character before concatenation. + * + * a = "hello " + * a << "world" #=> "hello world" + * a.concat(33) #=> "hello world!" + */ +static mrb_value +mrb_str_concat_m(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + mrb_get_args(mrb, "o", &str); + if (mrb_fixnum_p(str)) + str = mrb_fixnum_chr(mrb, str); + else + str = mrb_string_type(mrb, str); + mrb_str_concat(mrb, self, str); + return self; +} + +/* + * call-seq: + * str.start_with?([prefixes]+) -> true or false + * + * Returns true if +str+ starts with one of the +prefixes+ given. + * + * "hello".start_with?("hell") #=> true + * + * # returns true if one of the prefixes matches. + * "hello".start_with?("heaven", "hell") #=> true + * "hello".start_with?("heaven", "paradise") #=> false + * "h".start_with?("heaven", "hell") #=> false + */ +static mrb_value +mrb_str_start_with(mrb_state *mrb, mrb_value self) +{ + mrb_value *argv, sub; + mrb_int argc, i; + mrb_get_args(mrb, "*", &argv, &argc); + + for (i = 0; i < argc; i++) { + size_t len_l, len_r; + int ai = mrb_gc_arena_save(mrb); + sub = mrb_string_type(mrb, argv[i]); + mrb_gc_arena_restore(mrb, ai); + len_l = RSTRING_LEN(self); + len_r = RSTRING_LEN(sub); + if (len_l >= len_r) { + if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) { + return mrb_true_value(); + } + } + } + return mrb_false_value(); +} + +/* + * call-seq: + * str.end_with?([suffixes]+) -> true or false + * + * Returns true if +str+ ends with one of the +suffixes+ given. + */ +static mrb_value +mrb_str_end_with(mrb_state *mrb, mrb_value self) +{ + mrb_value *argv, sub; + mrb_int argc, i; + mrb_get_args(mrb, "*", &argv, &argc); + + for (i = 0; i < argc; i++) { + size_t len_l, len_r; + int ai = mrb_gc_arena_save(mrb); + sub = mrb_string_type(mrb, argv[i]); + mrb_gc_arena_restore(mrb, ai); + len_l = RSTRING_LEN(self); + len_r = RSTRING_LEN(sub); + if (len_l >= len_r) { + if (memcmp(RSTRING_PTR(self) + (len_l - len_r), + RSTRING_PTR(sub), + len_r) == 0) { + return mrb_true_value(); + } + } + } + return mrb_false_value(); +} + +static mrb_value +mrb_str_hex(mrb_state *mrb, mrb_value self) +{ + return mrb_str_to_inum(mrb, self, 16, FALSE); +} + +static mrb_value +mrb_str_oct(mrb_state *mrb, mrb_value self) +{ + return mrb_str_to_inum(mrb, self, 8, FALSE); +} + +/* + * call-seq: + * string.chr -> string + * + * Returns a one-character string at the beginning of the string. + * + * a = "abcde" + * a.chr #=> "a" + */ +static mrb_value +mrb_str_chr(mrb_state *mrb, mrb_value self) +{ + return mrb_str_substr(mrb, self, 0, 1); +} + +static mrb_value +mrb_fixnum_chr(mrb_state *mrb, mrb_value num) +{ + mrb_int cp = mrb_fixnum(num); +#ifdef MRB_UTF8_STRING + char utf8[4]; + mrb_int len; + + if (cp < 0 || 0x10FFFF < cp) { + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); + } + if (cp < 0x80) { + utf8[0] = (char)cp; + len = 1; + } + else if (cp < 0x800) { + utf8[0] = (char)(0xC0 | (cp >> 6)); + utf8[1] = (char)(0x80 | (cp & 0x3F)); + len = 2; + } + else if (cp < 0x10000) { + utf8[0] = (char)(0xE0 | (cp >> 12)); + utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ( cp & 0x3F)); + len = 3; + } + else { + utf8[0] = (char)(0xF0 | (cp >> 18)); + utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); + utf8[3] = (char)(0x80 | ( cp & 0x3F)); + len = 4; + } + return mrb_str_new(mrb, utf8, len); +#else + char c; + + if (cp < 0 || 0xff < cp) { + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); + } + c = (char)cp; + return mrb_str_new(mrb, &c, 1); +#endif +} + +/* + * call-seq: + * string.lines -> array of string + * + * Returns strings per line; + * + * a = "abc\ndef" + * a.lines #=> ["abc\n", "def"] + */ +static mrb_value +mrb_str_lines(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + mrb_value blk; + int ai; + mrb_int len; + mrb_value arg; + char *b = RSTRING_PTR(self); + char *p = b, *t; + char *e = b + RSTRING_LEN(self); + + mrb_get_args(mrb, "&", &blk); + + result = mrb_ary_new(mrb); + ai = mrb_gc_arena_save(mrb); + if (!mrb_nil_p(blk)) { + while (p < e) { + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + arg = mrb_str_new(mrb, t, len); + mrb_yield_argv(mrb, blk, 1, &arg); + mrb_gc_arena_restore(mrb, ai); + if (b != RSTRING_PTR(self)) { + ptrdiff_t diff = p - b; + b = RSTRING_PTR(self); + p = b + diff; + } + e = b + RSTRING_LEN(self); + } + return self; + } + while (p < e) { + t = p; + while (p < e && *p != '\n') p++; + if (*p == '\n') p++; + len = (mrb_int) (p - t); + mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len)); + mrb_gc_arena_restore(mrb, ai); + } + return result; +} + +/* + * call-seq: + * string.succ -> string + * + * Returns next sequence of the string; + * + * a = "abc" + * a.succ #=> "abd" + */ +static mrb_value +mrb_str_succ_bang(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + unsigned char *p, *e, *b, *t; + const char *prepend; + struct RString *s = mrb_str_ptr(self); + mrb_int l; + + if (RSTRING_LEN(self) == 0) + return self; + + mrb_str_modify(mrb, s); + l = RSTRING_LEN(self); + b = p = (unsigned char*) RSTRING_PTR(self); + t = e = p + l; + *(e--) = 0; + + // find trailing ascii/number + while (e >= b) { + if (ISALNUM(*e)) + break; + e--; + } + if (e < b) { + e = p + l - 1; + result = mrb_str_new_lit(mrb, ""); + } + else { + // find leading letter of the ascii/number + b = e; + while (b > p) { + if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z')) + break; + b--; + } + if (!ISALNUM(*b)) + b++; + result = mrb_str_new(mrb, (char*) p, b - p); + } + + while (e >= b) { + if (!ISALNUM(*e)) { + if (*e == 0xff) { + mrb_str_cat_lit(mrb, result, "\x01"); + (*e) = 0; + } + else + (*e)++; + break; + } + prepend = NULL; + if (*e == '9') { + if (e == b) prepend = "1"; + *e = '0'; + } + else if (*e == 'z') { + if (e == b) prepend = "a"; + *e = 'a'; + } + else if (*e == 'Z') { + if (e == b) prepend = "A"; + *e = 'A'; + } + else { + (*e)++; + break; + } + if (prepend) mrb_str_cat_cstr(mrb, result, prepend); + e--; + } + result = mrb_str_cat(mrb, result, (char*) b, t - b); + l = RSTRING_LEN(result); + mrb_str_resize(mrb, self, l); + memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l); + return self; +} + +static mrb_value +mrb_str_succ(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_succ_bang(mrb, str); + return str; +} + +#ifdef MRB_UTF8_STRING +static const char utf8len_codepage_zero[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, +}; + +static mrb_int +utf8code(unsigned char* p) +{ + mrb_int len; + + if (p[0] < 0x80) + return p[0]; + + len = utf8len_codepage_zero[p[0]]; + if (len > 1 && (p[1] & 0xc0) == 0x80) { + if (len == 2) + return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); + if ((p[2] & 0xc0) == 0x80) { + if (len == 3) + return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f); + if ((p[3] & 0xc0) == 0x80) { + if (len == 4) + return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); + if ((p[4] & 0xc0) == 0x80) { + if (len == 5) + return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) + + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) + + (p[4] & 0x3f); + if ((p[5] & 0xc0) == 0x80 && len == 6) + return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) + + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) + + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f); + } + } + } + } + return p[0]; +} + +static mrb_value +mrb_str_ord(mrb_state* mrb, mrb_value str) +{ + if (RSTRING_LEN(str) == 0) + mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); + return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str))); +} +#else +static mrb_value +mrb_str_ord(mrb_state* mrb, mrb_value str) +{ + if (RSTRING_LEN(str) == 0) + mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); + return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]); +} +#endif + +static mrb_bool +all_digits_p(const char *s, mrb_int len) +{ + while (len-- > 0) { + if (!ISDIGIT(*s)) return FALSE; + s++; + } + return TRUE; +} + +/* + * call-seq: + * str.upto(other_str, exclusive=false) {|s| block } -> str + * str.upto(other_str, exclusive=false) -> an_enumerator + * + * Iterates through successive values, starting at <i>str</i> and + * ending at <i>other_str</i> inclusive, passing each value in turn to + * the block. The <code>String#succ</code> method is used to generate + * each value. If optional second argument exclusive is omitted or is false, + * the last value will be included; otherwise it will be excluded. + * + * If no block is given, an enumerator is returned instead. + * + * "a8".upto("b6") {|s| print s, ' ' } + * for s in "a8".."b6" + * print s, ' ' + * end + * + * <em>produces:</em> + * + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * + * If <i>str</i> and <i>other_str</i> contains only ascii numeric characters, + * both are recognized as decimal numbers. In addition, the width of + * string (e.g. leading zeros) is handled appropriately. + * + * "9".upto("11").to_a #=> ["9", "10", "11"] + * "25".upto("5").to_a #=> [] + * "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] + */ +static mrb_value +mrb_str_upto(mrb_state *mrb, mrb_value beg) +{ + mrb_value end; + mrb_value exclusive = mrb_false_value(); + mrb_value block = mrb_nil_value(); + mrb_value current, after_end; + mrb_int n; + mrb_bool excl; + + mrb_get_args(mrb, "o|o&", &end, &exclusive, &block); + + if (mrb_nil_p(block)) { + return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive); + } + end = mrb_string_type(mrb, end); + excl = mrb_test(exclusive); + + /* single character */ + if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 && + ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) { + char c = RSTRING_PTR(beg)[0]; + char e = RSTRING_PTR(end)[0]; + int ai = mrb_gc_arena_save(mrb); + + if (c > e || (excl && c == e)) return beg; + for (;;) { + mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1)); + mrb_gc_arena_restore(mrb, ai); + if (!excl && c == e) break; + c++; + if (excl && c == e) break; + } + return beg; + } + /* both edges are all digits */ + if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) && + all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) && + all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) { + mrb_int min_width = RSTRING_LEN(beg); + mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); + mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); + int ai = mrb_gc_arena_save(mrb); + + while (bi <= ei) { + mrb_value ns, str; + + if (excl && bi == ei) break; + ns = mrb_format(mrb, "%S", mrb_fixnum_value(bi)); + if (min_width > RSTRING_LEN(ns)) { + str = mrb_str_new(mrb, NULL, min_width); + memset(RSTRING_PTR(str), '0', min_width-RSTRING_LEN(ns)); + memcpy(RSTRING_PTR(str)+min_width-RSTRING_LEN(ns), + RSTRING_PTR(ns), RSTRING_LEN(ns)); + } + else { + str = ns; + } + mrb_yield(mrb, block, str); + mrb_gc_arena_restore(mrb, ai); + bi++; + } + + return beg; + } + /* normal case */ + n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end)); + if (n > 0 || (excl && n == 0)) return beg; + + after_end = mrb_funcall(mrb, end, "succ", 0); + current = mrb_str_dup(mrb, beg); + while (!mrb_str_equal(mrb, current, after_end)) { + int ai = mrb_gc_arena_save(mrb); + mrb_value next = mrb_nil_value(); + if (excl || !mrb_str_equal(mrb, current, end)) + next = mrb_funcall(mrb, current, "succ", 0); + mrb_yield(mrb, block, current); + if (mrb_nil_p(next)) break; + current = mrb_str_to_str(mrb, next); + if (excl && mrb_str_equal(mrb, current, end)) break; + if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0) + break; + mrb_gc_arena_restore(mrb, ai); + } + + return beg; +} + +void +mrb_mruby_string_ext_gem_init(mrb_state* mrb) +{ + struct RClass * s = mrb->string_class; + + mrb_define_method(mrb, s, "dump", mrb_str_dump, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2)); + mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); + mrb_define_method(mrb, s, "swapcase!", mrb_str_swapcase_bang, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "concat", mrb_str_concat_m, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "<<", mrb_str_concat_m, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST()); + mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST()); + mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ")); + mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!")); + mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY()); + + mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); +} + +void +mrb_mruby_string_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb new file mode 100644 index 00000000..2a568c7d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb @@ -0,0 +1,667 @@ +## +# String(Ext) Test + +UTF8STRING = ("\343\201\202".size == 1) + +assert('String.try_convert') do + assert_nil String.try_convert(nil) + assert_nil String.try_convert(:foo) + assert_equal "", String.try_convert("") + assert_equal "1,2,3", String.try_convert("1,2,3") +end + +assert('String#getbyte') do + str1 = "hello" + bytes1 = [104, 101, 108, 108, 111] + assert_equal bytes1[0], str1.getbyte(0) + assert_equal bytes1[-1], str1.getbyte(-1) + assert_equal bytes1[6], str1.getbyte(6) + + str2 = "\xFF" + bytes2 = [0xFF] + assert_equal bytes2[0], str2.getbyte(0) +end + +assert('String#setbyte') do + str1 = "hello" + h = "H".getbyte(0) + str1.setbyte(0, h) + assert_equal(h, str1.getbyte(0)) + assert_equal("Hello", str1) +end + +assert("String#setbyte raises IndexError if arg conversion resizes String") do + $s = "01234\n" + class Tmp + def to_i + $s.chomp! '' + 95 + end + end + tmp = Tmp.new + assert_raise(IndexError) { $s.setbyte(5, tmp) } +end + +assert('String#byteslice') do + str1 = "hello" + assert_equal("e", str1.byteslice(1)) + assert_equal("o", str1.byteslice(-1)) + assert_equal("ell", str1.byteslice(1..3)) + assert_equal("el", str1.byteslice(1...3)) +end + +assert('String#dump') do + ("\1" * 100).dump # should not raise an exception - regress #1210 + "\0".inspect == "\"\\000\"" and + "foo".dump == "\"foo\"" +end + +assert('String#strip') do + s = " abc " + "".strip == "" and " \t\r\n\f\v".strip == "" and + "\0a\0".strip == "\0a" and + "abc".strip == "abc" and + " abc".strip == "abc" and + "abc ".strip == "abc" and + " abc ".strip == "abc" and + s == " abc " +end + +assert('String#lstrip') do + s = " abc " + s.lstrip + "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and + "\0a\0".lstrip == "\0a\0" and + "abc".lstrip == "abc" and + " abc".lstrip == "abc" and + "abc ".lstrip == "abc " and + " abc ".lstrip == "abc " and + s == " abc " +end + +assert('String#rstrip') do + s = " abc " + s.rstrip + "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and + "\0a\0".rstrip == "\0a" and + "abc".rstrip == "abc" and + " abc".rstrip == " abc" and + "abc ".rstrip == "abc" and + " abc ".rstrip == " abc" and + s == " abc " +end + +assert('String#strip!') do + s = " abc " + t = "abc" + s.strip! == "abc" and s == "abc" and t.strip! == nil +end + +assert('String#lstrip!') do + s = " abc " + t = "abc " + s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil +end + +assert('String#rstrip!') do + s = " abc " + t = " abc" + s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil +end + +assert('String#swapcase') do + assert_equal "hELLO", "Hello".swapcase + assert_equal "CyBeR_pUnK11", "cYbEr_PuNk11".swapcase +end + +assert('String#swapcase!') do + s = "Hello" + t = s.clone + t.swapcase! + assert_equal s.swapcase, t +end + +assert('String#concat') do + assert_equal "Hello World!", "Hello " << "World" << 33 + assert_equal "Hello World!", "Hello ".concat("World").concat(33) + + o = Object.new + def o.to_str + "to_str" + end + assert_equal "hi to_str", "hi " << o + + assert_raise(TypeError) { "".concat(Object.new) } +end + +assert('String#casecmp') do + assert_equal 1, "abcdef".casecmp("abcde") + assert_equal 0, "aBcDeF".casecmp("abcdef") + assert_equal(-1, "abcdef".casecmp("abcdefg")) + assert_equal 0, "abcdef".casecmp("ABCDEF") + o = Object.new + def o.to_str + "ABCDEF" + end + assert_equal 0, "abcdef".casecmp(o) +end + +assert('String#start_with?') do + assert_true "hello".start_with?("heaven", "hell") + assert_true !"hello".start_with?("heaven", "paradise") + assert_true !"h".start_with?("heaven", "hell") + assert_raise TypeError do "hello".start_with?(true) end +end + +assert('String#end_with?') do + assert_true "string".end_with?("ing", "mng") + assert_true !"string".end_with?("str", "tri") + assert_true !"ng".end_with?("ing", "mng") + assert_raise TypeError do "hello".end_with?(true) end +end + +assert('String#partition') do + assert_equal ["a", "x", "axa"], "axaxa".partition("x") + assert_equal ["aaaaa", "", ""], "aaaaa".partition("x") + assert_equal ["", "", "aaaaa"], "aaaaa".partition("") + assert_equal ["", "a", "aaaa"], "aaaaa".partition("a") + assert_equal ["aaaa", "b", ""], "aaaab".partition("b") + assert_equal ["", "b", "aaaa"], "baaaa".partition("b") + assert_equal ["", "", ""], "".partition("a") +end + +assert('String#rpartition') do + assert_equal ["axa", "x", "a"], "axaxa".rpartition("x") + assert_equal ["", "", "aaaaa"], "aaaaa".rpartition("x") + assert_equal ["aaaaa", "", ""], "aaaaa".rpartition("") + assert_equal ["aaaa", "a", ""], "aaaaa".rpartition("a") + assert_equal ["aaaa", "b", ""], "aaaab".rpartition("b") + assert_equal ["", "b", "aaaa"], "baaaa".rpartition("b") + assert_equal ["", "", ""], "".rpartition("a") +end + +assert('String#hex') do + assert_equal 16, "10".hex + assert_equal 255, "ff".hex + assert_equal 16, "0x10".hex + assert_equal (-16), "-0x10".hex + assert_equal 0, "xyz".hex + assert_equal 16, "10z".hex + assert_equal 16, "1_0".hex + assert_equal 0, "".hex +end + +assert('String#oct') do + assert_equal 8, "10".oct + assert_equal 7, "7".oct + assert_equal 0, "8".oct + assert_equal 0, "9".oct + assert_equal 0, "xyz".oct + assert_equal 8, "10z".oct + assert_equal 8, "1_0".oct + assert_equal 8, "010".oct + assert_equal (-8), "-10".oct +end + +assert('String#chr') do + assert_equal "a", "abcde".chr + # test Fixnum#chr as well + assert_equal "a", 97.chr +end + +assert('String#lines') do + assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines + assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines + assert_equal ["\n", "\n", "\n"], "\n\n\n".lines + assert_equal [], "".lines +end + +assert('String#clear') do + # embed string + s = "foo" + assert_equal("", s.clear) + assert_equal("", s) + + # not embed string and not shared string + s = "foo" * 100 + a = s + assert_equal("", s.clear) + assert_equal("", s) + assert_equal("", a) + + # shared string + s = "foo" * 100 + a = s[10, 90] # create shared string + assert_equal("", s.clear) # clear + assert_equal("", s) # s is cleared + assert_not_equal("", a) # a should not be affected +end + +assert('String#slice!') do + a = "AooBar" + b = a.dup + assert_equal "A", a.slice!(0) + assert_equal "AooBar", b + + a = "FooBar" + assert_equal "r", a.slice!(-1) + assert_equal "FooBa", a + + a = "FooBar" + assert_nil a.slice!(6) + assert_nil a.slice!(-7) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!(0, 3) + assert_equal "Bar", a + + a = "FooBar" + assert_equal "Bar", a.slice!(-3, 3) + assert_equal "Foo", a + + a = "FooBar" + assert_equal "", a.slice!(6, 2) + assert_equal "FooBar", a + + a = "FooBar" + assert_nil a.slice!(-7,10) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!(0..2) + assert_equal "Bar", a + + a = "FooBar" + assert_equal "Bar", a.slice!(-3..-1) + assert_equal "Foo", a + + a = "FooBar" + assert_equal "", a.slice!(6..2) + assert_equal "FooBar", a + + a = "FooBar" + assert_nil a.slice!(-10..-7) + assert_equal "FooBar", a + + a = "FooBar" + assert_equal "Foo", a.slice!("Foo") + assert_equal "Bar", a + + a = "FooBar" + assert_nil a.slice!("xyzzy") + assert_equal "FooBar", a + + assert_raise(ArgumentError) { "foo".slice! } +end + +assert('String#succ') do + assert_equal "", "".succ + assert_equal "1", "0".succ + assert_equal "10", "9".succ + assert_equal "01", "00".succ + assert_equal "a1", "a0".succ + assert_equal "A1", "A0".succ + assert_equal "10", "09".succ + assert_equal "b0", "a9".succ + assert_equal "B0", "A9".succ + + assert_equal "b", "a".succ + assert_equal "aa", "z".succ + assert_equal "ab", "aa".succ + assert_equal "Ab", "Aa".succ + assert_equal "0b", "0a".succ + assert_equal "ba", "az".succ + assert_equal "Ba", "Az".succ + assert_equal "1a", "0z".succ + + assert_equal "B", "A".succ + assert_equal "AA", "Z".succ + assert_equal "AB", "AA".succ + assert_equal "aB", "aA".succ + assert_equal "0B", "0A".succ + assert_equal "BA", "AZ".succ + assert_equal "bA", "aZ".succ + assert_equal "1A", "0Z".succ + + assert_equal ".", "-".succ + assert_equal "\x01\x00", "\xff".succ + assert_equal "-b", "-a".succ + assert_equal "-aa", "-z".succ + assert_equal "-a-b-", "-a-a-".succ + assert_equal "-b-", "-a-".succ + assert_equal "-aa-", "-z-".succ + assert_equal "あb", "あa".succ + assert_equal "あba", "あaz".succ + + a = ""; a.succ! + assert_equal "", a + a = "0"; a.succ! + assert_equal "1", a + a = "9"; a.succ! + assert_equal "10", a + a = "00"; a.succ! + assert_equal "01", a + a = "a0"; a.succ! + assert_equal "a1", a + a = "A0"; a.succ! + assert_equal "A1", a + a = "09"; a.succ! + assert_equal "10", a + a = "a9"; a.succ! + assert_equal "b0", a + a = "A9"; a.succ! + assert_equal "B0", a + + a = "a"; a.succ! + assert_equal "b", a + a = "z"; a.succ! + assert_equal "aa", a + a = "aa"; a.succ! + assert_equal "ab", a + a = "Aa"; a.succ! + assert_equal "Ab", a + a = "0a"; a.succ! + assert_equal "0b", a + a = "az"; a.succ! + assert_equal "ba", a + a = "Az"; a.succ! + assert_equal "Ba", a + a = "0z"; a.succ! + assert_equal "1a", a + + a = "A"; a.succ! + assert_equal "B", a + a = "Z"; a.succ! + assert_equal "AA", a + a = "AA"; a.succ! + assert_equal "AB", a + a = "aA"; a.succ! + assert_equal "aB", a + a = "0A"; a.succ! + assert_equal "0B", a + a = "AZ"; a.succ! + assert_equal "BA", a + a = "aZ"; a.succ! + assert_equal "bA", a + a = "0Z"; a.succ! + assert_equal "1A", a + + a = "-"; a.succ! + assert_equal ".", a + a = "\xff"; a.succ! + assert_equal "\x01\x00", a + a = "-a"; a.succ! + assert_equal "-b", a + a = "-z"; a.succ! + assert_equal "-aa", a + a = "-a-a-"; a.succ! + assert_equal "-a-b-", a + a = "-a-"; a.succ! + assert_equal "-b-", a + a = "-z-"; a.succ! + assert_equal "-aa-", a + a = "あb"; a.succ! + assert_equal "あc", a + a = "あaz"; a.succ! + assert_equal "あba", a +end + +assert('String#next') do + assert_equal "01", "00".next + + a = "00"; a.next! + assert_equal "01", a +end + +assert('String#insert') do + assert_equal "Xabcd", "abcd".insert(0, 'X') + assert_equal "abcXd", "abcd".insert(3, 'X') + assert_equal "abcdX", "abcd".insert(4, 'X') + assert_equal "abXcd", "abcd".insert(-3, 'X') + assert_equal "abcdX", "abcd".insert(-1, 'X') + assert_raise(IndexError) { "abcd".insert(5, 'X') } + assert_raise(IndexError) { "abcd".insert(-6, 'X') } + + a = "abcd" + a.insert(0, 'X') + assert_equal "Xabcd", a +end + +assert('String#prepend') do + a = "world" + assert_equal "hello world", a.prepend("hello ") + assert_equal "hello world", a +end + +assert('String#ljust') do + assert_equal "hello", "hello".ljust(4) + assert_equal "hello ", "hello".ljust(20) + assert_equal 20, "hello".ljust(20).length + assert_equal "hello123412341234123", "hello".ljust(20, '1234') + assert_equal "hello", "hello".ljust(-3) +end + +assert('String#rjust') do + assert_equal "hello", "hello".rjust(4) + assert_equal " hello", "hello".rjust(20) + assert_equal 20, "hello".rjust(20).length + assert_equal "123412341234123hello", "hello".rjust(20, '1234') + assert_equal "hello", "hello".rjust(-3) +end + +if UTF8STRING + assert('String#ljust with UTF8') do + assert_equal "helloん ", "helloん".ljust(20) + assert_equal "helloó ", "helloó".ljust(34) + assert_equal 34, "helloó".ljust(34).length + assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん') + assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん') + end + + assert('String#rjust with UTF8') do + assert_equal " helloん", "helloん".rjust(20) + assert_equal " helloó", "helloó".rjust(34) + # assert_equal 34, "helloó".rjust(34).length + assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん') + assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん') + end + + assert('UTF8 byte counting') do + ret = ' ' + ret[-6..-1] = "helloó" + assert_equal 34, ret.length + end +end + +assert('String#ljust should not change string') do + a = "hello" + a.ljust(20) + assert_equal "hello", a +end + +assert('String#rjust should not change string') do + a = "hello" + a.rjust(20) + assert_equal "hello", a +end + +assert('String#ljust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".ljust(10, '') } +end + +assert('String#rjust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".rjust(10, '') } +end + +assert('String#upto') do + assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a + assert_equal ["9", "10", "11"], "9".upto("11").to_a + assert_equal [], "25".upto("5").to_a + assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a + +if UTF8STRING + assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a +end + + assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a + + a = "aa" + start = "aa" + count = 0 + assert_equal("aa", a.upto("zz") {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(676, count) + + a = "a" + start = "a" + count = 0 + assert_equal("a", a.upto("a") {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(1, count) + + a = "a" + start = "a" + count = 0 + assert_equal("a", a.upto("b", true) {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(1, count) + + a = "0" + start = "0" + count = 0 + assert_equal("0", a.upto("0") {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(1, count) + + a = "0" + start = "0" + count = 0 + assert_equal("0", a.upto("-1") {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(0, count) + + a = "-1" + start = "-1" + count = 0 + assert_equal("-1", a.upto("-2") {|s| + assert_equal(start, s) + start.succ! + count += 1 + }) + assert_equal(2, count) + + assert_raise(TypeError) { "a".upto(:c) {} } +end + +assert('String#ord') do + got = "hello!".split('').map {|x| x.ord} + expect = [104, 101, 108, 108, 111, 33] + unless UTF8STRING + got << "\xff".ord + expect << 0xff + end + assert_equal expect, got +end + +assert('String#ord(UTF-8)') do + got = "こんにちは世界!".split('').map {|x| x.ord} + expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21] + assert_equal expect, got +end if UTF8STRING + +assert('String#chr') do + assert_equal "h", "hello!".chr +end +assert('String#chr(UTF-8)') do + assert_equal "こ", "こんにちは世界!".chr +end if UTF8STRING + +assert('String#chars') do + expect = ["h", "e", "l", "l", "o", "!"] + assert_equal expect, "hello!".chars + s = "" + "hello!".chars do |x| + s += x + end + assert_equal "hello!", s +end + +assert('String#chars(UTF-8)') do + expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] + assert_equal expect, "こんにちは世界!".chars + s = "" + "こんにちは世界!".chars do |x| + s += x + end + assert_equal "こんにちは世界!", s +end if UTF8STRING + +assert('String#each_char') do + s = "" + "hello!".each_char do |x| + s += x + end + assert_equal "hello!", s +end + +assert('String#each_char(UTF-8)') do + s = "" + "こんにちは世界!".each_char do |x| + s += x + end + assert_equal "こんにちは世界!", s +end if UTF8STRING + +assert('String#codepoints') do + expect = [104, 101, 108, 108, 111, 33] + assert_equal expect, "hello!".codepoints + cp = [] + "hello!".codepoints do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#codepoints(UTF-8)') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + assert_equal expect, "こんにちは世界!".codepoints + cp = [] + "こんにちは世界!".codepoints do |x| + cp << x + end + assert_equal expect, cp +end if UTF8STRING + +assert('String#each_codepoint') do + expect = [104, 101, 108, 108, 111, 33] + cp = [] + "hello!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end + +assert('String#each_codepoint(UTF-8)') do + expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] + cp = [] + "こんにちは世界!".each_codepoint do |x| + cp << x + end + assert_equal expect, cp +end if UTF8STRING diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake new file mode 100644 index 00000000..2826ad2a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-struct') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard Struct class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb new file mode 100644 index 00000000..7cf3dd3a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb @@ -0,0 +1,103 @@ +## +# Struct +# +# ISO 15.2.18 + +if Object.const_defined?(:Struct) + class Struct + + ## + # Calls the given block for each element of +self+ + # and pass the respective element. + # + # ISO 15.2.18.4.4 + def each(&block) + self.class.members.each{|field| + block.call(self[field]) + } + self + end + + ## + # Calls the given block for each element of +self+ + # and pass the name and value of the respectiev + # element. + # + # ISO 15.2.18.4.5 + def each_pair(&block) + self.class.members.each{|field| + block.call(field.to_sym, self[field]) + } + self + end + + ## + # Calls the given block for each element of +self+ + # and returns an array with all elements of which + # block is not false. + # + # ISO 15.2.18.4.7 + def select(&block) + ary = [] + self.class.members.each{|field| + val = self[field] + ary.push(val) if block.call(val) + } + ary + end + + def _inspect + name = self.class.to_s + if name[0] == "#" + str = "#<struct " + else + str = "#<struct #{name} " + end + buf = [] + self.each_pair do |k,v| + buf.push [k.to_s + "=" + v._inspect] + end + str + buf.join(", ") + ">" + end + + ## + # call-seq: + # struct.to_s -> string + # struct.inspect -> string + # + # Describe the contents of this struct in a string. + # + # 15.2.18.4.10(x) + # + def inspect + begin + self._inspect + rescue SystemStackError + "#<struct #{self.class.to_s}:...>" + end + end + + ## + # 15.2.18.4.11(x) + # + alias to_s inspect + end + + ## + # call-seq: + # hsh.dig(key,...) -> object + # + # Extracts the nested value specified by the sequence of <i>key</i> + # objects by calling +dig+ at each step, returning +nil+ if any + # intermediate step is +nil+. + # + def dig(idx,*args) + n = self[idx] + if args.size > 0 + n&.dig(*args) + else + n + end + end +end + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c new file mode 100644 index 00000000..67762a94 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c @@ -0,0 +1,714 @@ +/* +** struct.c - Struct class +** +** See Copyright Notice in mruby.h +*/ + +#include <string.h> +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/string.h> +#include <mruby/class.h> +#include <mruby/variable.h> +#include <mruby/hash.h> +#include <mruby/range.h> + +#define RSTRUCT_LEN(st) RARRAY_LEN(st) +#define RSTRUCT_PTR(st) RARRAY_PTR(st) + +static struct RClass * +struct_class(mrb_state *mrb) +{ + return mrb_class_get(mrb, "Struct"); +} + +static inline mrb_value +struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id) +{ + struct RClass* kclass; + struct RClass* sclass = struct_class(mrb); + mrb_value ans; + + for (;;) { + ans = mrb_iv_get(mrb, c, id); + if (!mrb_nil_p(ans)) return ans; + kclass = RCLASS_SUPER(c); + if (kclass == 0 || kclass == sclass) + return mrb_nil_value(); + c = mrb_obj_value(kclass); + } +} + +static mrb_value +struct_s_members(mrb_state *mrb, struct RClass *klass) +{ + mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__")); + + if (mrb_nil_p(members)) { + mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct"); + } + if (!mrb_array_p(members)) { + mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); + } + return members; +} + +static mrb_value +struct_members(mrb_state *mrb, mrb_value s) +{ + mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s)); + if (!mrb_array_p(s)) { + mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); + } + if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) { + if (RSTRUCT_LEN(s) == 0) { /* probably uninitialized */ + mrb_ary_resize(mrb, s, RARRAY_LEN(members)); + } + else { + mrb_raisef(mrb, E_TYPE_ERROR, + "struct size differs (%S required %S given)", + mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s))); + } + } + return members; +} + +static mrb_value +mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) +{ + mrb_value members, ary; + + members = struct_s_members(mrb, mrb_class_ptr(klass)); + ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members)); + mrb_ary_replace(mrb, ary, members); + return ary; +} + +static void +mrb_struct_modify(mrb_state *mrb, mrb_value strct) +{ + if (MRB_FROZEN_P(mrb_basic_ptr(strct))) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct"); + } + + mrb_write_barrier(mrb, mrb_basic_ptr(strct)); +} + +/* 15.2.18.4.6 */ +/* + * call-seq: + * struct.members -> array + * + * Returns an array of strings representing the names of the instance + * variables. + * + * Customer = Struct.new(:name, :address, :zip) + * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + * joe.members #=> [:name, :address, :zip] + */ + +static mrb_value +mrb_struct_members(mrb_state *mrb, mrb_value obj) +{ + return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); +} + +static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id); + +static mrb_value +mrb_struct_ref(mrb_state *mrb, mrb_value obj) +{ + return struct_aref_sym(mrb, obj, mrb->c->ci->mid); +} + +static mrb_sym +mrb_id_attrset(mrb_state *mrb, mrb_sym id) +{ + const char *name; + char *buf; + mrb_int len; + mrb_sym mid; + + name = mrb_sym2name_len(mrb, id, &len); + buf = (char *)mrb_malloc(mrb, (size_t)len+2); + memcpy(buf, name, (size_t)len); + buf[len] = '='; + buf[len+1] = '\0'; + + mid = mrb_intern(mrb, buf, len+1); + mrb_free(mrb, buf); + return mid; +} + +static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val); + +static mrb_value +mrb_struct_set_m(mrb_state *mrb, mrb_value obj) +{ + mrb_value val; + + const char *name; + mrb_int slen; + mrb_sym mid; + + mrb_get_args(mrb, "o", &val); + + /* get base id */ + name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen); + mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */ + + return mrb_struct_aset_sym(mrb, obj, mid, val); +} + +static mrb_bool +is_local_id(mrb_state *mrb, const char *name) +{ + if (!name) return FALSE; + return !ISUPPER(name[0]); +} + +static mrb_bool +is_const_id(mrb_state *mrb, const char *name) +{ + if (!name) return FALSE; + return ISUPPER(name[0]); +} + +static void +make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c) +{ + const mrb_value *ptr_members = RARRAY_PTR(members); + mrb_int i; + mrb_int len = RARRAY_LEN(members); + int ai = mrb_gc_arena_save(mrb); + + for (i=0; i<len; i++) { + mrb_sym id = mrb_symbol(ptr_members[i]); + const char *name = mrb_sym2name_len(mrb, id, NULL); + + if (is_local_id(mrb, name) || is_const_id(mrb, name)) { + mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1)); + mrb_gc_arena_restore(mrb, ai); + } + } +} + +static mrb_value +make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass) +{ + mrb_value nstr; + mrb_sym id; + struct RClass *c; + + if (mrb_nil_p(name)) { + c = mrb_class_new(mrb, klass); + } + else { + /* old style: should we warn? */ + name = mrb_str_to_str(mrb, name); + id = mrb_obj_to_sym(mrb, name); + if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) { + mrb_name_error(mrb, id, "identifier %S needs to be constant", name); + } + if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) { + mrb_warn(mrb, "redefining constant Struct::%S", name); + mrb_const_remove(mrb, mrb_obj_value(klass), id); + } + c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass); + } + MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY); + nstr = mrb_obj_value(c); + mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members); + + mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE()); + /* RSTRUCT(nstr)->basic.c->super = c->c; */ + make_struct_define_accessors(mrb, members, c); + return nstr; +} + +/* 15.2.18.3.1 */ +/* + * call-seq: + * Struct.new( [aString] [, aSym]+> ) -> StructClass + * StructClass.new(arg, ...) -> obj + * StructClass[arg, ...] -> obj + * + * Creates a new class, named by <i>aString</i>, containing accessor + * methods for the given symbols. If the name <i>aString</i> is + * omitted, an anonymous structure class will be created. Otherwise, + * the name of this struct will appear as a constant in class + * <code>Struct</code>, so it must be unique for all + * <code>Struct</code>s in the system and should start with a capital + * letter. Assigning a structure class to a constant effectively gives + * the class the name of the constant. + * + * <code>Struct::new</code> returns a new <code>Class</code> object, + * which can then be used to create specific instances of the new + * structure. The number of actual parameters must be + * less than or equal to the number of attributes defined for this + * class; unset parameters default to <code>nil</code>. Passing too many + * parameters will raise an <code>ArgumentError</code>. + * + * The remaining methods listed in this section (class and instance) + * are defined for this generated class. + * + * # Create a structure with a name in Struct + * Struct.new("Customer", :name, :address) #=> Struct::Customer + * Struct::Customer.new("Dave", "123 Main") #=> #<struct Struct::Customer name="Dave", address="123 Main"> + * + * # Create a structure named by its constant + * Customer = Struct.new(:name, :address) #=> Customer + * Customer.new("Dave", "123 Main") #=> #<struct Customer name="Dave", address="123 Main"> + */ +static mrb_value +mrb_struct_s_def(mrb_state *mrb, mrb_value klass) +{ + mrb_value name, rest; + mrb_value *pargv; + mrb_int argcnt; + mrb_int i; + mrb_value b, st; + mrb_sym id; + mrb_value *argv; + mrb_int argc; + + name = mrb_nil_value(); + mrb_get_args(mrb, "*&", &argv, &argc, &b); + if (argc == 0) { /* special case to avoid crash */ + rest = mrb_ary_new(mrb); + } + else { + if (argc > 0) name = argv[0]; + pargv = &argv[1]; + argcnt = argc-1; + if (!mrb_nil_p(name) && mrb_symbol_p(name)) { + /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ + name = mrb_nil_value(); + pargv = &argv[0]; + argcnt++; + } + rest = mrb_ary_new_from_values(mrb, argcnt, pargv); + for (i=0; i<RARRAY_LEN(rest); i++) { + id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]); + mrb_ary_set(mrb, rest, i, mrb_symbol_value(id)); + } + } + st = make_struct(mrb, name, rest, mrb_class_ptr(klass)); + if (!mrb_nil_p(b)) { + mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st)); + } + + return st; +} + +static mrb_int +num_members(mrb_state *mrb, struct RClass *klass) +{ + mrb_value members; + + members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__")); + if (!mrb_array_p(members)) { + mrb_raise(mrb, E_TYPE_ERROR, "broken members"); + } + return RARRAY_LEN(members); +} + +/* 15.2.18.4.8 */ +/* + */ +static mrb_value +mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self) +{ + struct RClass *klass = mrb_obj_class(mrb, self); + mrb_int i, n; + + n = num_members(mrb, klass); + if (n < argc) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs"); + } + + for (i = 0; i < argc; i++) { + mrb_ary_set(mrb, self, i, argv[i]); + } + for (i = argc; i < n; i++) { + mrb_ary_set(mrb, self, i, mrb_nil_value()); + } + return self; +} + +static mrb_value +mrb_struct_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_value *argv; + mrb_int argc; + + mrb_get_args(mrb, "*!", &argv, &argc); + return mrb_struct_initialize_withArg(mrb, argc, argv, self); +} + +/* 15.2.18.4.9 */ +/* :nodoc: */ +static mrb_value +mrb_struct_init_copy(mrb_state *mrb, mrb_value copy) +{ + mrb_value s; + + mrb_get_args(mrb, "o", &s); + + if (mrb_obj_equal(mrb, copy, s)) return copy; + if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) { + mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); + } + if (!mrb_array_p(s)) { + mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); + } + mrb_ary_replace(mrb, copy, s); + return copy; +} + +static mrb_value +struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id) +{ + mrb_value members, *ptr; + const mrb_value *ptr_members; + mrb_int i, len; + + members = struct_members(mrb, obj); + ptr_members = RARRAY_PTR(members); + len = RARRAY_LEN(members); + ptr = RSTRUCT_PTR(obj); + for (i=0; i<len; i++) { + mrb_value slot = ptr_members[i]; + if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) { + return ptr[i]; + } + } + mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id)); + return mrb_nil_value(); /* not reached */ +} + +static mrb_value +struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i) +{ + if (i < 0) i = RSTRUCT_LEN(s) + i; + if (i < 0) + mrb_raisef(mrb, E_INDEX_ERROR, + "offset %S too small for struct(size:%S)", + mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); + if (RSTRUCT_LEN(s) <= i) + mrb_raisef(mrb, E_INDEX_ERROR, + "offset %S too large for struct(size:%S)", + mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); + return RSTRUCT_PTR(s)[i]; +} + +/* 15.2.18.4.2 */ +/* + * call-seq: + * struct[symbol] -> anObject + * struct[fixnum] -> anObject + * + * Attribute Reference---Returns the value of the instance variable + * named by <i>symbol</i>, or indexed (0..length-1) by + * <i>fixnum</i>. Will raise <code>NameError</code> if the named + * variable does not exist, or <code>IndexError</code> if the index is + * out of range. + * + * Customer = Struct.new(:name, :address, :zip) + * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + * + * joe["name"] #=> "Joe Smith" + * joe[:name] #=> "Joe Smith" + * joe[0] #=> "Joe Smith" + */ +static mrb_value +mrb_struct_aref(mrb_state *mrb, mrb_value s) +{ + mrb_value idx; + + mrb_get_args(mrb, "o", &idx); + if (mrb_string_p(idx)) { + mrb_value sym = mrb_check_intern_str(mrb, idx); + + if (mrb_nil_p(sym)) { + mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx); + } + idx = sym; + } + if (mrb_symbol_p(idx)) { + return struct_aref_sym(mrb, s, mrb_symbol(idx)); + } + return struct_aref_int(mrb, s, mrb_int(mrb, idx)); +} + +static mrb_value +mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) +{ + mrb_value members, *ptr; + const mrb_value *ptr_members; + mrb_int i, len; + + members = struct_members(mrb, s); + len = RARRAY_LEN(members); + ptr = RSTRUCT_PTR(s); + ptr_members = RARRAY_PTR(members); + for (i=0; i<len; i++) { + if (mrb_symbol(ptr_members[i]) == id) { + mrb_struct_modify(mrb, s); + ptr[i] = val; + return val; + } + } + mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id)); + return val; /* not reach */ +} + +/* 15.2.18.4.3 */ +/* + * call-seq: + * struct[symbol] = obj -> obj + * struct[fixnum] = obj -> obj + * + * Attribute Assignment---Assigns to the instance variable named by + * <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and + * returns it. Will raise a <code>NameError</code> if the named + * variable does not exist, or an <code>IndexError</code> if the index + * is out of range. + * + * Customer = Struct.new(:name, :address, :zip) + * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + * + * joe["name"] = "Luke" + * joe[:zip] = "90210" + * + * joe.name #=> "Luke" + * joe.zip #=> "90210" + */ + +static mrb_value +mrb_struct_aset(mrb_state *mrb, mrb_value s) +{ + mrb_int i; + mrb_value idx; + mrb_value val; + + mrb_get_args(mrb, "oo", &idx, &val); + + if (mrb_string_p(idx)) { + mrb_value sym = mrb_check_intern_str(mrb, idx); + + if (mrb_nil_p(sym)) { + mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx); + } + idx = sym; + } + if (mrb_symbol_p(idx)) { + return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val); + } + + i = mrb_int(mrb, idx); + if (i < 0) i = RSTRUCT_LEN(s) + i; + if (i < 0) { + mrb_raisef(mrb, E_INDEX_ERROR, + "offset %S too small for struct(size:%S)", + mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); + } + if (RSTRUCT_LEN(s) <= i) { + mrb_raisef(mrb, E_INDEX_ERROR, + "offset %S too large for struct(size:%S)", + mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); + } + mrb_struct_modify(mrb, s); + return RSTRUCT_PTR(s)[i] = val; +} + +/* 15.2.18.4.1 */ +/* + * call-seq: + * struct == other_struct -> true or false + * + * Equality---Returns <code>true</code> if <i>other_struct</i> is + * equal to this one: they must be of the same class as generated by + * <code>Struct::new</code>, and the values of all instance variables + * must be equal (according to <code>Object#==</code>). + * + * Customer = Struct.new(:name, :address, :zip) + * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345) + * joe == joejr #=> true + * joe == jane #=> false + */ + +static mrb_value +mrb_struct_equal(mrb_state *mrb, mrb_value s) +{ + mrb_value s2; + mrb_value *ptr, *ptr2; + mrb_int i, len; + + mrb_get_args(mrb, "o", &s2); + if (mrb_obj_equal(mrb, s, s2)) { + return mrb_true_value(); + } + if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { + return mrb_false_value(); + } + if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { + mrb_bug(mrb, "inconsistent struct"); /* should never happen */ + } + ptr = RSTRUCT_PTR(s); + ptr2 = RSTRUCT_PTR(s2); + len = RSTRUCT_LEN(s); + for (i=0; i<len; i++) { + if (!mrb_equal(mrb, ptr[i], ptr2[i])) { + return mrb_false_value(); + } + } + + return mrb_true_value(); +} + +/* 15.2.18.4.12(x) */ +/* + * code-seq: + * struct.eql?(other) -> true or false + * + * Two structures are equal if they are the same object, or if all their + * fields are equal (using <code>eql?</code>). + */ +static mrb_value +mrb_struct_eql(mrb_state *mrb, mrb_value s) +{ + mrb_value s2; + mrb_value *ptr, *ptr2; + mrb_int i, len; + + mrb_get_args(mrb, "o", &s2); + if (mrb_obj_equal(mrb, s, s2)) { + return mrb_true_value(); + } + if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { + return mrb_false_value(); + } + if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { + mrb_bug(mrb, "inconsistent struct"); /* should never happen */ + } + ptr = RSTRUCT_PTR(s); + ptr2 = RSTRUCT_PTR(s2); + len = RSTRUCT_LEN(s); + for (i=0; i<len; i++) { + if (!mrb_eql(mrb, ptr[i], ptr2[i])) { + return mrb_false_value(); + } + } + + return mrb_true_value(); +} + +/* + * call-seq: + * struct.length -> Fixnum + * struct.size -> Fixnum + * + * Returns number of struct members. + */ +static mrb_value +mrb_struct_len(mrb_state *mrb, mrb_value self) +{ + return mrb_fixnum_value(RSTRUCT_LEN(self)); +} + +/* + * call-seq: + * struct.to_a -> array + * struct.values -> array + * + * Create an array from struct values. + */ +static mrb_value +mrb_struct_to_a(mrb_state *mrb, mrb_value self) +{ + return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self)); +} + +/* + * call-seq: + * struct.to_h -> hash + * + * Create a hash from member names and struct values. + */ +static mrb_value +mrb_struct_to_h(mrb_state *mrb, mrb_value self) +{ + mrb_value members, ret; + mrb_int i; + + members = struct_members(mrb, self); + ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); + + for (i = 0; i < RARRAY_LEN(members); ++i) { + mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]); + } + + return ret; +} + +static mrb_value +mrb_struct_values_at(mrb_state *mrb, mrb_value self) +{ + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + + return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int); +} + +/* + * A <code>Struct</code> is a convenient way to bundle a number of + * attributes together, using accessor methods, without having to write + * an explicit class. + * + * The <code>Struct</code> class is a generator of specific classes, + * each one of which is defined to hold a set of variables and their + * accessors. In these examples, we'll call the generated class + * "<i>Customer</i>Class," and we'll show an example instance of that + * class as "<i>Customer</i>Inst." + * + * In the descriptions that follow, the parameter <i>symbol</i> refers + * to a symbol, which is either a quoted string or a + * <code>Symbol</code> (such as <code>:name</code>). + */ +void +mrb_mruby_struct_gem_init(mrb_state* mrb) +{ + struct RClass *st; + st = mrb_define_class(mrb, "Struct", mrb->object_class); + MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY); + + mrb_define_class_method(mrb, st, "new", mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */ + + mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */ + mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */ + mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */ + mrb_define_method(mrb, st, "members", mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ + mrb_define_method(mrb, st, "initialize", mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */ + mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */ + mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */ + + mrb_define_method(mrb, st, "size", mrb_struct_len, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "length", mrb_struct_len, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_ANY()); +} + +void +mrb_mruby_struct_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb new file mode 100644 index 00000000..421fe4b5 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb @@ -0,0 +1,212 @@ +## +# Struct ISO Test + +assert('Struct', '15.2.18') do + assert_equal Class, Struct.class +end + +assert('Struct.new', '15.2.18.3.1') do + c = Struct.new(:m1, :m2) + assert_equal Struct, c.superclass + assert_equal [:m1, :m2], c.members +end + +# Check crash bug with Struc.new and no params. +assert('Struct.new', '15.2.18.3.1') do + c = Struct.new() + assert_equal Struct, c.superclass + assert_equal [], c.members +end + +assert('Struct#==', '15.2.18.4.1') do + c = Struct.new(:m1, :m2) + cc1 = c.new(1,2) + cc2 = c.new(1,2) + assert_true cc1 == cc2 + + Struct.new(:m1, :m2) { def foo; end } + assert_raise(NoMethodError) { Struct.new(:m1).new.foo } +end + +assert('Struct#[]', '15.2.18.4.2') do + c = Struct.new(:m1, :m2) + cc = c.new(1,2) + assert_equal 1, cc[:m1] + assert_equal 2, cc["m2"] + assert_equal 1, cc[0] + assert_equal 2, cc[-1] + assert_raise(TypeError) { cc[[]] } + assert_raise(IndexError) { cc[2] } + assert_raise(NameError) { cc['tama'] } +end + +assert('Struct#[]=', '15.2.18.4.3') do + c = Struct.new(:m1, :m2) + cc = c.new(1,2) + cc[:m1] = 3 + assert_equal 3, cc[:m1] + cc["m2"] = 3 + assert_equal 3, cc["m2"] + cc[0] = 4 + assert_equal 4, cc[0] + cc[-1] = 5 + assert_equal 5, cc[-1] + assert_raise(TypeError) { cc[[]] = 3 } + assert_raise(IndexError) { cc[2] = 7 } + assert_raise(NameError) { cc['pochi'] = 8 } +end + +assert('Struct#each', '15.2.18.4.4') do + c = Struct.new(:m1, :m2) + cc = c.new(1,2) + a = [] + cc.each{|x| + a << x + } + assert_equal [1, 2], a +end + +assert('Struct#each_pair', '15.2.18.4.5') do + c = Struct.new(:m1, :m2) + cc = c.new(1,2) + a = [] + cc.each_pair{|k,v| + a << [k,v] + } + assert_equal [[:m1, 1], [:m2, 2]], a +end + +assert('Struct#members', '15.2.18.4.6') do + c = Struct.new(:m1, :m2) + assert_equal [:m1, :m2], c.new(1,2).members +end + +assert('Struct#select', '15.2.18.4.7') do + c = Struct.new(:m1, :m2) + assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} } +end + +assert('large struct') do + c = Struct.new(:m1, :m2, :m3, :m4, :m5, :m6, :m7, :m8, :m9, :m10, :m11, :m12, :m13) + cc = c.new(1,2,3,4,5,6,7,8,9,10,11,12,13) + assert_equal 1, cc.m1 + assert_equal 2, cc.m2 + assert_equal 3, cc.m3 + assert_equal 4, cc.m4 + assert_equal 5, cc.m5 + assert_equal 6, cc.m6 + assert_equal 7, cc.m7 + assert_equal 8, cc.m8 + assert_equal 9, cc.m9 + assert_equal 10, cc.m10 + assert_equal 13, cc.m13 + + cc.m13 = 'test' + assert_equal 'test', cc.m13 + + assert_raise(NoMethodError) { cc.m14 } +end + +assert('wrong struct arg count') do + c = Struct.new(:m1) + assert_raise ArgumentError do + cc = c.new(1,2,3) + end +end + +assert('struct dup') do + c = Struct.new(:m1, :m2, :m3, :m4, :m5) + cc = c.new(1,2,3,4,5) + assert_nothing_raised { + assert_equal(cc, cc.dup) + } +end + +assert('struct inspect') do + c = Struct.new(:m1, :m2, :m3, :m4, :m5) + cc = c.new(1,2,3,4,5) + assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect +end + +assert('Struct#length, Struct#size') do + s = Struct.new(:f1, :f2).new(0, 1) + assert_equal 2, s.size + assert_equal 2, s.length +end + +assert('Struct#to_a, Struct#values') do + s = Struct.new(:mem1, :mem2).new('a', 'b') + assert_equal ['a', 'b'], s.to_a + assert_equal ['a', 'b'], s.values +end + +assert('Struct#to_h') do + s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe') + assert_equal(:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe') { s.to_h } +end + +assert('Struct#values_at') do + a = Struct.new(:blue, :purple).new('aki', 'io') + assert_equal ['aki'], a.values_at(0) + assert_equal ['io', 'aki'], a.values_at(1, 0) + assert_raise(IndexError) { a.values_at 2 } +end + +assert("Struct#dig") do + a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1)) + assert_equal 'aki', a.dig(:blue) + assert_equal 1, a.dig(:purple, :red) + assert_equal 1, a.dig(1, 0) +end + +assert("Struct.new removes existing constant") do + skip "redefining Struct with same name cause warnings" + begin + assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b) + ensure + Struct.remove_const :Test + end +end + +assert("Struct#initialize_copy requires struct to be the same type") do + begin + Struct.new("Test", :a) + a = Struct::Test.new("a") + Struct.remove_const :Test + Struct.new("Test", :a, :b) + assert_raise(TypeError) do + a.initialize_copy(Struct::Test.new("a", "b")) + end + ensure + Struct.remove_const :Test + end +end + +assert("Struct.new does not allow array") do + assert_raise(TypeError) do + Struct.new("Test", [:a]) + end +end + +assert("Struct.new generates subclass of Struct") do + begin + original_struct = Struct + Struct = String + assert_equal original_struct, original_struct.new.superclass + ensure + Struct = original_struct + end +end + +assert 'Struct#freeze' do + c = Struct.new :m + + o = c.new + o.m = :test + assert_equal :test, o.m + + o.freeze + assert_raise(RuntimeError) { o.m = :modify } + assert_raise(RuntimeError) { o[:m] = :modify } + assert_equal :test, o.m +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake new file mode 100644 index 00000000..4f3fa43b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Symbol class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb new file mode 100644 index 00000000..1e3d24b8 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb @@ -0,0 +1,65 @@ +class Symbol + include Comparable + + alias intern to_sym + + def to_proc + ->(obj,*args,&block) do + obj.__send__(self, *args, &block) + end + end + + ## + # call-seq: + # sym.capitalize -> symbol + # + # Same as <code>sym.to_s.capitalize.intern</code>. + + def capitalize + (self.to_s.capitalize! || self).to_sym + end + + ## + # call-seq: + # sym.downcase -> symbol + # + # Same as <code>sym.to_s.downcase.intern</code>. + + def downcase + (self.to_s.downcase! || self).to_sym + end + + ## + # call-seq: + # sym.upcase -> symbol + # + # Same as <code>sym.to_s.upcase.intern</code>. + + def upcase + (self.to_s.upcase! || self).to_sym + end + + ## + # call-seq: + # sym.casecmp(other) -> -1, 0, +1 or nil + # + # Case-insensitive version of <code>Symbol#<=></code>. + + def casecmp(other) + return nil unless other.kind_of?(Symbol) + lhs = self.to_s; lhs.upcase! + rhs = other.to_s; rhs.upcase! + lhs <=> rhs + end + + # + # call-seq: + # sym.empty? -> true or false + # + # Returns that _sym_ is :"" or not. + + def empty? + self.length == 0 + end + +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c new file mode 100644 index 00000000..a992dbfc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c @@ -0,0 +1,64 @@ +#include <mruby.h> +#include <mruby/khash.h> +#include <mruby/array.h> + +typedef struct symbol_name { + size_t len; + const char *name; +} symbol_name; + +/* + * call-seq: + * Symbol.all_symbols => array + * + * Returns an array of all the symbols currently in Ruby's symbol + * table. + * + * Symbol.all_symbols.size #=> 903 + * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink, + * :chown, :EOFError, :$;, :String, + * :LOCK_SH, :"setuid?", :$<, + * :default_proc, :compact, :extend, + * :Tms, :getwd, :$=, :ThreadGroup, + * :wait2, :$>] + */ +static mrb_value +mrb_sym_all_symbols(mrb_state *mrb, mrb_value self) +{ + mrb_sym i, lim; + mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx); + + for (i=1, lim=mrb->symidx+1; i<lim; i++) { + mrb_ary_push(mrb, ary, mrb_symbol_value(i)); + } + + return ary; +} + +/* + * call-seq: + * sym.length -> integer + * + * Same as <code>sym.to_s.length</code>. + */ +static mrb_value +mrb_sym_length(mrb_state *mrb, mrb_value self) +{ + mrb_int len; + mrb_sym2name_len(mrb, mrb_symbol(self), &len); + return mrb_fixnum_value(len); +} + +void +mrb_mruby_symbol_ext_gem_init(mrb_state* mrb) +{ + struct RClass *s = mrb->symbol_class; + mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE()); +} + +void +mrb_mruby_symbol_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb new file mode 100644 index 00000000..6070d141 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb @@ -0,0 +1,48 @@ +## +# Symbol(Ext) Test + +assert('Symbol#to_proc') do + assert_equal 5, :abs.to_proc[-5] +end + +assert('Symbol.all_symbols') do + foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort + symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort + assert_equal foo, symbols +end + +assert("Symbol#length") do + assert_equal 5, :hello.size + assert_equal 5, :mruby.length +end + +assert("Symbol#capitalize") do + assert_equal :Hello, :hello.capitalize + assert_equal :Hello, :HELLO.capitalize + assert_equal :Hello, :Hello.capitalize +end + +assert("Symbol#downcase") do + assert_equal :hello, :hEllO.downcase + assert_equal :hello, :hello.downcase +end + +assert("Symbol#upcase") do + assert_equal :HELLO, :hEllO.upcase + assert_equal :HELLO, :HELLO.upcase +end + +assert("Symbol#casecmp") do + assert_equal 0, :HELLO.casecmp(:hEllO) + assert_equal 1, :HELLO.casecmp(:hEllN) + assert_equal(-1, :HELLO.casecmp(:hEllP)) + assert_nil :HELLO.casecmp("hEllO") +end + +assert("Symbol#empty?") do + assert_true :''.empty? +end + +assert('Symbol#intern') do + assert_equal :test, :test.intern +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md new file mode 100644 index 00000000..fa4b91e3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md @@ -0,0 +1,7 @@ +Running Tests +============= + +To run the tests, execute the following from the project's root directory. + + $ make test + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c new file mode 100644 index 00000000..14e93ff3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c @@ -0,0 +1,172 @@ +/* +** mrbtest - Test for Embeddable Ruby +** +** This program runs Ruby test programs in test/t directory +** against the current mruby implementation. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <mruby.h> +#include <mruby/proc.h> +#include <mruby/data.h> +#include <mruby/compile.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/array.h> + +void +mrb_init_mrbtest(mrb_state *); + +/* Print a short remark for the user */ +static void +print_hint(void) +{ + printf("mrbtest - Embeddable Ruby Test\n\n"); +} + +static int +check_error(mrb_state *mrb) +{ + /* Error check */ + /* $ko_test and $kill_test should be 0 */ + mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test")); + mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test")); + + return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0; +} + +static int +eval_test(mrb_state *mrb) +{ + /* evaluate the test */ + mrb_funcall(mrb, mrb_top_self(mrb), "report", 0); + /* did an exception occur? */ + if (mrb->exc) { + mrb_print_error(mrb); + mrb->exc = 0; + return EXIT_FAILURE; + } + else if (!check_error(mrb)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static void +t_printstr(mrb_state *mrb, mrb_value obj) +{ + char *s; + int len; + + if (mrb_string_p(obj)) { + s = RSTRING_PTR(obj); + len = RSTRING_LEN(obj); + fwrite(s, len, 1, stdout); + } +} + +mrb_value +mrb_t_printstr(mrb_state *mrb, mrb_value self) +{ + mrb_value argv; + + mrb_get_args(mrb, "o", &argv); + t_printstr(mrb, argv); + + return argv; +} + +void +mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose) +{ + struct RClass *krn, *mrbtest; + + krn = mrb->kernel_module; + mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1)); + + mrbtest = mrb_define_module(mrb, "Mrbtest"); + + mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX)); + mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN)); + mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT)); + +#ifdef MRB_USE_FLOAT + mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6)); +#else + mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12)); +#endif + + if (verbose) { + mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); + } +} + +void +mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src) +{ + mrb_value res_src; + + if (mrb_src->exc) { + mrb_print_error(mrb_src); + exit(EXIT_FAILURE); + } + +#define TEST_COUNT_PASS(name) \ + do { \ + res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \ + if (mrb_fixnum_p(res_src)) { \ + mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \ + mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \ + } \ + } while (FALSE) \ + + TEST_COUNT_PASS(ok_test); + TEST_COUNT_PASS(ko_test); + TEST_COUNT_PASS(kill_test); + +#undef TEST_COUNT_PASS + + res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts")); + + if (mrb_array_p(res_src)) { + mrb_int i; + mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts")); + for (i = 0; i < RARRAY_LEN(res_src); ++i) { + mrb_value val_src = RARRAY_PTR(res_src)[i]; + mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src))); + } + } +} + +int +main(int argc, char **argv) +{ + mrb_state *mrb; + int ret; + mrb_bool verbose = FALSE; + + print_hint(); + + /* new interpreter instance */ + mrb = mrb_open(); + if (mrb == NULL) { + fprintf(stderr, "Invalid mrb_state, exiting test driver"); + return EXIT_FAILURE; + } + + if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') { + printf("verbose mode: enable\n\n"); + verbose = TRUE; + } + + mrb_init_test_driver(mrb, verbose); + mrb_init_mrbtest(mrb); + ret = eval_test(mrb); + mrb_close(mrb); + + return ret; +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c new file mode 100644 index 00000000..17ac1bde --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c @@ -0,0 +1,38 @@ +#include <stdlib.h> +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/variable.h> + +extern const uint8_t mrbtest_assert_irep[]; + +void mrbgemtest_init(mrb_state* mrb); +void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose); +void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src); + +void +mrb_init_mrbtest(mrb_state *mrb) +{ + mrb_state *core_test; + + mrb_load_irep(mrb, mrbtest_assert_irep); + + core_test = mrb_open_core(mrb_default_allocf, NULL); + if (core_test == NULL) { + fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__); + exit(EXIT_FAILURE); + } + mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose")))); + mrb_load_irep(core_test, mrbtest_assert_irep); + mrb_t_pass_result(mrb, core_test); + +#ifndef DISABLE_GEMS + mrbgemtest_init(mrb); +#endif + + if (mrb->exc) { + mrb_print_error(mrb); + exit(EXIT_FAILURE); + } + mrb_close(core_test); +} + diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake new file mode 100644 index 00000000..ae4c2f13 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake @@ -0,0 +1,187 @@ +MRuby::Gem::Specification.new('mruby-test') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby test' + + build.bins << 'mrbtest' + spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') + + spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb") + + clib = "#{build_dir}/mrbtest.c" + mlib = clib.ext(exts.object) + exec = exefile("#{build.build_dir}/bin/mrbtest") + + libmruby = libfile("#{build.build_dir}/lib/libmruby") + libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core") + + mrbtest_lib = libfile("#{build_dir}/mrbtest") + mrbtest_objs = [] + + driver_obj = objfile("#{build_dir}/driver") + driver = "#{spec.dir}/driver.c" + + assert_c = "#{build_dir}/assert.c" + assert_rb = "#{MRUBY_ROOT}/test/assert.rb" + assert_lib = assert_c.ext(exts.object) + mrbtest_objs << assert_lib + + file assert_lib => assert_c + file assert_c => assert_rb do |t| + open(t.name, 'w') do |f| + mrbc.run f, assert_rb, 'mrbtest_assert_irep' + end + end + + gem_table = build.gems.generate_gem_table build + + build.gems.each do |g| + test_rbobj = g.test_rbireps.ext(exts.object) + g.test_objs << test_rbobj + dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions) + + file test_rbobj => g.test_rbireps + file g.test_rbireps => [g.test_rbfiles].flatten do |t| + FileUtils.mkdir_p File.dirname(t.name) + open(t.name, 'w') do |f| + g.print_gem_test_header(f) + test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| + File.expand_path(g.test_preload, dir) + }.find {|file| File.exist?(file) } + + f.puts %Q[/*] + f.puts %Q[ * This file contains a test code for #{g.name} gem.] + f.puts %Q[ *] + f.puts %Q[ * IMPORTANT:] + f.puts %Q[ * This file was generated!] + f.puts %Q[ * All manual changes will get lost.] + f.puts %Q[ */] + if test_preload.nil? + f.puts %Q[extern const uint8_t mrbtest_assert_irep[];] + else + g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload" + end + g.test_rbfiles.flatten.each_with_index do |rbfile, i| + g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}" + end + f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty? + dep_list.each do |d| + f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);] + f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);] + end + f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);] + f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);] + f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {] + unless g.test_rbfiles.empty? + f.puts %Q[ mrb_state *mrb2;] + unless g.test_args.empty? + f.puts %Q[ mrb_value test_args_hash;] + end + f.puts %Q[ int ai;] + g.test_rbfiles.count.times do |i| + f.puts %Q[ ai = mrb_gc_arena_save(mrb);] + f.puts %Q[ mrb2 = mrb_open_core(mrb_default_allocf, NULL);] + f.puts %Q[ if (mrb2 == NULL) {] + f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __FUNCTION__);] + f.puts %Q[ exit(EXIT_FAILURE);] + f.puts %Q[ }] + dep_list.each do |d| + f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);] + f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);] + end + f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));] + if test_preload.nil? + f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);] + else + f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);] + end + f.puts %Q[ if (mrb2->exc) {] + f.puts %Q[ mrb_print_error(mrb2);] + f.puts %Q[ exit(EXIT_FAILURE);] + f.puts %Q[ }] + f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));] + + unless g.test_args.empty? + f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ] + g.test_args.each do |arg_name, arg_value| + escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"') + escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"') + f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ] + end + f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ] + end + + f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init? + + f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});] + f.puts %Q[ ] + + f.puts %Q[ mrb_t_pass_result(mrb, mrb2);] + f.puts %Q[ mrb_close(mrb2);] + f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] + end + end + f.puts %Q[}] + end + end + end + + build.gems.each do |v| + mrbtest_objs.concat v.test_objs + end + + file mrbtest_lib => mrbtest_objs do |t| + build.archiver.run t.name, t.prerequisites + end + + unless build.build_mrbtest_lib_only? + file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t| + gem_flags = build.gems.map { |g| g.linker.flags } + gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries } + gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries } + gem_libraries = build.gems.map { |g| g.linker.libraries } + gem_library_paths = build.gems.map { |g| g.linker.library_paths } + build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries + end + end + + init = "#{spec.dir}/init_mrbtest.c" + + # store the last gem selection and make the re-build + # of the test gem depending on a change to the gem + # selection + active_gems = "#{build_dir}/active_gems.lst" + FileUtils.mkdir_p File.dirname(active_gems) + open(active_gems, 'w+') do |f| + build.gems.each do |g| + f.puts g.name + end + end + file clib => active_gems + + file mlib => clib + file clib => init do |t| + _pp "GEN", "*.rb", "#{clib.relative_path}" + FileUtils.mkdir_p File.dirname(clib) + open(clib, 'w') do |f| + f.puts %Q[/*] + f.puts %Q[ * This file contains a list of all] + f.puts %Q[ * test functions.] + f.puts %Q[ *] + f.puts %Q[ * IMPORTANT:] + f.puts %Q[ * This file was generated!] + f.puts %Q[ * All manual changes will get lost.] + f.puts %Q[ */] + f.puts %Q[] + f.puts IO.read(init) + build.gems.each do |g| + f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] + end + f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] + build.gems.each do |g| + f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] + end + f.puts %Q[}] + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake new file mode 100644 index 00000000..45b2ead7 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-time') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'standard Time class' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb new file mode 100644 index 00000000..df0d8ca8 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb @@ -0,0 +1,9 @@ +class Time + def sunday?; wday == 0 end + def monday?; wday == 1 end + def tuesday?; wday == 2 end + def wednesday?; wday == 3 end + def thursday?; wday == 4 end + def friday?; wday == 5 end + def saturday?; wday == 6 end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c new file mode 100644 index 00000000..5e862483 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c @@ -0,0 +1,869 @@ +/* +** time.c - Time class +** +** See Copyright Notice in mruby.h +*/ + +#include <math.h> +#include <time.h> +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/data.h> + +#ifndef DISABLE_STDIO +#include <stdio.h> +#else +#include <string.h> +#endif + +#define NDIV(x,y) (-(-((x)+1)/(y))-1) + +#if defined(_MSC_VER) && _MSC_VER < 1800 +double round(double x) { + return floor(x + 0.5); +} +#endif + +#if !defined(__MINGW64__) && defined(_WIN32) +# define llround(x) round(x) +#endif + +#if defined(__MINGW64__) || defined(__MINGW32__) +# include <sys/time.h> +#endif + +/** Time class configuration */ + +/* gettimeofday(2) */ +/* C99 does not have gettimeofday that is required to retrieve microseconds */ +/* uncomment following macro on platforms without gettimeofday(2) */ +/* #define NO_GETTIMEOFDAY */ + +/* gmtime(3) */ +/* C99 does not have reentrant gmtime_r() so it might cause troubles under */ +/* multi-threading environment. undef following macro on platforms that */ +/* does not have gmtime_r() and localtime_r(). */ +/* #define NO_GMTIME_R */ + +#ifdef _WIN32 +#if _MSC_VER +/* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */ +#define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL) +#define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL) +#else +#define NO_GMTIME_R +#endif +#endif + +/* asctime(3) */ +/* mruby usually use its own implementation of struct tm to string conversion */ +/* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */ +/* By default mruby tries to use asctime_r() which is reentrant. */ +/* Undef following macro on platforms that does not have asctime_r(). */ +/* #define NO_ASCTIME_R */ + +/* timegm(3) */ +/* mktime() creates tm structure for localtime; timegm() is for UTC time */ +/* define following macro to use probably faster timegm() on the platform */ +/* #define USE_SYSTEM_TIMEGM */ + +/** end of Time class configuration */ + +#ifndef NO_GETTIMEOFDAY +# ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN /* don't include winsock.h */ +# include <windows.h> +# define gettimeofday my_gettimeofday + +# ifdef _MSC_VER +# define UI64(x) x##ui64 +# else +# define UI64(x) x##ull +# endif + +typedef long suseconds_t; + +# if (!defined __MINGW64__) && (!defined __MINGW32__) +struct timeval { + time_t tv_sec; + suseconds_t tv_usec; +}; +# endif + +static int +gettimeofday(struct timeval *tv, void *tz) +{ + if (tz) { + mrb_assert(0); /* timezone is not supported */ + } + if (tv) { + union { + FILETIME ft; + unsigned __int64 u64; + } t; + GetSystemTimeAsFileTime(&t.ft); /* 100 ns intervals since Windows epoch */ + t.u64 -= UI64(116444736000000000); /* Unix epoch bias */ + t.u64 /= 10; /* to microseconds */ + tv->tv_sec = (time_t)(t.u64 / (1000 * 1000)); + tv->tv_usec = t.u64 % (1000 * 1000); + } + return 0; +} +# else +# include <sys/time.h> +# endif +#endif +#ifdef NO_GMTIME_R +#define gmtime_r(t,r) gmtime(t) +#define localtime_r(t,r) localtime(t) +#endif + +#ifndef USE_SYSTEM_TIMEGM +#define timegm my_timgm + +static unsigned int +is_leapyear(unsigned int y) +{ + return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); +} + +static time_t +timegm(struct tm *tm) +{ + static const unsigned int ndays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + }; + time_t r = 0; + int i; + unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)]; + + for (i = 70; i < tm->tm_year; ++i) + r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60; + for (i = 0; i < tm->tm_mon; ++i) + r += nday[i] * 24 * 60 * 60; + r += (tm->tm_mday - 1) * 24 * 60 * 60; + r += tm->tm_hour * 60 * 60; + r += tm->tm_min * 60; + r += tm->tm_sec; + return r; +} +#endif + +/* Since we are limited to using ISO C99, this implementation is based +* on time_t. That means the resolution of time is only precise to the +* second level. Also, there are only 2 timezones, namely UTC and LOCAL. +*/ + +enum mrb_timezone { + MRB_TIMEZONE_NONE = 0, + MRB_TIMEZONE_UTC = 1, + MRB_TIMEZONE_LOCAL = 2, + MRB_TIMEZONE_LAST = 3 +}; + +typedef struct mrb_timezone_name { + const char name[8]; + size_t len; +} mrb_timezone_name; + +static const mrb_timezone_name timezone_names[] = { + { "none", sizeof("none") - 1 }, + { "UTC", sizeof("UTC") - 1 }, + { "LOCAL", sizeof("LOCAL") - 1 }, +}; + +#ifndef DISABLE_STDIO +static const char mon_names[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +}; + +static const char wday_names[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", +}; +#endif + +struct mrb_time { + time_t sec; + time_t usec; + enum mrb_timezone timezone; + struct tm datetime; +}; + +static const struct mrb_data_type mrb_time_type = { "Time", mrb_free }; + +/** Updates the datetime of a mrb_time based on it's timezone and +seconds setting. Returns self on success, NULL of failure. */ +static struct mrb_time* +time_update_datetime(mrb_state *mrb, struct mrb_time *self) +{ + struct tm *aid; + + if (self->timezone == MRB_TIMEZONE_UTC) { + aid = gmtime_r(&self->sec, &self->datetime); + } + else { + aid = localtime_r(&self->sec, &self->datetime); + } + if (!aid) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec)); + /* not reached */ + return NULL; + } +#ifdef NO_GMTIME_R + self->datetime = *aid; /* copy data */ +#endif + + return self; +} + +static mrb_value +mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm) +{ + return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm)); +} + +void mrb_check_num_exact(mrb_state *mrb, mrb_float num); + +/* Allocates a mrb_time object and initializes it. */ +static struct mrb_time* +time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) +{ + struct mrb_time *tm; + time_t tsec = 0; + + mrb_check_num_exact(mrb, (mrb_float)sec); + mrb_check_num_exact(mrb, (mrb_float)usec); + + if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) { + goto out_of_range; + } + if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) { + goto out_of_range; + } + tsec = (time_t)sec; + if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) { + out_of_range: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec)); + } + tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); + tm->sec = tsec; + tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); + if (tm->usec < 0) { + long sec2 = (long)NDIV(usec,1000000); /* negative div */ + tm->usec -= sec2 * 1000000; + tm->sec += sec2; + } + else if (tm->usec >= 1000000) { + long sec2 = (long)(usec / 1000000); + tm->usec -= sec2 * 1000000; + tm->sec += sec2; + } + tm->timezone = timezone; + time_update_datetime(mrb, tm); + + return tm; +} + +static mrb_value +mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone) +{ + return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone)); +} + +static struct mrb_time* +current_mrb_time(mrb_state *mrb) +{ + struct mrb_time *tm; + + tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); +#if defined(TIME_UTC) + { + struct timespec ts; + if (timespec_get(&ts, TIME_UTC) == 0) { + mrb_free(mrb, tm); + mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons"); + } + tm->sec = ts.tv_sec; + tm->usec = ts.tv_nsec / 1000; + } +#elif defined(NO_GETTIMEOFDAY) + { + static time_t last_sec = 0, last_usec = 0; + + tm->sec = time(NULL); + if (tm->sec != last_sec) { + last_sec = tm->sec; + last_usec = 0; + } + else { + /* add 1 usec to differentiate two times */ + last_usec += 1; + } + tm->usec = last_usec; + } +#else + { + struct timeval tv; + + gettimeofday(&tv, NULL); + tm->sec = tv.tv_sec; + tm->usec = tv.tv_usec; + } +#endif + tm->timezone = MRB_TIMEZONE_LOCAL; + time_update_datetime(mrb, tm); + + return tm; +} + +/* Allocates a new Time object with given millis value. */ +static mrb_value +mrb_time_now(mrb_state *mrb, mrb_value self) +{ + return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb)); +} + +/* 15.2.19.6.1 */ +/* Creates an instance of time at the given time in seconds, etc. */ +static mrb_value +mrb_time_at(mrb_state *mrb, mrb_value self) +{ + mrb_float f, f2 = 0; + + mrb_get_args(mrb, "f|f", &f, &f2); + return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL); +} + +static struct mrb_time* +time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday, + mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec, + enum mrb_timezone timezone) +{ + time_t nowsecs; + struct tm nowtime = { 0 }; + + nowtime.tm_year = (int)ayear - 1900; + nowtime.tm_mon = (int)amonth - 1; + nowtime.tm_mday = (int)aday; + nowtime.tm_hour = (int)ahour; + nowtime.tm_min = (int)amin; + nowtime.tm_sec = (int)asec; + nowtime.tm_isdst = -1; + + if (nowtime.tm_mon < 0 || nowtime.tm_mon > 11 + || nowtime.tm_mday < 1 || nowtime.tm_mday > 31 + || nowtime.tm_hour < 0 || nowtime.tm_hour > 24 + || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0)) + || nowtime.tm_min < 0 || nowtime.tm_min > 59 + || nowtime.tm_sec < 0 || nowtime.tm_sec > 60) + mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); + + if (timezone == MRB_TIMEZONE_UTC) { + nowsecs = timegm(&nowtime); + } + else { + nowsecs = mktime(&nowtime); + } + if (nowsecs == (time_t)-1) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time."); + } + + return time_alloc(mrb, (double)nowsecs, ausec, timezone); +} + +/* 15.2.19.6.2 */ +/* Creates an instance of time at the given time in UTC. */ +static mrb_value +mrb_time_gm(mrb_state *mrb, mrb_value self) +{ + mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; + + mrb_get_args(mrb, "i|iiiiii", + &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); + return mrb_time_wrap(mrb, mrb_class_ptr(self), + time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC)); +} + + +/* 15.2.19.6.3 */ +/* Creates an instance of time at the given time in local time zone. */ +static mrb_value +mrb_time_local(mrb_state *mrb, mrb_value self) +{ + mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; + + mrb_get_args(mrb, "i|iiiiii", + &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); + return mrb_time_wrap(mrb, mrb_class_ptr(self), + time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL)); +} + +static struct mrb_time* +time_get_ptr(mrb_state *mrb, mrb_value time) +{ + struct mrb_time *tm; + + tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time); + if (!tm) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); + } + return tm; +} + +static mrb_value +mrb_time_eq(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + struct mrb_time *tm1, *tm2; + mrb_bool eq_p; + + mrb_get_args(mrb, "o", &other); + tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); + eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec; + + return mrb_bool_value(eq_p); +} + +static mrb_value +mrb_time_cmp(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + struct mrb_time *tm1, *tm2; + + mrb_get_args(mrb, "o", &other); + tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); + if (!tm1 || !tm2) return mrb_nil_value(); + if (tm1->sec > tm2->sec) { + return mrb_fixnum_value(1); + } + else if (tm1->sec < tm2->sec) { + return mrb_fixnum_value(-1); + } + /* tm1->sec == tm2->sec */ + if (tm1->usec > tm2->usec) { + return mrb_fixnum_value(1); + } + else if (tm1->usec < tm2->usec) { + return mrb_fixnum_value(-1); + } + return mrb_fixnum_value(0); +} + +static mrb_value +mrb_time_plus(mrb_state *mrb, mrb_value self) +{ + mrb_float f; + struct mrb_time *tm; + + mrb_get_args(mrb, "f", &f); + tm = time_get_ptr(mrb, self); + return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone); +} + +static mrb_value +mrb_time_minus(mrb_state *mrb, mrb_value self) +{ + mrb_float f; + mrb_value other; + struct mrb_time *tm, *tm2; + + mrb_get_args(mrb, "o", &other); + tm = time_get_ptr(mrb, self); + tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); + if (tm2) { + f = (mrb_float)(tm->sec - tm2->sec) + + (mrb_float)(tm->usec - tm2->usec) / 1.0e6; + return mrb_float_value(mrb, f); + } + else { + mrb_get_args(mrb, "f", &f); + return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone); + } +} + +/* 15.2.19.7.30 */ +/* Returns week day number of time. */ +static mrb_value +mrb_time_wday(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_wday); +} + +/* 15.2.19.7.31 */ +/* Returns year day number of time. */ +static mrb_value +mrb_time_yday(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_yday + 1); +} + +/* 15.2.19.7.32 */ +/* Returns year of time. */ +static mrb_value +mrb_time_year(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_year + 1900); +} + +/* 15.2.19.7.33 */ +/* Returns name of time's timezone. */ +static mrb_value +mrb_time_zone(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value(); + if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value(); + return mrb_str_new_static(mrb, + timezone_names[tm->timezone].name, + timezone_names[tm->timezone].len); +} + +/* 15.2.19.7.4 */ +/* Returns a string that describes the time. */ +static mrb_value +mrb_time_asctime(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm = time_get_ptr(mrb, self); + struct tm *d = &tm->datetime; + int len; + +#if defined(DISABLE_STDIO) + char *s; +# ifdef NO_ASCTIME_R + s = asctime(d); +# else + char buf[32]; + s = asctime_r(d, buf); +# endif + len = strlen(s)-1; /* truncate the last newline */ +#else + char buf[256]; + + len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d", + wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday, + d->tm_hour, d->tm_min, d->tm_sec, + tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "", + d->tm_year + 1900); +#endif + return mrb_str_new(mrb, buf, len); +} + +/* 15.2.19.7.6 */ +/* Returns the day in the month of the time. */ +static mrb_value +mrb_time_day(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_mday); +} + + +/* 15.2.19.7.7 */ +/* Returns true if daylight saving was applied for this time. */ +static mrb_value +mrb_time_dst_p(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_bool_value(tm->datetime.tm_isdst); +} + +/* 15.2.19.7.8 */ +/* 15.2.19.7.10 */ +/* Returns the Time object of the UTC(GMT) timezone. */ +static mrb_value +mrb_time_getutc(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm, *tm2; + + tm = time_get_ptr(mrb, self); + tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); + *tm2 = *tm; + tm2->timezone = MRB_TIMEZONE_UTC; + time_update_datetime(mrb, tm2); + return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); +} + +/* 15.2.19.7.9 */ +/* Returns the Time object of the LOCAL timezone. */ +static mrb_value +mrb_time_getlocal(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm, *tm2; + + tm = time_get_ptr(mrb, self); + tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); + *tm2 = *tm; + tm2->timezone = MRB_TIMEZONE_LOCAL; + time_update_datetime(mrb, tm2); + return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); +} + +/* 15.2.19.7.15 */ +/* Returns hour of time. */ +static mrb_value +mrb_time_hour(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_hour); +} + +/* 15.2.19.7.16 */ +/* Initializes a time by setting the amount of milliseconds since the epoch.*/ +static mrb_value +mrb_time_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, + amin = 0, asec = 0, ausec = 0; + int n; + struct mrb_time *tm; + + n = mrb_get_args(mrb, "|iiiiiii", + &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); + tm = (struct mrb_time*)DATA_PTR(self); + if (tm) { + mrb_free(mrb, tm); + } + mrb_data_init(self, NULL, &mrb_time_type); + + if (n == 0) { + tm = current_mrb_time(mrb); + } + else { + tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL); + } + mrb_data_init(self, tm, &mrb_time_type); + return self; +} + +/* 15.2.19.7.17(x) */ +/* Initializes a copy of this time object. */ +static mrb_value +mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy) +{ + mrb_value src; + struct mrb_time *t1, *t2; + + mrb_get_args(mrb, "o", &src); + if (mrb_obj_equal(mrb, copy, src)) return copy; + if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { + mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); + } + t1 = (struct mrb_time *)DATA_PTR(copy); + t2 = (struct mrb_time *)DATA_PTR(src); + if (!t2) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); + } + if (!t1) { + t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); + mrb_data_init(copy, t1, &mrb_time_type); + } + *t1 = *t2; + return copy; +} + +/* 15.2.19.7.18 */ +/* Sets the timezone attribute of the Time object to LOCAL. */ +static mrb_value +mrb_time_localtime(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + tm->timezone = MRB_TIMEZONE_LOCAL; + time_update_datetime(mrb, tm); + return self; +} + +/* 15.2.19.7.19 */ +/* Returns day of month of time. */ +static mrb_value +mrb_time_mday(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_mday); +} + +/* 15.2.19.7.20 */ +/* Returns minutes of time. */ +static mrb_value +mrb_time_min(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_min); +} + +/* 15.2.19.7.21 and 15.2.19.7.22 */ +/* Returns month of time. */ +static mrb_value +mrb_time_mon(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_mon + 1); +} + +/* 15.2.19.7.23 */ +/* Returns seconds in minute of time. */ +static mrb_value +mrb_time_sec(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_fixnum_value(tm->datetime.tm_sec); +} + + +/* 15.2.19.7.24 */ +/* Returns a Float with the time since the epoch in seconds. */ +static mrb_value +mrb_time_to_f(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6); +} + +/* 15.2.19.7.25 */ +/* Returns a Fixnum with the time since the epoch in seconds. */ +static mrb_value +mrb_time_to_i(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) { + return mrb_float_value(mrb, (mrb_float)tm->sec); + } + return mrb_fixnum_value((mrb_int)tm->sec); +} + +/* 15.2.19.7.26 */ +/* Returns a Float with the time since the epoch in microseconds. */ +static mrb_value +mrb_time_usec(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) { + return mrb_float_value(mrb, (mrb_float)tm->usec); + } + return mrb_fixnum_value((mrb_int)tm->usec); +} + +/* 15.2.19.7.27 */ +/* Sets the timezone attribute of the Time object to UTC. */ +static mrb_value +mrb_time_utc(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + tm->timezone = MRB_TIMEZONE_UTC; + time_update_datetime(mrb, tm); + return self; +} + +/* 15.2.19.7.28 */ +/* Returns true if this time is in the UTC timezone false if not. */ +static mrb_value +mrb_time_utc_p(mrb_state *mrb, mrb_value self) +{ + struct mrb_time *tm; + + tm = time_get_ptr(mrb, self); + return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC); +} + + +void +mrb_mruby_time_gem_init(mrb_state* mrb) +{ + struct RClass *tc; + /* ISO 15.2.19.2 */ + tc = mrb_define_class(mrb, "Time", mrb->object_class); + MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA); + mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable")); + mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */ + mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ + mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ + mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ + mrb_define_class_method(mrb, tc, "now", mrb_time_now, MRB_ARGS_NONE()); /* 15.2.19.6.5 */ + mrb_define_class_method(mrb, tc, "utc", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.6 */ + + mrb_define_method(mrb, tc, "==" , mrb_time_eq , MRB_ARGS_REQ(1)); + mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */ + mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */ + mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */ + mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE()); + mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE()); + mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */ + mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */ + mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */ + mrb_define_method(mrb, tc, "dst?" , mrb_time_dst_p , MRB_ARGS_NONE()); /* 15.2.19.7.7 */ + mrb_define_method(mrb, tc, "getgm" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */ + mrb_define_method(mrb, tc, "getlocal",mrb_time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */ + mrb_define_method(mrb, tc, "getutc" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */ + mrb_define_method(mrb, tc, "gmt?" , mrb_time_utc_p , MRB_ARGS_NONE()); /* 15.2.19.7.11 */ + mrb_define_method(mrb, tc, "gmtime" , mrb_time_utc , MRB_ARGS_NONE()); /* 15.2.19.7.13 */ + mrb_define_method(mrb, tc, "hour" , mrb_time_hour, MRB_ARGS_NONE()); /* 15.2.19.7.15 */ + mrb_define_method(mrb, tc, "localtime", mrb_time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */ + mrb_define_method(mrb, tc, "mday" , mrb_time_mday, MRB_ARGS_NONE()); /* 15.2.19.7.19 */ + mrb_define_method(mrb, tc, "min" , mrb_time_min, MRB_ARGS_NONE()); /* 15.2.19.7.20 */ + + mrb_define_method(mrb, tc, "mon" , mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.21 */ + mrb_define_method(mrb, tc, "month", mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.22 */ + + mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */ + mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */ + mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */ + mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */ + mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */ + mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */ + mrb_define_method(mrb, tc, "wday", mrb_time_wday, MRB_ARGS_NONE()); /* 15.2.19.7.30 */ + mrb_define_method(mrb, tc, "yday", mrb_time_yday, MRB_ARGS_NONE()); /* 15.2.19.7.31 */ + mrb_define_method(mrb, tc, "year", mrb_time_year, MRB_ARGS_NONE()); /* 15.2.19.7.32 */ + mrb_define_method(mrb, tc, "zone", mrb_time_zone, MRB_ARGS_NONE()); /* 15.2.19.7.33 */ + + mrb_define_method(mrb, tc, "initialize", mrb_time_initialize, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */ + mrb_define_method(mrb, tc, "initialize_copy", mrb_time_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */ + + /* + methods not available: + gmt_offset(15.2.19.7.12) + gmtoff(15.2.19.7.14) + utc_offset(15.2.19.7.29) + */ +} + +void +mrb_mruby_time_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb new file mode 100644 index 00000000..52b93117 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb @@ -0,0 +1,228 @@ +## +# Time ISO Test + +assert('Time.new', '15.2.3.3.3') do + Time.new.class == Time +end + +assert('Time', '15.2.19') do + Time.class == Class +end + +assert('Time.at', '15.2.19.6.1') do + assert_kind_of(Time, Time.at(1300000000.0)) + + assert_raise(FloatDomainError) { Time.at(Float::NAN) } + assert_raise(FloatDomainError) { Time.at(Float::INFINITY) } + assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) } + assert_raise(FloatDomainError) { Time.at(0, Float::NAN) } + assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) } + assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) } +end + +assert('Time.gm', '15.2.19.6.2') do + Time.gm(2012, 12, 23) +end + +assert('Time.local', '15.2.19.6.3') do + Time.local(2012, 12, 23) +end + +assert('Time.mktime', '15.2.19.6.4') do + Time.mktime(2012, 12, 23) +end + +assert('Time.now', '15.2.19.6.5') do + Time.now.class == Time +end + +assert('Time.utc', '15.2.19.6.6') do + Time.utc(2012, 12, 23) +end + +assert('Time#+', '15.2.19.7.1') do + t1 = Time.at(1300000000.0) + t2 = t1.+(60) + + assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011") + + assert_raise(FloatDomainError) { Time.at(0) + Float::NAN } + assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY } + assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY } +end + +assert('Time#-', '15.2.19.7.2') do + t1 = Time.at(1300000000.0) + t2 = t1.-(60) + + assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011") + + assert_raise(FloatDomainError) { Time.at(0) - Float::NAN } + assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY } + assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY } +end + +assert('Time#<=>', '15.2.19.7.3') do + t1 = Time.at(1300000000.0) + t2 = Time.at(1400000000.0) + t3 = Time.at(1500000000.0) + + t2.<=>(t1) == 1 and + t2.<=>(t2) == 0 and + t2.<=>(t3) == -1 and + t2.<=>(nil) == nil +end + +assert('Time#asctime', '15.2.19.7.4') do + Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('Time#ctime', '15.2.19.7.5') do + Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('Time#day', '15.2.19.7.6') do + Time.gm(2012, 12, 23).day == 23 +end + +assert('Time#dst?', '15.2.19.7.7') do + not Time.gm(2012, 12, 23).utc.dst? +end + +assert('Time#getgm', '15.2.19.7.8') do + Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('Time#getlocal', '15.2.19.7.9') do + t1 = Time.at(1300000000.0) + t2 = Time.at(1300000000.0) + t3 = t1.getlocal + + t1 == t3 and t3 == t2.getlocal +end + +assert('Time#getutc', '15.2.19.7.10') do + Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('Time#gmt?', '15.2.19.7.11') do + Time.at(1300000000.0).utc.gmt? +end + +# ATM not implemented +# assert('Time#gmt_offset', '15.2.19.7.12') do + +assert('Time#gmtime', '15.2.19.7.13') do + Time.at(1300000000.0).gmtime +end + +# ATM not implemented +# assert('Time#gmtoff', '15.2.19.7.14') do + +assert('Time#hour', '15.2.19.7.15') do + Time.gm(2012, 12, 23, 7, 6).hour == 7 +end + +# ATM doesn't really work +# assert('Time#initialize', '15.2.19.7.16') do + +assert('Time#initialize_copy', '15.2.19.7.17') do + time_tmp_2 = Time.at(7.0e6) + time_tmp_2.clone == time_tmp_2 +end + +assert('Time#localtime', '15.2.19.7.18') do + t1 = Time.at(1300000000.0) + t2 = Time.at(1300000000.0) + + t1.localtime + t1 == t2.getlocal +end + +assert('Time#mday', '15.2.19.7.19') do + Time.gm(2012, 12, 23).mday == 23 +end + +assert('Time#min', '15.2.19.7.20') do + Time.gm(2012, 12, 23, 7, 6).min == 6 +end + +assert('Time#mon', '15.2.19.7.21') do + Time.gm(2012, 12, 23).mon == 12 +end + +assert('Time#month', '15.2.19.7.22') do + Time.gm(2012, 12, 23).month == 12 +end + +assert('Times#sec', '15.2.19.7.23') do + Time.gm(2012, 12, 23, 7, 6, 40).sec == 40 +end + +assert('Time#to_f', '15.2.19.7.24') do + Time.at(1300000000.0).to_f == 1300000000.0 +end + +assert('Time#to_i', '15.2.19.7.25') do + Time.at(1300000000.0).to_i == 1300000000 +end + +assert('Time#usec', '15.2.19.7.26') do + Time.at(1300000000.0).usec == 0 +end + +assert('Time#utc', '15.2.19.7.27') do + Time.at(1300000000.0).utc +end + +assert('Time#utc?', '15.2.19.7.28') do + Time.at(1300000000.0).utc.utc? +end + +# ATM not implemented +# assert('Time#utc_offset', '15.2.19.7.29') do + +assert('Time#wday', '15.2.19.7.30') do + Time.gm(2012, 12, 23).wday == 0 +end + +assert('Time#yday', '15.2.19.7.31') do + Time.gm(2012, 12, 23).yday == 358 +end + +assert('Time#year', '15.2.19.7.32') do + Time.gm(2012, 12, 23).year == 2012 +end + +assert('Time#zone', '15.2.19.7.33') do + Time.at(1300000000.0).utc.zone == 'UTC' +end + +# Not ISO specified + +assert('Time#to_s') do + Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('Time#inspect') do + Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011" +end + +assert('day of week methods') do + t = Time.gm(2012, 12, 24) + assert_false t.sunday? + assert_true t.monday? + assert_false t.tuesday? + assert_false t.wednesday? + assert_false t.thursday? + assert_false t.friday? + assert_false t.saturday? +end + +assert('2000 times 500us make a second') do + t = Time.utc 2015 + 2000.times do + t += 0.0005 + end + t.usec == 0 +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake new file mode 100644 index 00000000..ce77e0bc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'toplevel object (main) methods extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb new file mode 100644 index 00000000..77456239 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb @@ -0,0 +1,11 @@ + +def self.include (*modules) + self.class.include(*modules) +end + +def self.private(*methods) +end +def self.protected(*methods) +end +def self.public(*methods) +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb new file mode 100644 index 00000000..aebdd8b4 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb @@ -0,0 +1,24 @@ +## +# Toplevel Self(Ext) Test + +assert('Toplevel#include') do + module ToplevelTestModule1 + def method_foo + :foo + end + + CONST_BAR = :bar + end + + module ToplevelTestModule2 + CONST_BAR = :bar2 + end + + self.include ToplevelTestModule2, ToplevelTestModule1 + + assert_true self.class.included_modules.include?( ToplevelTestModule1 ) + assert_true self.class.included_modules.include?( ToplevelTestModule2 ) + assert_equal :foo, method_foo + assert_equal :bar2, CONST_BAR +end + |