pax_global_header00006660000000000000000000000064126714035510014516gustar00rootroot0000000000000052 comment=4f20d58ac231526e26ef113e7623dc75c1140335 mruby-1.2.0+20160315+git4f20d58a/000077500000000000000000000000001267140355100154065ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/.gitignore000066400000000000000000000003271267140355100174000ustar00rootroot00000000000000# / *.bak *.d *.o /benchmark/**/*.dat /benchmark/*.pdf /benchmark/*.png *.orig *.pdb *.rej *.sav *.swp *.tmp *~ .DS_Store .ccmalloc .svn /.git cscope.out /src/y.tab.c /bin /build /mruby-source-*.gem doc/api .yardoc mruby-1.2.0+20160315+git4f20d58a/.travis.yml000066400000000000000000000007051267140355100175210ustar00rootroot00000000000000language: c sudo: false matrix: include: - os: linux sudo: 9000 - os: osx osx_image: xcode7.1 addons: apt: packages: - gperf env: MRUBY_CONFIG=travis_config.rb script: "./minirake all test" notifications: # Update mruby-head installed on Travis CI so other projects can test against it. webhooks: urls: - "https://rubies.travis-ci.org/rebuild/mruby-head" on_success: always on_failure: never mruby-1.2.0+20160315+git4f20d58a/.yardopts000066400000000000000000000003501267140355100172520ustar00rootroot00000000000000--markup markdown --plugin mruby --plugin coderay --output-dir doc/api src/**/*.c mrblib/**/*.rb include/**/*.h mrbgems/*/src/**/*.c mrbgems/*/mrblib/**/*.rb mrbgems/*/include/**/*.h - AUTHORS MITL CONTRIBUTING.md doc/guides/*.md mruby-1.2.0+20160315+git4f20d58a/AUTHORS000066400000000000000000000015101267140355100164530ustar00rootroot00000000000000Original Authors "mruby developers" are: Yukihiro Matsumoto SCSK KYUSHU CORPORATION Kyushu Institute of Technology Network Applied Communication Laboratory, Inc. Daniel Bovensiepen Jon Maken Bjorn De Meyer Yuichiro MASUI Masamitsu MURASE Masaki Muranaka Internet Initiative Japan Inc. Tadashi FUKUZAWA MATSUMOTO Ryosuke Yasuhiro Matsumoto Koji Yoshioka Jun Hiroe Narihiro Nakamura Yuichi Nishiwaki Tatsuhiko Kubo Takeshi Watanabe Yuki Kurihara specified non-profit corporation mruby Forum Kazuaki Tanaka Hiromasa Ishii Hiroshi Mimaki Satoshi Odawara Mitsubishi Electric Micro-Computer Application Software Co.,Ltd. Ralph Desir(Mav7) Hiroyuki Matsuzaki Yuhei Okazaki Manycolors, Inc. Shota Nakano Yuichi Osawa Terence Lee Zachary Scott mruby-1.2.0+20160315+git4f20d58a/CONTRIBUTING.md000066400000000000000000000044651267140355100176500ustar00rootroot00000000000000# How to contribute mruby is an open-source project which is looking forward to each contribution. ## Your Pull Request To make it easy to review and understand your change please keep the following things in mind before submitting your pull request: * Work on the latest possible state of **mruby/master** * Create a branch which is dedicated to your change * Test your changes before creating a pull request (```./minirake test```) * If possible write a test case which confirms your change * Don't mix several features or bug-fixes in one pull request * Create a meaningful commit message * Explain your change (i.e. with a link to the issue you are fixing) * Use mrbgem to provide non ISO features (classes, modules and methods) unless you have a special reason to implement them in the core ## Coding conventions How to style your C and Ruby code which you want to submit. ### C code The core part (parser, bytecode-interpreter, core-lib, etc.) of mruby is written in the C programming language. Please note the following hints for your C code: #### Comply with C99 (ISO/IEC 9899:1999) mruby should be highly portable to other systems and compilers. For this it is recommended to keep your code as close as possible to the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf). Although we target C99, Visual C++ is also an important target for mruby. For this reason a declaration of a local variable has to be at the beginning of a scope block. #### Reduce library dependencies to a minimum The dependencies to libraries should be kept to an absolute minimum. This increases the portability but makes it also easier to cut away parts of mruby on-demand. #### Don't use C++ style comments /* This is the preferred comment style */ Use C++ style comments only for temporary comment e.g. commenting out some code lines. #### Insert a break after the method return value: int main(void) { ... } ### Ruby code Parts of the standard library of mruby are written in the Ruby programming language itself. Please note the following hints for your Ruby code: #### Comply with the Ruby standard (ISO/IEC 30170:2012) mruby is currently targeting to execute Ruby code which complies to ISO/IEC 30170:2012 (http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579). mruby-1.2.0+20160315+git4f20d58a/ChangeLog000066400000000000000000000006311267140355100171600ustar00rootroot00000000000000Thu Apl 19 17:25:18 2012 Yukihiro Matsumoto * first release version 1.0.0 released. Local variables: add-log-time-format: (lambda () (let* ((time (current-time)) (system-time-locale "C") (diff (+ (cadr time) 32400)) (lo (% diff 65536)) (hi (+ (car time) (/ diff 65536)))) (format-time-string "%a %b %e %H:%M:%S %Y" (list hi lo) t))) indent-tabs-mode: t tab-width: 8 end: mruby-1.2.0+20160315+git4f20d58a/LEGAL000066400000000000000000000002601267140355100161530ustar00rootroot00000000000000LEGAL NOTICE INFORMATION ------------------------ All the files in this distribution are covered under the MIT license (see the file MITL) except some files mentioned below: mruby-1.2.0+20160315+git4f20d58a/MITL000066400000000000000000000020451267140355100160770ustar00rootroot00000000000000Copyright (c) 2016 mruby developers 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. mruby-1.2.0+20160315+git4f20d58a/Makefile000066400000000000000000000004251267140355100170470ustar00rootroot00000000000000# mruby is using Rake (http://rake.rubyforge.org) as a build tool. # We provide a minimalistic version called minirake inside of our # codebase. RAKE = ruby ./minirake all : $(RAKE) .PHONY : all test : all $(RAKE) test .PHONY : test clean : $(RAKE) clean .PHONY : clean mruby-1.2.0+20160315+git4f20d58a/NEWS000066400000000000000000000005271267140355100161110ustar00rootroot00000000000000* NEWS This document is a list of user visible feature changes made between releases except for bug fixes. Note that each entry is kept so brief that no reason behind or reference information is supplied with. For a full list of changes with all sufficient information, see the ChangeLog file. ** Information about first release v1.0.0 mruby-1.2.0+20160315+git4f20d58a/README.md000066400000000000000000000074321267140355100166730ustar00rootroot00000000000000[![Build Status][build-status-img]][travis-ci] ## What is mruby mruby is the lightweight implementation of the Ruby language complying to (part of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible. mruby can be linked and embedded within your application. We provide the interpreter program "mruby" and the interactive mruby shell "mirb" as examples. You can also compile Ruby programs into compiled byte code using the mruby compiler "mrbc". All those tools reside in the "bin" directory. "mrbc" is also able to generate compiled byte code in a C source file, see the "mrbtest" program under the "test" directory for an example. This achievement was sponsored by the Regional Innovation Creation R&D Programs of the Ministry of Economy, Trade and Industry of Japan. ## How to get mruby The stable version 1.2.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.2.0.zip](https://github.com/mruby/mruby/archive/1.2.0.zip) The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master) The trunk of the mruby source tree can be checked out with the following command: $ git clone https://github.com/mruby/mruby.git You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm). ## mruby home-page The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org). ## Mailing list We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby). ## How to compile and install (mruby and gems) See the [doc/guides/compile.md](doc/guides/compile.md) file. ## Running Tests To run the tests, execute the following from the project's root directory. $ make test Or $ ruby ./minirake test ## How to customize mruby (mrbgems) mruby contains a package manager called *mrbgems*. To create extensions in C and/or Ruby you should create a *GEM*. For a documentation of how to use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of how to use mrbgems look into the folder *examples/mrbgems/*. ## License mruby is released under the [MIT License](MITL). ## Note for License mruby has chosen a MIT License due to its permissive license allowing developers to target various environments such as embedded systems. However, the license requires the display of the copyright notice and license information in manuals for instance. Doing so for big projects can be complicated or troublesome. This is why mruby has decided to display "mruby developers" as the copyright name to make it simple conventionally. In the future, mruby might ask you to distribute your new code (that you will commit,) under the MIT License as a member of "mruby developers" but contributors will keep their copyright. (We did not intend for contributors to transfer or waive their copyrights, Actual copyright holder name (contributors) will be listed in the AUTHORS file.) Please ask us if you want to distribute your code under another license. ## How to Contribute See the [contribution guidelines][contribution-guidelines], and then send a pull request to . We consider you have granted non-exclusive right to your contributed code under MIT license. If you want to be named as one of mruby developers, please include an update to the AUTHORS file in your pull request. [ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579 [build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master [contribution-guidelines]: CONTRIBUTING.md [travis-ci]: https://travis-ci.org/mruby/mruby mruby-1.2.0+20160315+git4f20d58a/Rakefile000066400000000000000000000111011267140355100170450ustar00rootroot00000000000000# encoding: utf-8 # Build description. # basic build file for mruby MRUBY_ROOT = File.dirname(File.expand_path(__FILE__)) MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin') MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd') # load build systems load "#{MRUBY_ROOT}/tasks/ruby_ext.rake" load "#{MRUBY_ROOT}/tasks/mruby_build.rake" load "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake" # load configuration file MRUBY_CONFIG = (ENV['MRUBY_CONFIG'] && ENV['MRUBY_CONFIG'] != '') ? ENV['MRUBY_CONFIG'] : "#{MRUBY_ROOT}/build_config.rb" load MRUBY_CONFIG # load basic rules MRuby.each_target do |build| build.define_rules end # load custom rules load "#{MRUBY_ROOT}/src/mruby_core.rake" load "#{MRUBY_ROOT}/mrblib/mrblib.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" load "#{MRUBY_ROOT}/tasks/benchmark.rake" ############################## # generic build targets, rules task :default => :all bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin" FileUtils.mkdir_p bin_path, { :verbose => $verbose } depfiles = MRuby.targets['host'].bins.map do |bin| install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") source_path = MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/#{bin}") file install_path => source_path do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end install_path end MRuby.each_target do |target| gems.map do |gem| current_dir = gem.dir.relative_path_from(Dir.pwd) relative_from_root = gem.dir.relative_path_from(MRUBY_ROOT) current_build_dir = File.expand_path "#{build_dir}/#{relative_from_root}" if current_build_dir !~ /^#{build_dir}/ current_build_dir = "#{build_dir}/mrbgems/#{gem.name}" end gem.bins.each do |bin| exec = exefile("#{build_dir}/bin/#{bin}") objs = Dir.glob("#{current_dir}/tools/#{bin}/*.{c,cpp,cxx,cc}").map { |f| objfile(f.pathmap("#{current_build_dir}/tools/#{bin}/%n")) } file exec => objs + [libfile("#{build_dir}/lib/libmruby")] do |t| gem_flags = gems.map { |g| g.linker.flags } gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } gem_flags_after_libraries = gems.map { |g| g.linker.flags_after_libraries } gem_libraries = gems.map { |g| g.linker.libraries } gem_library_paths = gems.map { |g| g.linker.library_paths } linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries, gem_flags_after_libraries end if target == MRuby.targets['host'] install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end depfiles += [ install_path ] elsif target == MRuby.targets['host-debug'] unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin]) install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end depfiles += [ install_path ] end else depfiles += [ exec ] end end end end depfiles += MRuby.targets.map { |n, t| [t.libfile("#{t.build_dir}/lib/libmruby")] }.flatten depfiles += MRuby.targets.reject { |n, t| n == 'host' }.map { |n, t| t.bins.map { |bin| t.exefile("#{t.build_dir}/bin/#{bin}") } }.flatten desc "build all targets, install (locally) in-repo" task :all => depfiles do puts puts "Build summary:" puts MRuby.each_target do print_build_summary end end desc "run all mruby tests" task :test => ["all"] do MRuby.each_target do run_test if test_enabled? end end desc "clean all built and in-repo installed artifacts" task :clean do MRuby.each_target do |t| FileUtils.rm_rf t.build_dir, { :verbose => $verbose } end FileUtils.rm_f depfiles, { :verbose => $verbose } puts "Cleaned up target build folder" end desc "clean everything!" task :deep_clean => ["clean"] do MRuby.each_target do |t| FileUtils.rm_rf t.gem_clone_dir, { :verbose => $verbose } end puts "Cleaned up mrbgems build folder" end desc 'generate document' task :doc do begin sh "mrbdoc" rescue puts "ERROR: To generate documents, you should install yard-mruby gem." puts " $ gem install yard-mruby" end end mruby-1.2.0+20160315+git4f20d58a/TODO000066400000000000000000000003751267140355100161030ustar00rootroot00000000000000Things to do (Things that are not done yet) * special variables ($1,$2..) * super in aliased methods * multi-assignment decomposing * keyword arguments in def statement Things to improve (Done but things to fix) * Make additions as they are noticed. mruby-1.2.0+20160315+git4f20d58a/benchmark/000077500000000000000000000000001267140355100173405ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/benchmark/bm_ao_render.rb000066400000000000000000000152661267140355100223130ustar00rootroot00000000000000# AO render benchmark # Original program (C) Syoyo Fujita in Javascript (and other languages) # https://code.google.com/p/aobench/ # Ruby(yarv2llvm) version by Hideki Miura # mruby version by Hideki Miura # IMAGE_WIDTH = 64 IMAGE_HEIGHT = 64 NSUBSAMPLES = 2 NAO_SAMPLES = 8 module Rand # Use xorshift @@x = 123456789 @@y = 362436069 @@z = 521288629 @@w = 88675123 BNUM = 1 << 29 BNUMF = BNUM.to_f def self.rand x = @@x t = x ^ ((x & 0xfffff) << 11) w = @@w @@x, @@y, @@z = @@y, @@z, w w = @@w = (w ^ (w >> 19) ^ (t ^ (t >> 8))) (w % BNUM) / BNUMF end end class Vec def initialize(x, y, z) @x = x @y = y @z = z end def x=(v); @x = v; end def y=(v); @y = v; end def z=(v); @z = v; end def x; @x; end def y; @y; end def z; @z; end def vadd(b) Vec.new(@x + b.x, @y + b.y, @z + b.z) end def vsub(b) Vec.new(@x - b.x, @y - b.y, @z - b.z) end def vcross(b) Vec.new(@y * b.z - @z * b.y, @z * b.x - @x * b.z, @x * b.y - @y * b.x) end def vdot(b) r = @x * b.x + @y * b.y + @z * b.z r end def vlength Math.sqrt(@x * @x + @y * @y + @z * @z) end def vnormalize len = vlength v = Vec.new(@x, @y, @z) if len > 1.0e-17 then v.x = v.x / len v.y = v.y / len v.z = v.z / len end v end end class Sphere def initialize(center, radius) @center = center @radius = radius end def center; @center; end def radius; @radius; end def intersect(ray, isect) rs = ray.org.vsub(@center) b = rs.vdot(ray.dir) c = rs.vdot(rs) - (@radius * @radius) d = b * b - c if d > 0.0 then t = - b - Math.sqrt(d) if t > 0.0 and t < isect.t then isect.t = t isect.hit = true isect.pl = Vec.new(ray.org.x + ray.dir.x * t, ray.org.y + ray.dir.y * t, ray.org.z + ray.dir.z * t) n = isect.pl.vsub(@center) isect.n = n.vnormalize end end end end class Plane def initialize(p, n) @p = p @n = n end def intersect(ray, isect) d = -@p.vdot(@n) v = ray.dir.vdot(@n) v0 = v if v < 0.0 then v0 = -v end if v0 < 1.0e-17 then return end t = -(ray.org.vdot(@n) + d) / v if t > 0.0 and t < isect.t then isect.hit = true isect.t = t isect.n = @n isect.pl = Vec.new(ray.org.x + t * ray.dir.x, ray.org.y + t * ray.dir.y, ray.org.z + t * ray.dir.z) end end end class Ray def initialize(org, dir) @org = org @dir = dir end def org; @org; end def org=(v); @org = v; end def dir; @dir; end def dir=(v); @dir = v; end end class Isect def initialize @t = 10000000.0 @hit = false @pl = Vec.new(0.0, 0.0, 0.0) @n = Vec.new(0.0, 0.0, 0.0) end def t; @t; end def t=(v); @t = v; end def hit; @hit; end def hit=(v); @hit = v; end def pl; @pl; end def pl=(v); @pl = v; end def n; @n; end def n=(v); @n = v; end end def clamp(f) i = f * 255.5 if i > 255.0 then i = 255.0 end if i < 0.0 then i = 0.0 end i.to_i end def otherBasis(basis, n) basis[2] = Vec.new(n.x, n.y, n.z) basis[1] = Vec.new(0.0, 0.0, 0.0) if n.x < 0.6 and n.x > -0.6 then basis[1].x = 1.0 elsif n.y < 0.6 and n.y > -0.6 then basis[1].y = 1.0 elsif n.z < 0.6 and n.z > -0.6 then basis[1].z = 1.0 else basis[1].x = 1.0 end basis[0] = basis[1].vcross(basis[2]) basis[0] = basis[0].vnormalize basis[1] = basis[2].vcross(basis[0]) basis[1] = basis[1].vnormalize end class Scene def initialize @spheres = Array.new @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5) @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5) @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5) @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0)) end def ambient_occlusion(isect) basis = Array.new(3) otherBasis(basis, isect.n) ntheta = NAO_SAMPLES nphi = NAO_SAMPLES eps = 0.0001 occlusion = 0.0 p0 = Vec.new(isect.pl.x + eps * isect.n.x, isect.pl.y + eps * isect.n.y, isect.pl.z + eps * isect.n.z) nphi.times do |j| ntheta.times do |i| r = Rand::rand phi = 2.0 * 3.14159265 * Rand::rand x = Math.cos(phi) * Math.sqrt(1.0 - r) y = Math.sin(phi) * Math.sqrt(1.0 - r) z = Math.sqrt(r) rx = x * basis[0].x + y * basis[1].x + z * basis[2].x ry = x * basis[0].y + y * basis[1].y + z * basis[2].y rz = x * basis[0].z + y * basis[1].z + z * basis[2].z raydir = Vec.new(rx, ry, rz) ray = Ray.new(p0, raydir) occisect = Isect.new @spheres[0].intersect(ray, occisect) @spheres[1].intersect(ray, occisect) @spheres[2].intersect(ray, occisect) @plane.intersect(ray, occisect) if occisect.hit then occlusion = occlusion + 1.0 else 0.0 end end end occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f) Vec.new(occlusion, occlusion, occlusion) end def render(w, h, nsubsamples) cnt = 0 nsf = nsubsamples.to_f h.times do |y| w.times do |x| rad = Vec.new(0.0, 0.0, 0.0) # Subsmpling nsubsamples.times do |v| nsubsamples.times do |u| cnt = cnt + 1 wf = w.to_f hf = h.to_f xf = x.to_f yf = y.to_f uf = u.to_f vf = v.to_f px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0) py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0) eye = Vec.new(px, py, -1.0).vnormalize ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye) isect = Isect.new @spheres[0].intersect(ray, isect) @spheres[1].intersect(ray, isect) @spheres[2].intersect(ray, isect) @plane.intersect(ray, isect) if isect.hit then col = ambient_occlusion(isect) rad.x = rad.x + col.x rad.y = rad.y + col.y rad.z = rad.z + col.z else 0.0 end end end r = rad.x / (nsf * nsf) g = rad.y / (nsf * nsf) b = rad.z / (nsf * nsf) printf("%c", clamp(r)) printf("%c", clamp(g)) printf("%c", clamp(b)) end end end end # File.open("ao.ppm", "w") do |fp| printf("P6\n") printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT) printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT) Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) # Scene.new.render(256, 256, 2) # end mruby-1.2.0+20160315+git4f20d58a/benchmark/bm_app_lc_fizzbuzz.rb000066400000000000000000000367301267140355100235670ustar00rootroot00000000000000# # FizzBuzz program using only lambda calculus # # This program is quoted from # "Understanding Computation" by Tom Stuart # http://computationbook.com/ # # You can understand why this program works fine by reading this book. # solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }] FIRST = -> l { LEFT[RIGHT[l]] } IF = -> b { b } LEFT = -> p { p[-> x { -> y { x } } ] } RIGHT = -> p { p[-> x { -> y { y } } ] } IS_EMPTY = LEFT REST = -> l { RIGHT[RIGHT[l]] } def to_integer(proc) proc[-> n { n + 1 }][0] end def to_boolean(proc) IF[proc][true][false] end def to_array(proc) array = [] until to_boolean(IS_EMPTY[proc]) array.push(FIRST[proc]) proc = REST[proc] end array end def to_char(c) '0123456789BFiuz'.slice(to_integer(c)) end def to_string(s) to_array(s).map { |c| to_char(c) }.join end answer = to_array(solution).map do |p| to_string(p) end answer_str = answer.to_a # puts answer_str mruby-1.2.0+20160315+git4f20d58a/benchmark/bm_fib.rb000066400000000000000000000001071267140355100211010ustar00rootroot00000000000000 def fib n return n if n < 2 fib(n-2) + fib(n-1) end puts fib(37) mruby-1.2.0+20160315+git4f20d58a/benchmark/bm_so_lists.rb000066400000000000000000000017511267140355100222060ustar00rootroot00000000000000#from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby NUM = 300 SIZE = 10000 def test_lists() # create a list of integers (Li1) from 1 to SIZE li1 = (1..SIZE).to_a # copy the list to li2 (not by individual items) li2 = li1.dup # remove each individual item from left side of li2 and # append to right side of li3 (preserving order) li3 = Array.new while (not li2.empty?) li3.push(li2.shift) end # li2 must now be empty # remove each individual item from right side of li3 and # append to right side of li2 (reversing list) while (not li3.empty?) li2.push(li3.pop) end # li3 must now be empty # reverse li1 in place li1.reverse! # check that first item is now SIZE if li1[0] != SIZE then p "not SIZE" 0 else # compare li1 and li2 for equality if li1 != li2 then return(0) else # return the length of the list li1.length end end end i = 0 while i 'masuidrive/mrbgems-example', :checksum_hash => '76518e8aecd131d047378448ac8055fa29d974a9' # conf.gem :git => 'git@github.com:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v' # include the default GEMs conf.gembox 'default' # C compiler settings # conf.cc do |cc| # cc.command = ENV['CC'] || 'gcc' # cc.flags = [ENV['CFLAGS'] || %w()] # cc.include_paths = ["#{root}/include"] # cc.defines = %w(DISABLE_GEMS) # cc.option_include_path = '-I%s' # cc.option_define = '-D%s' # cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}" # end # mrbc settings # conf.mrbc do |mrbc| # mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers # end # Linker settings # conf.linker do |linker| # linker.command = ENV['LD'] || 'gcc' # linker.flags = [ENV['LDFLAGS'] || []] # linker.flags_before_libraries = [] # linker.libraries = %w() # linker.flags_after_libraries = [] # linker.library_paths = [] # linker.option_library = '-l%s' # linker.option_library_path = '-L%s' # linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}" # end # Archiver settings # conf.archiver do |archiver| # archiver.command = ENV['AR'] || 'ar' # archiver.archive_options = 'rs %{outfile} %{objs}' # end # Parser generator settings # conf.yacc do |yacc| # yacc.command = ENV['YACC'] || 'bison' # yacc.compile_options = '-o %{outfile} %{infile}' # end # gperf settings # conf.gperf do |gperf| # gperf.command = 'gperf' # gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' # end # file extensions # conf.exts do |exts| # exts.object = '.o' # exts.executable = '' # '.exe' if Windows # exts.library = '.a' # end # file separetor # conf.file_separator = '/' # bintest # conf.enable_bintest end MRuby::Build.new('host-debug') do |conf| # load specific toolchain settings # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' # C compiler settings conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK) # Generate mruby debugger command (require mruby-eval) conf.gem :core => "mruby-bin-debugger" # bintest # conf.enable_bintest end MRuby::Build.new('test') do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug conf.enable_bintest conf.enable_test conf.gembox 'default' end MRuby::Build.new('bench') do |conf| toolchain :gcc conf.cc.flags << '-O3' conf.gembox 'default' end # Define cross build settings # MRuby::CrossBuild.new('32bit') do |conf| # toolchain :gcc # # conf.cc.flags << "-m32" # conf.linker.flags << "-m32" # # conf.build_mrbtest_lib_only # # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' # # conf.test_runner.command = 'env' # # end mruby-1.2.0+20160315+git4f20d58a/doc/000077500000000000000000000000001267140355100161535ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/doc/guides/000077500000000000000000000000001267140355100174335ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/doc/guides/compile.md000066400000000000000000000333211267140355100214070ustar00rootroot00000000000000# Compile mruby uses Rake to compile and cross-compile all libraries and binaries. ## Prerequisites To compile mruby out of the source code you need the following tools: * C Compiler (i.e. ```gcc```) * Linker (i.e. ```gcc```) * Archive utility (i.e. ```ar```) * Parser generator (i.e. ```bison```) * Ruby 1.8 or 1.9 (i.e. ```ruby``` or ```jruby```) Optional: * GIT (to update mruby source and integrate mrbgems easier) * C++ compiler (to use GEMs which include \*.cpp, \*.cxx, \*.cc) * Assembler (to use GEMs which include \*.asm) ## Usage Inside of the root directory of the mruby source a file exists called *build_config.rb*. This file contains the build configuration of mruby and looks like this for example: ```ruby MRuby::Build.new do |conf| toolchain :gcc end ``` All tools necessary to compile mruby can be set or modified here. In case you want to maintain an additional *build_config.rb* you can define a customized path using the *$MRUBY_CONFIG* environment variable. To compile just call ```./minirake``` inside of the mruby source root. To generate and execute the test tools call ```./minirake test```. To clean all build files call ```./minirake clean```. To see full command line on build, call ```./minirake -v```. ## Build Configuration Inside of the *build_config.rb* the following options can be configured based on your environment. ### Toolchains The mruby build system already contains a set of toolchain templates which configure the build environment for specific compiler infrastructures. #### GCC Toolchain configuration for the GNU C Compiler. ```ruby toolchain :gcc ``` #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. ```ruby toolchain :clang ``` #### Visual Studio 2010, 2012 and 2013 Toolchain configuration for Visual Studio on Windows. If you use the [Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx), you normally do not have to specify this manually, since it gets automatically detected by our build process. ``` toolchain :visualcpp ``` #### Android Toolchain configuration for Android. ```ruby toolchain :android ``` Requires the custom standalone Android NDK and the toolchain path in ```ANDROID_STANDALONE_TOOLCHAIN```. ### Binaries It is possible to select which tools should be compiled during the compilation process. The following tools can be selected: * mruby (mruby interpreter) * mirb (mruby interactive shell) To select them declare conf.gem as follows: ```ruby conf.gem "#{root}/mrbgems/mruby-bin-mruby" conf.gem "#{root}/mrbgems/mruby-bin-mirb" ``` ### File Separator Some environments require a different file separator character. It is possible to set the character via ```conf.file_separator```. ```ruby conf.file_separator = '/' ``` ### C Compiler Configuration of the C compiler binary, flags and include paths. ```ruby conf.cc do |cc| cc.command = ... cc.flags = ... cc.include_paths = ... cc.defines = ... cc.option_include_path = ... cc.option_define = ... cc.compile_options = ... end ``` C Compiler has header searcher to detect installed library. If you need a include path of header file use ```search_header_path```: ```ruby # Searches ```iconv.h```. # If found it will return include path of the header file. # Otherwise it will return nil . fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h' ``` If you need a full file name of header file use ```search_header```: ```ruby # Searches ```iconv.h```. # If found it will return full path of the header file. # Otherwise it will return nil . iconv_h = conf.cc.search_header 'iconv.h' print "iconv.h found: #{iconv_h}\n" ``` Header searcher uses compiler's ```include_paths``` by default. When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain) it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```) If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler: ```ruby def conf.cc.header_search_paths ['/opt/local/include'] + include_paths end ``` ### Linker Configuration of the Linker binary, flags and library paths. ```ruby conf.linker do |linker| linker.command = ... linker.flags = ... linker.flags_before_libraries = ... linker.libraries = ... linker.flags_after_libraries = ... linker.library_paths = .... linker.option_library = ... linker.option_library_path = ... linker.link_options = ... end ``` ### Archiver Configuration of the Archiver binary and flags. ```ruby conf.archiver do |archiver| archiver.command = ... archiver.archive_options = ... end ``` ### Parser Generator Configuration of the Parser Generator binary and flags. ```ruby conf.yacc do |yacc| yacc.command = ... yacc.compile_options = ... end ``` ### GPerf Configuration of the GPerf binary and flags. ```ruby conf.gperf do |gperf| gperf.command = ... gperf.compile_options = ... end ``` ### File Extensions ```ruby conf.exts do |exts| exts.object = ... exts.executable = ... exts.library = ... end ``` ### Mrbgems Integrate GEMs in the build process. ```ruby # Integrate GEM with additional configuration conf.gem 'path/to/gem' do |g| g.cc.flags << ... end # Integrate GEM without additional configuration conf.gem 'path/to/another/gem' ``` See doc/mrbgems/README.md for more option about mrbgems. ### Mrbtest Configuration Mrbtest build process. If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only``` ```ruby conf.build_mrbtest_lib_only ``` ### Bintest Tests for mrbgem tools using CRuby. To have bintests place \*.rb scripts to ```bintest/``` directory of mrbgems. See ```mruby-bin-*/bintest/*.rb``` if you need examples. If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```. You can enable it with following: ```ruby conf.enable_bintest ``` ### C++ ABI mruby can use C++ exception to raise exception internally. It is called C++ ABI mode. By using C++ exception it can release C++ stack object correctly. Whenever you mix C++ code C++ ABI mode would be enabled automatically. If you need to enable C++ ABI mode explicitly add the following: ```ruby conf.enable_cxx_abi ``` #### C++ exception disabling. If you need to force C++ exception disable (For example using a compiler option to disable C++ exception) add following: ```ruby conf.disable_cxx_exception ``` Note that it must be called before ```enable_cxx_abi``` or ```gem``` method. ### Debugging mode To enable debugging mode add the following: ```ruby conf.enable_debug ``` When debugging mode is enabled * Macro ```MRB_DEBUG``` would be defined. * Which means ```mrb_assert()``` macro is enabled. * Debug information of irep would be generated by ```mrbc```. * Because ```-g``` flag would be added to ```mrbc``` runner. * You can have better backtrace of mruby scripts with this. ## Cross-Compilation mruby can also be cross-compiled from one platform to another. To achieve this the *build_config.rb* needs to contain an instance of ```MRuby::CrossBuild```. This instance defines the compilation tools and flags for the target platform. An example could look like this: ```ruby MRuby::CrossBuild.new('32bit') do |conf| toolchain :gcc conf.cc.flags << "-m32" conf.linker.flags << "-m32" end ``` All configuration options of ```MRuby::Build``` can also be used in ```MRuby::CrossBuild```. ### Mrbtest in Cross-Compilation In cross compilation, you can run ```mrbtest``` on emulator if you have it by changing configuration of test runner. ```ruby conf.test_runner do |t| t.command = ... # set emulator. this value must be non nil or false t.flags = ... # set flags of emulator def t.run(bin) # override `run` if you need to change the behavior of it ... # `bin` is the full path of mrbtest end end ``` ## Build process During the build process the directory *build* will be created in the root directory. The structure of this directory will look like this: +- build | +- host | +- bin <- Binaries (mirb, mrbc and mruby) | +- lib <- Libraries (libmruby.a and libmruby_core.a) | +- mrblib | +- src | +- test <- mrbtest tool | +- tools | +- mirb | +- mrbc | +- mruby The compilation workflow will look like this: * compile all files under *src* (object files will be stored in *build/host/src*) * generate parser grammar out of *src/parse.y* (generated result will be stored in *build/host/src/y.tab.c*) * compile *build/host/src/y.tab.c* to *build/host/src/y.tab.o* * create *build/host/lib/libmruby_core.a* out of all object files (C only) * create ```build/host/bin/mrbc``` by compiling *tools/mrbc/mrbc.c* and linking with *build/host/lib/libmruby_core.a* * create *build/host/mrblib/mrblib.c* by compiling all \*.rb files under *mrblib* with ```build/host/bin/mrbc``` * compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o* * create *build/host/lib/libmruby.a* out of all object files (C and Ruby) * create ```build/host/bin/mruby``` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and linking with *build/host/lib/libmruby.a* * create ```build/host/bin/mirb``` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and linking with *build/host/lib/libmruby.a* ``` _____ _____ ______ ____ ____ _____ _____ ____ | CC |->|GEN |->|AR |->|CC |->|CC |->|AR |->|CC |->|CC | | *.c | |y.tab| |core.a| |mrbc| |*.rb| |lib.a| |mruby| |mirb| ----- ----- ------ ---- ---- ----- ----- ---- ``` ### Cross-Compilation In case of a cross-compilation to *i386* the *build* directory structure looks like this: +- build | +- host | | | +- bin <- Native Binaries | | | +- lib <- Native Libraries | | | +- mrblib | | | +- src | | | +- test <- Native mrbtest tool | | | +- tools | | | +- mirb | | | +- mrbc | | | +- mruby +- i386 | +- bin <- Cross-compiled Binaries | +- lib <- Cross-compiled Libraries | +- mrblib | +- src | +- test <- Cross-compiled mrbtest tool | +- tools | +- mirb | +- mrbc | +- mruby An extra directory is created for the target platform. In case you compile for *i386* a directory called *i386* is created under the build directory. The cross compilation workflow starts in the same way as the normal compilation by compiling all *native* libraries and binaries. Afterwards the cross compilation process proceeds like this: * cross-compile all files under *src* (object files will be stored in *build/i386/src*) * generate parser grammar out of *src/parse.y* (generated result will be stored in *build/i386/src/y.tab.c*) * cross-compile *build/i386/src/y.tab.c* to *build/i386/src/y.tab.o* * create *build/i386/mrblib/mrblib.c* by compiling all \*.rb files under *mrblib* with the native ```build/host/bin/mrbc``` * cross-compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o* * create *build/i386/lib/libmruby.a* out of all object files (C and Ruby) * create ```build/i386/bin/mruby``` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and linking with *build/i386/lib/libmruby.a* * create ```build/i386/bin/mirb``` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and linking with *build/i386/lib/libmruby.a* * create *build/i386/lib/libmruby_core.a* out of all object files (C only) * create ```build/i386/bin/mrbc``` by cross-compiling *tools/mrbc/mrbc.c* and linking with *build/i386/lib/libmruby_core.a* ``` _______________________________________________________________ | Native Compilation for Host System | | _____ ______ _____ ____ ____ _____ | | | CC | -> |AR | -> |GEN | -> |CC | -> |CC | -> |AR | | | | *.c | |core.a| |y.tab| |mrbc| |*.rb| |lib.a| | | ----- ------ ----- ---- ---- ----- | --------------------------------------------------------------- || \||/ \/ ________________________________________________________________ | Cross Compilation for Target System | | _____ _____ _____ ____ ______ _____ | | | CC | -> |AR | -> |CC | -> |CC | -> |AR | -> |CC | | | | *.c | |lib.a| |mruby| |mirb| |core.a| |mrbc | | | ----- ----- ----- ---- ------ ----- | ---------------------------------------------------------------- ``` ## Build Configuration Examples ### Minimal Library To build a minimal mruby library you need to use the Cross Compiling feature due to the reason that there are functions (i.e. stdio) which can't be disabled for the main build. ```ruby MRuby::CrossBuild.new('Minimal') do |conf| toolchain :gcc conf.cc.defines = %w(MRB_DISABLE_STDIO) conf.bins = [] end ``` This configuration defines a cross compile build called 'Minimal' which is using the GCC and compiles for the host machine. It also disables all usages of stdio and doesn't compile any binaries (i.e. mrbc). ## Test Environment mruby's build process includes a test environment. In case you start the testing of mruby, a native binary called ```mrbtest``` will be generated and executed. This binary contains all test cases which are defined under *test/t*. In case of a cross-compilation an additional cross-compiled *mrbtest* binary is generated. You can copy this binary and run on your target system. mruby-1.2.0+20160315+git4f20d58a/doc/guides/debugger.md000066400000000000000000000153511267140355100215460ustar00rootroot00000000000000# How to Use the mruby Debugger copyright (c) 2014 Specified Non-Profit Corporation mruby Forum ## 1. Summary This file documents the mruby debugger ('mrdb') methods. ## 2 Debugging with mrdb ## 2.1 Building mrdb The trunk of the mruby source tree, with the most recent mrdb, can be checked out with the following command: ```bash $ git clone https://github.com/mruby/mruby.git ``` To run the `make` command: ```bash $ cd mruby $ make ``` By default, the `make` command will install the debugger files into mruby/bin. You can add the path for mrdb on your host environment with the following command: ```bash $ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc $ source ~/.bashrc ``` `*MRUBY_ROOT` is the directory in which mruby source code will be installed. To confirm mrdb was installed properly, run mrdb with the `--version` option: ```bash $ mrdb --version mruby 1.2.0 (2014-11-17) ``` ## 2.2 Basic Operation ### 2.2.1 Debugging mruby Script Files (rb file) with mrdb To invoke the mruby debugger, just type `mrdb`. To specify the script file: ```bash $ mrdb [option] file name ``` For example: Debugging sample.rb ```bash $ mrdb sample.rb ``` You can execute the shell commands listed below: |command|description| |:-:|:--| |run|execute programs| |step|execute stepping| |continue|execute continuing program| |break|configure the breaking point| |delete|deleting the breaking points| |disable|disabling the breaking points| |enable|enabling the breaking points| |info breakpoints|showing list of the breaking points| |print|evaluating and printing the values of the mruby expressions in the script| |list|displaying the source cords| |help|showing help| |quit|terminating the mruby debugger| ### 2.2.2 Debugging mruby Binary Files (mrb file) with mrdb You can debug the mruby binary files. #### 2.2.2.1 Debugging the binary files * notice To debug mruby binary files, you need to compile mruby files with option `-g`. ```bash $ mrbc -g sample.rb ``` You can debug the mruby binary files with following command and the option `-b`. ```bash $ mrdb -b sample.mrb ``` Then you can execute all debugger shell commands. #### Break Command You can use any breakpoint to stop the program by specifying the line number and method name. The breakpoint list will be displayed after you have set the breakpoint successfully. Usage: ``` break [file:]linenum b [file:]linenum break [class:]method b [class:]method ``` The breakpoint will be ordered in serial from 1. The number, which was given to the deleted breakpoint, will never be given to another breakpoint again. You can give multiple breakpoints to specified the line number and method. Be ware that breakpoint command will not check the validity of the class name and method name. You can get the current breakpoint information by the following options. breakpoint breakpoint number : file name. line number breakpoint breakpoint number : [class name,] method name #### Continue Command Usage: ``` continue [N] c [N] ``` N: the next breakpoint number When resuming the program, it will stop at breakpoint N (N-1 breakpoint will be ignored). When you run the `continue` command without specifying N, the program will be stopped at the next breakpoint. Example: ``` (foo.rb:1) continue 3 ``` This will resume the program and stop it at the third breakpoint. #### Delete Command This will delete the specified breakpoint. Usage: ``` delete [breakpoint-no] d [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) delete ``` This will delete all of the breakpoints. ``` (foo.rb:1) delete 1 3 ``` This will delete the breakpoint at 1 and 3. #### Disable Command This will disable the specified breakpoint. Usage: ``` disable [breakpoint-no] dis [breakpoint-no] ``` reappointing: breakpoint number Example: ``` (foo.rb:1) disable ``` Use `disable` if you would like to disable all of the breakpoints. ``` (foo.rb:1) disable 1 3 ``` This will disable the breakpoints at 1 and 3. #### Enable Command This will enable the specified breakpoints. Usage: ``` enable [breakpoint-no] e [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) enable ``` Enabling all breakpoints ``` (foo.rb:1) enable 1 3 ``` Enabling the breakpoint 1 and 3 #### eval command Evaluating the string as source code and printing the value. Same as print command, please see print command. #### help command Displaying the help message. Usage: ``` help [command] h [command] ``` Typing `help` without any options will display the command list. #### Info Breakpoints Command Displaying the specified breakpoint information. Usage: ``` info breakpoints [breakpoint-no] i b [breakpoint-no] ``` breakpoint-no: breakpoint number Typing "info breakpoints" without ant option will display all breakpoint information. Example: ``` (sample.rb:1) info breakpoints Num Type Enb What 1 breakpoint y at sample.rb:3 -> file name,line number 2 breakpoint n in Sample_class:sample_class_method -> [class:]method name 3 breakpoint y in sample_global_method ``` Displaying the specified breakpoint number: ``` (foo.rb:1) info breakpoints 1 3 Num Type Enb What 1 breakpoint y at sample.rb:3 3 breakpoint y in sample_global_method ``` #### List Command To display the code of the source file. Usage: ``` list [filename:]first[,last] l [filename]:first[,last] ``` first: the opening row number last : the closing row number When you specify the `first`, but not the `last` option, you will receive 10 rows. When you do not specify both the `first` and `last` options, you will receive the next 10 rows. Example: ``` Specifying file name and first row number sample.rb:1) list sample2.rb:5 ``` Specifying the file name and the first and last row number: ``` (sample.rb:1) list sample2.rb:6,7 ``` #### Print Command Evaluating the string as source code and printing the value. Usage: ``` print [expr] p [expr] ``` expr: expression The expression is mandatory. The displayed expressions will be serially ordered from 1. If an exception occurs, the exception information will be displayed and the debugging will be continued. Example: ``` (sample.rb:1) print 1+2 $1 = 3 (sample.rb:1) print self $2 = main ``` Below is the case of the exception: ``` (sample.rb:1) print (1+2 $1 = SyntaxError: line 1: syntax error, unexpected $end, expecting ')' ``` #### Quit Command Quitting the debugger. Usage: ``` quit q ``` #### Run Command Running the program and stopping at the first breakpoint. Usage: ``` run r ``` #### Step Command This will run the program step by step. When the method and the block are invoked, the program will be stop at the first row. The program, which is developed in C, will be ignored. mruby-1.2.0+20160315+git4f20d58a/doc/guides/gc-arena-howto.md000066400000000000000000000151311267140355100225710ustar00rootroot00000000000000# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()` _This is an English translation of [Matz's blog post][matz blog post] written in Japanese._ _Some parts are updated to reflect recent changes._ [matz blog post]: http://www.rubyist.net/~matz/20130731.html When you are extending mruby using C language, you may encounter mysterious "arena overflow error" or memory leak or very slow execution speed. This is an error indicating overflow of "GC arena" implementing "conservative GC". GC (garbage collector) must ensure that object is "alive", in other words, that it is referenced by somewhere from program. This can be determined by checking if the object can be directly or indirectly referenced by root. The local variables, global variables and constants etc are root. If program execution is performed inside mruby VM, there is nothing to worry about because GC can access all roots owned by VM. The problem arises when executing C functions. The object referenced by C variable is also "alive", but mruby GC cannot aware of this, so it might mistakenly recognize the objects referenced by only C variables as dead. This can be a fatal bug if the GC tries to collect a live object. In CRuby, we scan C stack area, and use C variable as root to check whether object is alive or not. Of course, because we are accessing C stack just as memory region, we never know it is an integer or a pointer. We workaround this by assuming that if it looks like a pointer, then assume it as a pointer. We call it "conservative". By the way, CRuby's "conservative GC" has some problems. The biggest problem is we have no way to access to the stack area in portable way. Therefore, we cannot use this method if we'd like to implement highly portable runtime, like mruby. So we came up with an another plan to implement "conservative GC" in mruby. Again, the problem is when an object which was created in C function, becomes no longer referenced in the Ruby world, and cannot be treated as garbage. In mruby, we recognize all objects created in C function are alive. Then we have no problem such as confusing a live object as dead. This means that because we cannot collect truly dead object, we may lose efficiency, but as a trade-off the GC itself is highly portable. We can say goodbye to the problem that GC deletes live objects due to optimization which sometimes occurs in CRuby. According to this idea, we have a table, called "GC arena", which remembers objects created in C function. The arena is stack structure, when C function execution is returned to mruby VM, all objects registered in the arena are popped. This works very well, but can cause another problem: "arena overflow error" or memory leak. As of this writing, mruby automatically extend arena to remember objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in doc/guides/mrbconf.md). If you create many objects in C functions, memory usage will increase, since GC never kick in. This memory usage may look like memory leak, but will also make execution slower as more memory will need to be allocated. With the build time configuration, you can limit the maximum size of arena (e.g., 100). Then if you create many objects, arena overflows, thus you will get an "arena overflow error". To workaround these problems, we have `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` functions. `int mrb_gc_arena_save(mrb)` returns the current position of the stack top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the stack top position to back to given `idx`. We can use them like this: ```c int arena_idx = mrb_gc_arena_save(mrb); // ...create objects... mrb_gc_arena_restore(mrb, arena_idx); ``` In mruby, C function calls are surrounded by this save/restore, but we can further optimize memory usage by surrounding save/restore, and can avoid creating arena overflow bugs. Let's take a real example. Here is the source code of `Array#inspect`: ```c static mrb_value inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) { mrb_int i; mrb_value s, arystr; char head[] = { '[' }; char sep[] = { ',', ' ' }; char tail[] = { ']' }; /* check recursive */ for(i=0; i 0) { mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep)); } if (mrb_array_p(RARRAY_PTR(ary)[i])) { s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); } else { s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); } mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); mrb_gc_arena_restore(mrb, ai); } mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail)); mrb_ary_pop(mrb, list); return arystr; } ``` This is a real example, so a little bit complicated, but bear with me. The essence of `Array#inspect` is that after stringifying each element of array using `inspect` method, we join them together so that we can get `inspect` representation of the entire array. After the `inspect` representation is created, we no longer require the individual string representation. This means that we don't have to register these temporal objects into GC arena. Therefore, in order to keep the arena size small; the `ary_inspect()` function will do the following: * save the position of the stack top using `mrb_gc_arena_save()`. * get `inspect` representation of each element. * append it to the constructing entire `inspect` representation of array. * restore stack top position using `mrb_gc_arena_restore()`. Please note that the final `inspect` representation of entire array was created before the call of `mrb_gc_arena_restore()`. Otherwise, required temporal object may be deleted by GC. We may have a usecase where after creating many temporal objects, we'd like to keep some of them. In this case, we cannot use the same idea in `ary_inspect()` like appending objects to existing one. Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we want to keep in the arena using `mrb_gc_protect(mrb, obj)`. Use `mrb_gc_protect()` with caution because it could also lead to an "arena overflow error". We must also mention that when `mrb_funcall` is called in top level, the return value is also registered to GC arena, so repeated use of `mrb_funcall` may eventually lead to an "arena overflow error". Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of `mrb_gc_protect()` to workaround this. mruby-1.2.0+20160315+git4f20d58a/doc/guides/mrbconf.md000066400000000000000000000122161267140355100214050ustar00rootroot00000000000000# mruby configuration macros. ## How to use these macros. You can use mrbconfs with following ways: * Write them in `mrbconf.h`. * Using compiler flags is preferred when building a cross binaries or multiple mruby binaries since it's easier to use different mrbconf per each `MRuby::Build`. * Most flags can be enabled by just commenting in. * Pass them as compiler flags. * Make sure you pass the same flags to all compilers since some mrbconf(e.g., `MRB_GC_FIXED_ARENA`) changes `struct` layout and cause memory access error when C and other language(e.g., C++) is mixed. ## stdio setting. `MRB_DISABLE_STDIO` * When defined `` functions won't be used. * Some features will be disabled when this is enabled: * `mrb_irep` load/dump from/to file. * Compiling mruby script from file. * Printing features in **src/print.c**. ## Debug macros. `MRB_ENABLE_DEBUG_HOOK` * When defined code fetch hook and debug OP hook will be enabled. * When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`. * Fetch hook will be called before any OP. * Debug OP hook will be called when dispatching `OP_DEBUG`. `MRB_DEBUG` * When defined `mrb_assert*` macro will be defined with macros from ``. * Could be enabled via `enable_debug` method of `MRuby::Build`. ## Stack configuration `MRB_STACK_EXTEND_DOUBLING` * If defined doubles the stack size when extending it. * Else extends stack with `MRB_STACK_GROWTH`. `MRB_STACK_GROWTH` * Default value is `128`. * Used in stack extending. * Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined. `MRB_STACK_MAX` * Default value is `0x40000 - MRB_STACK_GROWTH`. * Raises `RuntimeError` when stack size exceeds this value. ## Primitive type configuration. `MRB_USE_FLOAT` * When defined single precision floating point type(C type `float`) is used as `mrb_float`. * Else double precision floating point type(C type `double`) is used as `mrb_float`. `MRB_INT16` * When defined `int16_t` will be defined as `mrb_int`. * Conflicts with `MRB_INT64`. `MRB_INT64` * When defined `int64_t` will be defined as `mrb_int`. * Conflicts with `MRB_INT16`. * When `MRB_INT16` or `MRB_INT64` isn't defined `int`(most of the times 32-bit integer) will be defined as `mrb_int`. ## Garbage collector configuration. `MRB_GC_STRESS` * When defined full GC is emitted per each `RBasic` allocation. * Mainly used in memory manager debugging. `MRB_GC_TURN_OFF_GENERATIONAL` * When defined turns generational GC by default. `MRB_GC_FIXED_ARENA` * When defined used fixed size GC arena. * Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`. * Useful tracking unnecessary mruby object allocation. `MRB_GC_ARENA_SIZE` * Default value is `100`. * Ignored when `MRB_GC_FIXED_ARENA` isn't defined. * Defines fixed GC arena size. `MRB_HEAP_PAGE_SIZE` * Defines value is `1024`. * Specifies number of `RBasic` per each heap page. ## Memory pool configuration. `POOL_ALIGNMENT` * Default value is `4`. * If you're allocating data types that requires alignment more than default value define the largest value of required alignment. `POOL_PAGE_SIZE` * Default value is `16000`. * Specifies page size of pool page. * Smaller the value is increases memory overhead. ## State atexit configuration. `MRB_FIXED_STATE_ATEXIT_STACK` * If defined enables fixed size `mrb_state` atexit stack. * Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds `MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value. `MRB_FIXED_STATE_ATEXIT_STACK_SIZE` * Default value is `5`. * If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored. ## `mrb_value` configuration. `MRB_ENDIAN_BIG` * If defined compiles mruby for big endian machines. * Used in `MRB_NAN_BOXING`. * Some mrbgem use this mrbconf. `MRB_NAN_BOXING` * If defined represent `mrb_value` in boxed `double`. * Conflicts with `MRB_USE_FLOAT`. `MRB_WORD_BOXING` * If defined represent `mrb_value` as a word. * If defined `Float` will be a mruby object with `RBasic`. ## Instance variable configuration. `MRB_USE_IV_SEGLIST` * If defined enable segmented list in instance variable table instead of khash. * Segmented list is a linked list of key and value segments. * It will linear search instead of hash search. `MRB_SEGMENT_SIZE` * Default value is `4`. * Specifies size of each segment in segment list. * Ignored when `MRB_USE_IV_SEGLIST` isn't defined. `MRB_IVHASH_INIT_SIZE` * Default value is `8`. * Specifies initial size for instance variable table. * Ignored when `MRB_USE_IV_SEGLIST` is defined. ## Other configuration. `MRB_UTF8_STRING` * Adds UTF-8 encoding support to character-oriented String instance methods. * If it isn't defined, they only support the US-ASCII encoding. `MRB_FUNCALL_ARGC_MAX` * Default value is `16`. * Specifies 4th argument(`argc`) max value of `mrb_funcall`. * Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`. `KHASH_DEFAULT_SIZE` * Default value is `32`. * Specifies default size of khash table bucket. * Used in `kh_init_ ## name` function. `MRB_STR_BUF_MIN_SIZE` * Default value is `128`. * Specifies initial capacity of `RString` created by `mrb_str_buf_new` function.. mruby-1.2.0+20160315+git4f20d58a/doc/guides/mrbgems.md000066400000000000000000000260621267140355100214170ustar00rootroot00000000000000# mrbgems mrbgems is a library manager to integrate C and Ruby extension in an easy and standardised way into mruby. ## Usage By default mrbgems is currently deactivated. As soon as you add a GEM to your build configuration (i.e. *build_config.rb*), mrbgems will be activated and the extension integrated. To add a GEM into the *build_config.rb* add the following line for example: ```ruby conf.gem '/path/to/your/gem/dir' ``` You can also use a relative path which would be relative from the mruby root: ```ruby conf.gem 'examples/mrbgems/ruby_extension_example' ``` A remote GIT repository location for a GEM is also supported: ```ruby conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master' conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master' ``` To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option: ```ruby conf.gem :mgem => 'mruby-yaml' conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted ``` If there is missing dependencies, mrbgem dependencies solver will reference mrbgem from core or mgem-list. To pull all gems from remote GIT repository on build, call ```./minirake -p```, or ```./minirake --pull-gems```. NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version. ## GemBox There are instances when you wish to add a collection of mrbgems into mruby at once, or be able to substitute mrbgems based on configuration, without having to add each gem to the *build_config.rb* file. A packaged collection of mrbgems is called a GemBox. A GemBox is a file that contains a list of mrbgems to load into mruby, in the same format as if you were adding them to *build_config.rb* via `config.gem`, but wrapped in an `MRuby::GemBox` object. GemBoxes are loaded into mruby via `config.gembox 'boxname'`. Below we have created a GemBox containing *mruby-time* and *mrbgems-example*: ```ruby MRuby::GemBox.new do |conf| conf.gem "#{root}/mrbgems/mruby-time" conf.gem :github => 'masuidrive/mrbgems-example' end ``` As mentioned, the GemBox uses the same conventions as `MRuby::Build`. The GemBox must be saved with a *.gembox* extension inside the *mrbgems* directory to to be picked up by mruby. To use this example GemBox, we save it as `custom.gembox` inside the *mrbgems* directory in mruby, and add the following to our *build_config.rb* file inside the build block: ```ruby conf.gembox 'custom' ``` This will cause the *custom* GemBox to be read in during the build process, adding *mruby-time* and *mrbgems-example* to the build. If you want, you can put GemBox outside of mruby directory. In that case you must specify an absolute path like below. ```ruby conf.gembox "#{ENV["HOME"]}/mygemboxes/custom" ``` There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox) and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox contains several core components of mruby, and [full-core](../../mrbgems/full-core.gembox) contains every gem found in the *mrbgems* directory. ## GEM Structure The maximal GEM structure looks like this: +- GEM_NAME <- Name of GEM | +- include/ <- Header for Ruby extension (will exported) | +- mrblib/ <- Source for Ruby extension | +- src/ <- Source for C extension | +- test/ <- Test code (Ruby) | +- mrbgem.rake <- GEM Specification | +- README.md <- Readme for GEM The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src* contains C/C++ files to extend mruby. The folder *include* contains C/C++ header files. The folder *test* contains C/C++ and pure Ruby files for testing purposes which will be used by `mrbtest`. *mrbgem.rake* contains the specification to compile C and Ruby files. *README.md* is a short description of your GEM. ## Build process mrbgems expects a specification file called *mrbgem.rake* inside of your GEM directory. A typical GEM specification could look like this for example: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Example mrbgem using C and ruby' end ``` The mrbgems build process will use this specification to compile Object and Ruby files. The compilation results will be added to *lib/libmruby.a*. This file exposes the GEM functionality to tools like `mruby` and `mirb`. The following properties can be set inside of your `MRuby::Gem::Specification` for information purpose: * `spec.license` or `spec.licenses` (A single license or a list of them under which this GEM is licensed) * `spec.author` or `spec.authors` (Developer name or a list of them) * `spec.version` (Current version) * `spec.description` (Detailed description) * `spec.summary` * One line short description of mrbgem. * Printed in build summary of rake when set. * `spec.homepage` (Homepage) * `spec.requirements` (External requirements as information for user) The `license` and `author` properties are required in every GEM! In case your GEM is depending on other GEMs please use `spec.add_dependency(gem, *requirements[, default_get_info])` like: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add GEM dependency mruby-parser. # The version must be between 1.0.0 and 1.5.2 . spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2') # Use any version of mruby-uv from github. spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv') # Use latest mruby-onig-regexp from github. (version requirements can be omitted) spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp') end ``` The version requirements and default gem information are optional. Version requirement supports following operators: * '=': is equal * '!=': is not equal * '>': is greater * '<': is lesser * '>=': is equal or greater * '<=': is equal or lesser * '~>': is equal or greater and is lesser than the next major version * example 1: '~> 2.2.2' means '>= 2.2.2' and '< 2.3.0' * example 2: '~> 2.2' means '>= 2.2.0' and '< 3.0.0' When more than one version requirements is passed, the dependency must satisfy all of it. You can have default gem to use as depedency when it's not defined in *build_config.rb*. When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information. Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location. When a special version of depedency is required, use `MRuby::Build#gem` in *build_config.rb* to override default gem. If you have conflicting GEMs use the following method: * `spec.add_conflict(gem, *requirements)` * The `requirements` argument is same as in `add_dependency` method. like following code: ```ruby MRuby::Gem::Specification.new 'some-regexp-binding' do |spec| spec.license = 'BSD' spec.author = 'John Doe' spec.add_conflict 'mruby-onig-regexp', '> 0.0.0' spec.add_conflict 'mruby-hs-regexp' spec.add_conflict 'mruby-pcre-regexp' spec.add_conflict 'mruby-regexp-pcre' end ``` In case your GEM has more complex build requirements you can use the following options additionally inside of your GEM specification: * `spec.cc.flags` (C compiler flags) * `spec.cc.defines` (C compiler defines) * `spec.cc.include_paths` (C compiler include paths) * `spec.linker.flags` (Linker flags) * `spec.linker.libraries` (Linker libraries) * `spec.linker.library_paths` (Linker additional library path) * `spec.bins` (Generate binary file) * `spec.rbfiles` (Ruby files to compile) * `spec.objs` (Object files to compile) * `spec.test_rbfiles` (Ruby test files for integration into mrbtest) * `spec.test_objs` (Object test files for integration into mrbtest) * `spec.test_preload` (Initialization files for mrbtest) You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for compiler and linker. ### include_paths and dependency Your GEM can export include paths to another GEMs that depends on your GEM. By default, `/...absolute path.../{GEM_NAME}/include` will be exported. So it is recommended not to put GEM's local header files on include/. These exports are retroactive. For example: when B depends to C and A depends to B, A will get include paths exported by C. Exported include_paths are automatically appended to GEM local include_paths by Minirake. You can use `spec.export_include_paths` accessor if you want more complex build. ## C Extension mruby can be extended with C. This is possible by using the C API to integrate C libraries into mruby. ### Preconditions mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM *c_extension_example*, your initialisation method could look like this: ```C void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } ``` ### Finalize mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_final(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM *c_extension_example*, your finalizer method could look like this: ```C void mrb_c_extension_example_gem_final(mrb_state* mrb) { free(someone); } ``` ### Example +- c_extension_example/ | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C extension | +- mrbgem.rake <- GEM specification | +- README.md ## Ruby Extension mruby can be extended with pure Ruby. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the *mrblib* folder. ### Pre-Conditions none ### Example +- ruby_extension_example/ | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- test/ | | | +- example.rb <- Test code for Ruby extension | +- mrbgem.rake <- GEM specification | +- README.md ## C and Ruby Extension mruby can be extended with C and Ruby at the same time. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the *mrblib* folder and all C files into the *src* folder. mruby codes under *mrblib* directory would be executed after gem init C function is called. Make sure *mruby script* depends on *C code* and *C code* doesn't depend on *mruby script*. ### Pre-Conditions See C and Ruby example. ### Example +- c_and_ruby_extension_example/ | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C and Ruby extension | +- mrbgem.rake <- GEM specification | +- README.md mruby-1.2.0+20160315+git4f20d58a/doc/limitations.md000066400000000000000000000074621267140355100210420ustar00rootroot00000000000000# Limitations and Differences The philosophy of mruby is to be a lightweight implementation of the Ruby ISO standard. These two objectives are partially contradicting. Ruby is an expressive language with complex implementation details which are difficult to implement in a lightweight manner. To cope with this, limitations to the "Ruby Compatibility" are defined. This document is collecting these limitations. ## Integrity This document does not contain a complete list of limitations. Please help to improve it by submitting your findings. ## ```1/2``` gives ```0.5``` Since mruby does not have ```Bignum```, bigger integers are represented by ```Float``` numbers. To enhance interoperability between ```Float``` and ```Float```, mruby provides ``Float#upto``` and other iterating methods for ```Float`` class. As a side effect, ```1/2``` gives ```0.5``` not ```0```. ## ```Array``` passed to ```puts``` Passing an Array to ```puts``` results in different output. ```ruby puts [1,2,3] ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` 1 2 3 ``` #### mruby [1.2.0 (2015-11-17)] ``` [1, 2, 3] ``` ## ```Kernel.raise``` in rescue clause ```Kernel.raise``` without arguments does not raise the current exception within a rescue clause. ```ruby begin 1 / 0 rescue raise end ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ```ZeroDivisionError``` is raised. #### mruby [1.2.0 (2015-11-17)] No exception is raised. ## Check of infinite recursion mruby does not check infinite recursion across C extensions. ```ruby def test; eval 'test'; end; test ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ```SystemStackError``` is raised. #### mruby [1.2.0 (2015-11-17)] Segmentation fault. ## Fiber execution can't cross C function boundary mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This results in the consequence that you can't switch context within C functions. Only exception is ```mrb_fiber_yield``` at return. ## ```Array``` does not support instance variables To reduce memory consumption ```Array``` does not support instance variables. ```ruby class Liste < Array def initialize(str = nil) @feld = str end end p Liste.new "foobar" ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` [] ``` #### mruby [1.2.0 (2015-11-17)] ```ArgumentError``` is raised. ## Method visibility For simplicity reasons no method visibility (public/private/protected) is supported. ```ruby class VisibleTest def public_method; end private def private_method; end end p VisibleTest.new.respond_to?(:private_method, false) p VisibleTest.new.respond_to?(:private_method, true) ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` false true ``` #### mruby [1.2.0 (2015-11-17)] ``` true true ``` ## defined? The ```defined?``` keyword is considered to complex to be fully implemented. It is recommended to use ```const_defined?``` and other reflection methods instead. ```ruby defined?(Foo) ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` nil ``` #### mruby [1.2.0 (2015-11-17)] ```NameError``` is raised. ## ```alias``` on global variables Aliasing a global variable works in CRuby but is not part of the ISO standard. ```ruby alias $a $__a__ ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` nil ``` #### mruby [1.2.0 (2015-11-17)] Syntax error ## Operator modification An operator can't be overwritten by the user. ```ruby class String def + end end 'a' + 'b' ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ```ArgumentError``` is raised. The re-defined ```+``` operator does not accept any arguments. #### mruby [1.2.0 (2015-11-17)] ``` 'ab' ``` Behavior of the operator wasn't changed. ## ```Kernel.binding``` missing ```Kernel.binding``` is not implemented as it is not in the ISO standard. mruby-1.2.0+20160315+git4f20d58a/examples/000077500000000000000000000000001267140355100172245ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/000077500000000000000000000000001267140355100206605ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/000077500000000000000000000000001267140355100265745ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/README.md000066400000000000000000000001501267140355100300470ustar00rootroot00000000000000C and Ruby Extension Example ========= This is an example gem which implements a C and Ruby extension. mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake000066400000000000000000000015111267140355100307070ustar00rootroot00000000000000MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/mrblib/000077500000000000000000000000001267140355100300435ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb000066400000000000000000000001351267140355100320220ustar00rootroot00000000000000module CRubyExtension def CRubyExtension.ruby_method puts "A Ruby Extension" end end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/src/000077500000000000000000000000001267140355100273635ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/src/example.c000066400000000000000000000007271267140355100311700ustar00rootroot00000000000000#include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { puts("A C Extension"); return self; } void mrb_c_and_ruby_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CRubyExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_and_ruby_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/test/000077500000000000000000000000001267140355100275535ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_and_ruby_extension_example/test/example.rb000066400000000000000000000002621267140355100315330ustar00rootroot00000000000000assert('C and Ruby Extension Example 1') do CRubyExtension.respond_to? :c_method end assert('C and Ruby Extension Example 2') do CRubyExtension.respond_to? :ruby_method end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/000077500000000000000000000000001267140355100247115ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/README.md000066400000000000000000000001261267140355100261670ustar00rootroot00000000000000C Extension Example ========= This is an example gem which implements a C extension. mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/mrbgem.rake000066400000000000000000000015021267140355100270240ustar00rootroot00000000000000MRuby::Gem::Specification.new('c_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '-g' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/src/000077500000000000000000000000001267140355100255005ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/src/example.c000066400000000000000000000007011267140355100272750ustar00rootroot00000000000000#include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { puts("A C Extension"); return self; } void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/test/000077500000000000000000000000001267140355100256705ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/test/example.c000066400000000000000000000001541267140355100274670ustar00rootroot00000000000000#include void mrb_c_extension_example_gem_test(mrb_state *mrb) { /* test initializer in C */ } mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/c_extension_example/test/example.rb000066400000000000000000000001101267140355100276400ustar00rootroot00000000000000assert('C Extension Example') do CExtension.respond_to? :c_method end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/000077500000000000000000000000001267140355100254505ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/README.md000066400000000000000000000001461267140355100267300ustar00rootroot00000000000000Pure Ruby Extension Example ========= This is an example gem which implements a pure Ruby extension. mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/mrbgem.rake000066400000000000000000000016011267140355100275630ustar00rootroot00000000000000MRuby::Gem::Specification.new('ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' spec.add_dependency('mruby-print', :core => 'mruby-print') # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/mrblib/000077500000000000000000000000001267140355100267175ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/mrblib/example.rb000066400000000000000000000001321267140355100306730ustar00rootroot00000000000000class RubyExtension def RubyExtension.ruby_method puts "A Ruby Extension" end end mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/test/000077500000000000000000000000001267140355100264275ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/mrbgems/ruby_extension_example/test/example.rb000066400000000000000000000001211267140355100304010ustar00rootroot00000000000000assert('Ruby Extension Example') do RubyExtension.respond_to? :ruby_method end mruby-1.2.0+20160315+git4f20d58a/examples/targets/000077500000000000000000000000001267140355100206755ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/examples/targets/build_config_ArduinoDue.rb000066400000000000000000000060061267140355100257670ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Arduino Due # http://arduino.cc/en/Main/ArduinoBoardDue # # Requires Arduino IDE >= 1.5 MRuby::CrossBuild.new("ArduinoDue") do |conf| toolchain :gcc # Mac OS X, Arduino IDE <= 1.5.6 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java' # Mac OS X, Arduino IDE >= 1.5.7 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java' # GNU Linux ARDUINO_PATH = '/opt/arduino' # Arduino IDE <= 1.5.6 BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/bin" # Arduino IDE >= 1.5.7 # BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin" SAM_PATH = "#{ARDUINO_PATH}/hardware/arduino/sam" TARGET_PATH = "#{SAM_PATH}/variants/arduino_due_x" conf.cc do |cc| cc.command = "#{BIN_PATH}/arm-none-eabi-gcc" cc.include_paths << ["#{SAM_PATH}/system/libsam", "#{SAM_PATH}/system/CMSIS/CMSIS/Include/", "#{SAM_PATH}/system/CMSIS/Device/ATMEL/", "#{SAM_PATH}/cores/arduino", "#{SAM_PATH}/libraries","#{TARGET_PATH}"] cc.flags = %w(-g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=156 -DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Due") cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(MRB_USE_IV_SEGLIST) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w(-fno-rtti -fno-exceptions) cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{BIN_PATH}/arm-none-eabi-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end mruby-1.2.0+20160315+git4f20d58a/examples/targets/build_config_IntelGalileo.rb000066400000000000000000000067701267140355100263100ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Intel Galileo on Arduino environment # http://arduino.cc/en/ArduinoCertified/IntelGalileo # # Requires Arduino IDE for Intel Galileo MRuby::CrossBuild.new("Galileo") do |conf| toolchain :gcc # Mac OS X # Assume you renamed Arduino.app to Arduino_Galileo.app GALILEO_ARDUINO_PATH = '/Applications/Arduino_Galileo.app/Contents/Resources/Java' # GNU Linux #ARDUINO_GALILEO_PATH = '/opt/arduino' GALILEO_BIN_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i386-pokysdk-darwin/usr/bin/i586-poky-linux-uclibc" GALILEO_SYSROOT = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc" GALILEO_X86_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/arduino/x86" conf.cc do |cc| cc.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-gcc" cc.include_paths << ["#{GALILEO_X86_PATH}/cores/arduino", "#{GALILEO_X86_PATH}/variants/galileo_fab_d"] cc.flags = %w(-m32 -march=i586 -c -g -Os -w -ffunction-sections -fdata-sections -MMD -DARDUINO=153) cc.flags << "--sysroot=#{GALILEO_SYSROOT}" cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" end conf.cxx do |cxx| cxx.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++" cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++/i586-poky-linux-uclibc" cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end conf.linker do |linker| linker.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" linker.flags = %w(-m32 -march=i586) linker.flags << "--sysroot=#{GALILEO_SYSROOT}" linker.flags << %w(-Os -Wl,--gc-sections) linker.libraries = %w(m pthread) end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #official mrbgems conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-time" conf.gem :core => "mruby-struct" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-string-ext" conf.gem :core => "mruby-numeric-ext" conf.gem :core => "mruby-array-ext" conf.gem :core => "mruby-hash-ext" conf.gem :core => "mruby-range-ext" conf.gem :core => "mruby-proc-ext" conf.gem :core => "mruby-symbol-ext" conf.gem :core => "mruby-random" conf.gem :core => "mruby-object-ext" conf.gem :core => "mruby-objectspace" conf.gem :core => "mruby-fiber" conf.gem :core => "mruby-toplevel-ext" #lightweigh regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" do |g| # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Wire" # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Servo" #enable unsupported Servo class # g.cxx.defines << "MRUBY_ARDUINO_GALILEO_ENABLE_SERVO" #end end mruby-1.2.0+20160315+git4f20d58a/examples/targets/build_config_chipKITMax32.rb000066400000000000000000000051361267140355100260410ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Digilent chipKIT Max32 # http://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-MAX32 # # Requires MPIDE (https://github.com/chipKIT32/chipKIT32-MAX) # # This configuration is based on @kyab's version # http://d.hatena.ne.jp/kyab/20130201 MRuby::CrossBuild.new("chipKITMax32") do |conf| toolchain :gcc # Mac OS X # MPIDE_PATH = '/Applications/Mpide.app/Contents/Resources/Java' # GNU Linux MPIDE_PATH = '/opt/mpide-0023-linux-20120903' PIC32_PATH = "#{MPIDE_PATH}/hardware/pic32" conf.cc do |cc| cc.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-gcc" cc.include_paths << ["#{PIC32_PATH}/cores/pic32", "#{PIC32_PATH}/variants/Max32", "#{PIC32_PATH}/libraries"] cc.flags = %w(-O2 -mno-smart-io -w -ffunction-sections -fdata-sections -g -mdebugger -Wcast-align -fno-short-double -mprocessor=32MX795F512L -DF_CPU=80000000L -DARDUINO=23 -D_BOARD_MEGA_ -DMPIDEVER=0x01000202 -DMPIDE=23) cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(MRB_USE_IV_SEGLIST) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end #no executables conf.bins = [] #do not build test executable conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end mruby-1.2.0+20160315+git4f20d58a/include/000077500000000000000000000000001267140355100170315ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/include/mrbconf.h000066400000000000000000000051661267140355100206400ustar00rootroot00000000000000/* ** mrbconf.h - mruby core configuration ** ** See Copyright Notice in mruby.h */ #ifndef MRUBYCONF_H #define MRUBYCONF_H /* configuration options: */ /* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */ //#define MRB_USE_FLOAT /* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */ //#define MRB_INT16 /* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */ //#define MRB_INT64 /* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT */ //#define MRB_NAN_BOXING /* define on big endian machines; used by MRB_NAN_BOXING */ //#define MRB_ENDIAN_BIG /* represent mrb_value as a word (natural unit of data for the processor) */ //#define MRB_WORD_BOXING /* string class to handle UTF-8 encoding */ //#define MRB_UTF8_STRING /* argv max size in mrb_funcall */ //#define MRB_FUNCALL_ARGC_MAX 16 /* number of object per heap page */ //#define MRB_HEAP_PAGE_SIZE 1024 /* use segmented list for IV table */ //#define MRB_USE_IV_SEGLIST /* initial size for IV khash; ignored when MRB_USE_IV_SEGLIST is set */ //#define MRB_IVHASH_INIT_SIZE 8 /* if _etext and _edata available, mruby can reduce memory used by symbols */ //#define MRB_USE_ETEXT_EDATA /* do not use __init_array_start to determine readonly data section; effective only when MRB_USE_ETEXT_EDATA is defined */ //#define MRB_NO_INIT_ARRAY_START /* turn off generational GC by default */ //#define MRB_GC_TURN_OFF_GENERATIONAL /* default size of khash table bucket */ //#define KHASH_DEFAULT_SIZE 32 /* allocated memory address alignment */ //#define POOL_ALIGNMENT 4 /* page size of memory pool */ //#define POOL_PAGE_SIZE 16000 /* initial minimum size for string buffer */ //#define MRB_STR_BUF_MIN_SIZE 128 /* arena size */ //#define MRB_GC_ARENA_SIZE 100 /* fixed size GC arena */ //#define MRB_GC_FIXED_ARENA /* state atexit stack size */ //#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 /* fixed size state atexit stack */ //#define MRB_FIXED_STATE_ATEXIT_STACK /* -DMRB_DISABLE_XXXX to drop following features */ //#define MRB_DISABLE_STDIO /* use of stdio */ /* -DMRB_ENABLE_XXXX to enable following features */ //#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */ /* end of configuration */ /* define MRB_DISABLE_XXXX from DISABLE_XXX (for compatibility) */ #ifdef DISABLE_STDIO #define MRB_DISABLE_STDIO #endif /* define MRB_ENABLE_XXXX from ENABLE_XXX (for compatibility) */ #ifdef ENABLE_DEBUG #define MRB_ENABLE_DEBUG_HOOK #endif #ifndef MRB_DISABLE_STDIO # include #endif #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #endif /* MRUBYCONF_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby.h000066400000000000000000001163101267140355100203420ustar00rootroot00000000000000/* ** mruby - An embeddable Ruby implementation ** ** Copyright (c) mruby developers 2010-2016 ** ** 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 ] */ #ifndef MRUBY_H #define MRUBY_H #include #include #include #include "mrbconf.h" #include "mruby/common.h" #include #include #include /** * MRuby C API entry point */ MRB_BEGIN_DECL typedef uint32_t mrb_code; /** * Required arguments signature type. */ typedef uint32_t mrb_aspec; struct mrb_irep; struct mrb_state; /** * Function pointer type of custom allocator used in @see mrb_open_allocf. * * The function pointing it must behave similarly as realloc except: * - If ptr is NULL it must allocate new space. * - If s is NULL, ptr must be freed. * * See @see mrb_default_allocf for the default implementation. */ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud); #ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE #define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 #endif typedef struct { mrb_sym mid; struct RProc *proc; mrb_value *stackent; int nregs; int ridx; int eidx; struct REnv *env; mrb_code *pc; /* return address */ mrb_code *err; /* error position */ int argc; int acc; struct RClass *target_class; } mrb_callinfo; enum mrb_fiber_state { MRB_FIBER_CREATED = 0, MRB_FIBER_RUNNING, MRB_FIBER_RESUMED, MRB_FIBER_SUSPENDED, MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, }; struct mrb_context { struct mrb_context *prev; mrb_value *stack; /* stack of virtual machine */ mrb_value *stbase, *stend; mrb_callinfo *ci; mrb_callinfo *cibase, *ciend; mrb_code **rescue; /* exception handler stack */ int rsize; struct RProc **ensure; /* ensure handler stack */ int esize; enum mrb_fiber_state status; mrb_bool vmexec; struct RFiber *fib; }; struct mrb_jmpbuf; typedef struct { const char *filename; int lineno; struct RClass *klass; char sep; mrb_sym method_id; } mrb_backtrace_entry; typedef void (*mrb_atexit_func)(struct mrb_state*); #define MRB_STATE_NO_REGEXP 1 #define MRB_STATE_REGEXP 2 typedef struct mrb_state { struct mrb_jmpbuf *jmp; uint32_t flags; mrb_allocf allocf; /* memory allocation function */ void *allocf_ud; /* auxiliary data of allocf */ struct mrb_context *c; struct mrb_context *root_c; struct RObject *exc; /* exception */ struct { struct RObject *exc; int n; int n_allocated; mrb_backtrace_entry *entries; } backtrace; struct iv_tbl *globals; /* global variable table */ struct RObject *top_self; struct RClass *object_class; /* Object class */ struct RClass *class_class; struct RClass *module_class; struct RClass *proc_class; struct RClass *string_class; struct RClass *array_class; struct RClass *hash_class; struct RClass *float_class; struct RClass *fixnum_class; struct RClass *true_class; struct RClass *false_class; struct RClass *nil_class; struct RClass *symbol_class; struct RClass *kernel_module; struct alloca_header *mems; mrb_gc gc; mrb_sym symidx; struct kh_n2s *name2sym; /* symbol hash */ struct symbol_name *symtbl; /* symbol table */ size_t symcapa; #ifdef MRB_ENABLE_DEBUG_HOOK void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs); void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs); #endif struct RClass *eException_class; struct RClass *eStandardError_class; struct RObject *nomem_err; /* pre-allocated NoMemoryError */ void *ud; /* auxiliary data */ #ifdef MRB_FIXED_STATE_ATEXIT_STACK mrb_atexit_func atexit_stack[MRB_FIXED_STATE_ATEXIT_STACK_SIZE]; #else mrb_atexit_func *atexit_stack; #endif mrb_int atexit_stack_len; } mrb_state; typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value); /** * Defines a new class. * * If you're creating a gem it may look something like this: * * !!!c * void mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); * } * * void mrb_example_gem_final(mrb_state* mrb) { * //free(TheAnimals); * } * * @param [mrb_state *] mrb The current mruby state. * @param [const char *] name The name of the defined class. * @param [struct RClass *] super The new class parent. * @return [struct RClass *] Reference to the newly defined class. * @see mrb_define_class_under */ MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super); /** * Defines a new module. * * @param [mrb_state *] mrb_state* The current mruby state. * @param [const char *] char* The name of the module. * @return [struct RClass *] Reference to the newly defined module. */ MRB_API struct RClass *mrb_define_module(mrb_state *, const char*); MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value); /** * Include a module in another class or module. * Equivalent to: * * module B * include A * end * @param [mrb_state *] mrb_state* The current mruby state. * @param [struct RClass *] RClass* A reference to module or a class. * @param [struct RClass *] RClass* A reference to the module to be included. */ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*); /** * Prepends a module in another class or module. * * Equivalent to: * module B * prepend A * end * @param [mrb_state *] mrb_state* The current mruby state. * @param [struct RClass *] RClass* A reference to module or a class. * @param [struct RClass *] RClass* A reference to the module to be prepended. */ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*); /** * Defines a global function in ruby. * * If you're creating a gem it may look something like this * * Example: * * !!!c * mrb_value example_method(mrb_state* mrb, mrb_value self) * { * puts("Executing example command!"); * return self; * } * * void mrb_example_gem_init(mrb_state* mrb) * { * mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE()); * } * * @param [mrb_state *] mrb The MRuby state reference. * @param [struct RClass *] cla The class pointer where the method will be defined. * @param [const char *] name The name of the method being defined. * @param [mrb_func_t] func The function pointer to the method definition. * @param [mrb_aspec] aspec The method parameters declaration. */ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec); /** * Defines a class method. * * Example: * * # Ruby style * class Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_class(mrb, "Foo", mrb->object_class); * mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* The class where the class method will be defined. * @param [const char *] char* The name of the class method being defined. * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition. * @param [mrb_aspec] mrb_aspec The method parameters declaration. */ MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec); /** * Defines a module fuction. * * Example: * * # Ruby style * module Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_module(mrb, "Foo"); * mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* The module where the module function will be defined. * @param [const char *] char* The name of the module function being defined. * @param [mrb_func_t] mrb_func_t The function pointer to the module function definition. * @param [mrb_aspec] mrb_aspec The method parameters declaration. */ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); /** * Defines a constant. * * Example: * * # Ruby style * class ExampleClass * AGE = 22 * end * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb){ * mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22)); * } * * mrb_value * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* A class or module the constant is defined in. * @param [const char *] name The name of the constant being defined. * @param [mrb_value] mrb_value The value for the constant. */ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value); /** * Undefines a method. * * Example: * * # Ruby style * * class ExampleClassA * def example_method * "example" * end * end * ExampleClassA.new.example_method # => example * * class ExampleClassB < ExampleClassA * undef_method :example_method * end * * ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError) * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_cstr(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class_a; * struct RClass *example_class_b; * struct RClass *example_class_c; * * example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class); * mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE()); * example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a); * example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b); * mrb_undef_method(mrb, example_class_c, "example_method"); * } * * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state*] mrb_state* The mruby state reference. * @param [struct RClass*] RClass* A class the method will be undefined from. * @param [const char*] constchar* The name of the method to be undefined. */ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*); /** * Undefine a class method. * Example: * * # Ruby style * class ExampleClass * def self.example_method * "example" * end * end * * ExampleClass.example_method * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_cstr(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE()); * mrb_undef_class_method(mrb, example_class, "example_method"); * } * * void * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state*] mrb_state* The mruby state reference. * @param [RClass*] RClass* A class the class method will be undefined from. * @param [constchar*] constchar* The name of the class method to be undefined. */ MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*); /** * Initialize a new object instace of c class. * * Example: * * # Ruby style * class ExampleClass * end * * p ExampleClass # => # * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_value obj; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end * obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new * mrb_p(mrb, obj); // => Kernel#p * } * @param [mrb_state*] mrb The current mruby state. * @param [RClass*] c Reference to the class of the new object. * @param [mrb_int] argc Number of arguments in argv * @param [const mrb_value *] argv Array of mrb_value to initialize the object * @return [mrb_value] The newly initialized object */ MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv); /** @see mrb_obj_new */ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c) { return mrb_obj_new(mrb,c,argc,argv); } MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv); /** * Creates a new instance of Class, Class. * * Example: * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * * mrb_value obj; * example_class = mrb_class_new(mrb, mrb->object_class); * obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#:0x9a94588> * mrb_p(mrb, obj); // => Kernel#p * } * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] super The super class or parent. * @return [struct RClass *] Reference to the new class. */ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super); /** * Creates a new module, Module. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_module; * * example_module = mrb_module_new(mrb); * } * * @param [mrb_state*] mrb The current mruby state. * @return [struct RClass *] Reference to the new module. */ MRB_API struct RClass * mrb_module_new(mrb_state *mrb); /** * Returns an mrb_bool. True if class was defined, and false if the class was not defined. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_bool cd; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * cd = mrb_class_defined(mrb, "ExampleClass"); * * // If mrb_class_defined returns 1 then puts "True" * // If mrb_class_defined returns 0 then puts "False" * if (cd == 1){ * puts("True"); * } * else { * puts("False"); * } * } * * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name A string representing the name of the class. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name); /** * Gets a class. * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name); /** * Gets a child class. * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer The name of the parent class. * @param [const char *] name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name); /** * Gets a module. * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name); /** * Gets a module defined under another module. * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer The name of the outer module. * @param [const char *] name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value); /** * Duplicate an object. * * Equivalent to: * Object#dup * @param [mrb_state*] mrb The current mruby state. * @param [mrb_value] obj Object to be duplicate. * @return [mrb_value] The newly duplicated object. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method); /** * Returns true if obj responds to the given method. If the method was defined for that * class it returns true, it returns false otherwise. * * Example: * # Ruby style * class ExampleClass * def example_method * end * end * * ExampleClass.new.respond_to?(:example_method) # => true * * // C style * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_sym mid; * mrb_bool obj_resp; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE()); * mid = mrb_intern_str(mrb, mrb_str_new_cstr(mrb, "example_method" )); * obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world) * * // If mrb_obj_respond_to returns 1 then puts "True" * // If mrb_obj_respond_to returns 0 then puts "False" * if (obj_resp == 1) { * puts("True"); * } * else if (obj_resp == 0) { * puts("False"); * } * } * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] c A reference to a class. * @param [mrb_sym] mid A symbol referencing a method id. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid); /** * Defines a new class under a given module * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer Reference to the module under which the new class will be defined * @param [const char *] name The name of the defined class * @param [struct RClass *] super The new class parent * @return [struct RClass *] Reference to the newly defined class * @see mrb_define_class */ MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super); MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name); /** * Function requires n arguments. * * @param n * The number of required arguments. */ #define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18) /** * Funtion takes n optional arguments * * @param n * The number of optional arguments. */ #define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13) /** * Funtion takes n1 mandatory arguments and n2 optional arguments * * @param n1 * The number of required arguments. * @param n2 * The number of optional arguments. */ #define MRB_ARGS_ARG(n1,n2) (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2)) /** rest argument */ #define MRB_ARGS_REST() ((mrb_aspec)(1 << 12)) /** required arguments after rest */ #define MRB_ARGS_POST(n) ((mrb_aspec)((n)&0x1f) << 7) /** keyword arguments (n of keys, kdict) */ #define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0))) /** * Function takes a block argument */ #define MRB_ARGS_BLOCK() ((mrb_aspec)1) /** * Function accepts any number of arguments */ #define MRB_ARGS_ANY() MRB_ARGS_REST() /** * Function accepts no arguments */ #define MRB_ARGS_NONE() ((mrb_aspec)0) /** * Format specifiers for {mrb_get_args} function * * Must be a C string composed of the following format specifiers: * * | char | Ruby type | C types | Notes | * |:----:|----------------|-------------------|----------------------------------------------------| * | `o` | {Object} | {mrb_value} | Could be used to retrieve any type of argument | * | `C` | {Class}/{Module} | {mrb_value} | | * | `S` | {String} | {mrb_value} | when `!` follows, the value may be `nil` | * | `A` | {Array} | {mrb_value} | when `!` follows, the value may be `nil` | * | `H` | {Hash} | {mrb_value} | when `!` follows, the value may be `nil` | * | `s` | {String} | char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` | * | `z` | {String} | char * | `NULL` terminated string; `z!` gives `NULL` for `nil` | * | `a` | {Array} | {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` | * | `f` | {Float} | {mrb_float} | | * | `i` | {Integer} | {mrb_int} | | * | `b` | boolean | {mrb_bool} | | * | `n` | {Symbol} | {mrb_sym} | | * | `&` | block | {mrb_value} | | * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array. | * | | | optional | | After this spec following specs would be optional. | * | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. | * * @see mrb_get_args */ typedef const char *mrb_args_format; /** * Retrieve arguments from mrb_state. * * When applicable, implicit conversions (such as `to_str`, `to_ary`, `to_hash`) are * applied to received arguments. * Used inside a function of mrb_func_t type. * * @param mrb The current MRuby state. * @param format [mrb_args_format] is a list of format specifiers * @param ... The passing variadic arguments must be a pointer of retrieving type. * @return the number of arguments retrieved. * @see mrb_args_format */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...); static inline mrb_sym mrb_get_mid(mrb_state *mrb) /* get method symbol */ { return mrb->c->ci->mid; } static inline int mrb_get_argc(mrb_state *mrb) /* get argc */ { return mrb->c->ci->argc; } /* `strlen` for character string literals (use with caution or `strlen` instead) Adjacent string literals are concatenated in C/C++ in translation phase 6. If `lit` is not one, the compiler will report a syntax error: MSVC: "error C2143: syntax error : missing ')' before 'string'" GCC: "error: expected ')' before string constant" */ #define mrb_strlen_lit(lit) (sizeof(lit "") - 1) /** * Call existing ruby functions. * * #include * #include * #include "mruby/compile.h" * * int * main() * { * mrb_int i = 99; * mrb_state *mrb = mrb_open(); * * if (!mrb) { } * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i)); * fclose(fp); * mrb_close(mrb); * } * @param [mrb_state*] mrb_state* The current mruby state. * @param [mrb_value] mrb_value A reference to an mruby value. * @param [const char*] const char* The name of the method. * @param [mrb_int] mrb_int The number of arguments the method has. * @param [...] ... Variadic values(not type safe!). * @return [mrb_value] mrb_value mruby function value. */ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...); /** * Call existing ruby functions. This is basically the type safe version of mrb_funcall. * * #include * #include * #include "mruby/compile.h" * int * main() * { * mrb_int i = 99; * mrb_state *mrb = mrb_open(); * * if (!mrb) { } * mrb_sym m_sym = mrb_intern_cstr(mrb, "method_name"); // Symbol for method. * * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall_argv(mrb, obj, m_sym, 1, &obj); // Calling ruby function from test.rb. * fclose(fp); * mrb_close(mrb); * } * @param [mrb_state*] mrb_state* The current mruby state. * @param [mrb_value] mrb_value A reference to an mruby value. * @param [mrb_sym] mrb_sym The symbol representing the method. * @param [mrb_int] mrb_int The number of arguments the method has. * @param [const mrb_value*] mrb_value* Pointer to the object. * @return [mrb_value] mrb_value mruby function value. * @see mrb_funcall */ MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*); /** * Call existing ruby functions with a block. */ MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value); /** * Create a symbol * * # Ruby style: * :pizza # => :pizza * * // C style: * mrb_sym m_sym = mrb_intern_cstr(mrb, "pizza"); // => :pizza * @param [mrb_state*] mrb_state* The current mruby state. * @param [const char*] const char* The name of the method. * @return [mrb_sym] mrb_sym A symbol. */ MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*); MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t); MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t); #define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, lit, mrb_strlen_lit(lit)) MRB_API mrb_sym mrb_intern_str(mrb_state*,mrb_value); MRB_API mrb_value mrb_check_intern_cstr(mrb_state*,const char*); MRB_API mrb_value mrb_check_intern(mrb_state*,const char*,size_t); MRB_API mrb_value mrb_check_intern_str(mrb_state*,mrb_value); MRB_API const char *mrb_sym2name(mrb_state*,mrb_sym); MRB_API const char *mrb_sym2name_len(mrb_state*,mrb_sym,mrb_int*); MRB_API mrb_value mrb_sym2str(mrb_state*,mrb_sym); MRB_API void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */ MRB_API void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */ MRB_API void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */ MRB_API void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */ MRB_API void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */ MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*); MRB_API void mrb_free(mrb_state*, void*); MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len); /** * Turns a C string into a Ruby string value. */ MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*); MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len); #define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit)) #ifdef _WIN32 char* mrb_utf8_from_locale(const char *p, size_t len); char* mrb_locale_from_utf8(const char *p, size_t len); #define mrb_locale_free(p) free(p) #define mrb_utf8_free(p) free(p) #else #define mrb_utf8_from_locale(p, l) (p) #define mrb_locale_from_utf8(p, l) (p) #define mrb_locale_free(p) #define mrb_utf8_free(p) #endif /** * Creates new mrb_state. * * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open(void); /** * Create new mrb_state with custom allocators. * * @param f * Reference to the allocation function. * @param ud * User data will be passed to custom allocator f. * If user data isn't required just pass NULL. * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud); /** * Create new mrb_state with just the MRuby core * * @param f * Reference to the allocation function. * Use mrb_default_allocf for the default * @param ud * User data will be passed to custom allocator f. * If user data isn't required just pass NULL. * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud); /** * Closes and frees a mrb_state. * * @param mrb * Pointer to the mrb_state to be closed. */ MRB_API void mrb_close(mrb_state *mrb); /** * The default allocation function. * * @see mrb_allocf */ MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*); MRB_API mrb_value mrb_top_self(mrb_state *); MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value); MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int); MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int); MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*); /* compatibility macros */ #define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k)) #define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0) #define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k)) MRB_API void mrb_p(mrb_state*, mrb_value); MRB_API mrb_int mrb_obj_id(mrb_value obj); MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name); MRB_API mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value); MRB_API mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value); MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2); MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base); MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val); MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val); MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj); MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2); MRB_API void mrb_garbage_collect(mrb_state*); MRB_API void mrb_full_gc(mrb_state*); MRB_API void mrb_incremental_gc(mrb_state *); MRB_API int mrb_gc_arena_save(mrb_state*); MRB_API void mrb_gc_arena_restore(mrb_state*,int); MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*); #define mrb_gc_mark_value(mrb,val) do {\ if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*); #define mrb_field_write_barrier_value(mrb, obj, val) do{\ if (!mrb_immediate_p(val)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_write_barrier(mrb_state *, struct RBasic*); MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj); MRB_API const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj); MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c); MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c); MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); #ifndef ISPRINT #define ISASCII(c) ((unsigned)(c) <= 0x7f) #define ISPRINT(c) (((unsigned)(c) - 0x20) < 0x5f) #define ISSPACE(c) ((c) == ' ' || (unsigned)(c) - '\t' < 5) #define ISUPPER(c) (((unsigned)(c) - 'A') < 26) #define ISLOWER(c) (((unsigned)(c) - 'a') < 26) #define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26) #define ISDIGIT(c) (((unsigned)(c) - '0') < 10) #define ISXDIGIT(c) (ISDIGIT(c) || ((unsigned)(c) | 0x20) - 'a' < 6) #define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c)) #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #define ISCNTRL(c) ((unsigned)(c) < 0x20 || (c) == 0x7f) #define TOUPPER(c) (ISLOWER(c) ? ((c) & 0x5f) : (c)) #define TOLOWER(c) (ISUPPER(c) ? ((c) | 0x20) : (c)) #endif MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len); MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc); MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg); MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...); MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...); MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...); MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...); MRB_API void mrb_print_backtrace(mrb_state *mrb); MRB_API void mrb_print_error(mrb_state *mrb); /* macros to get typical exception objects note: + those E_* macros requires mrb_state* variable named mrb. + exception objects obtained from those macros are local to mrb */ #define E_RUNTIME_ERROR (mrb_class_get(mrb, "RuntimeError")) #define E_TYPE_ERROR (mrb_class_get(mrb, "TypeError")) #define E_ARGUMENT_ERROR (mrb_class_get(mrb, "ArgumentError")) #define E_INDEX_ERROR (mrb_class_get(mrb, "IndexError")) #define E_RANGE_ERROR (mrb_class_get(mrb, "RangeError")) #define E_NAME_ERROR (mrb_class_get(mrb, "NameError")) #define E_NOMETHOD_ERROR (mrb_class_get(mrb, "NoMethodError")) #define E_SCRIPT_ERROR (mrb_class_get(mrb, "ScriptError")) #define E_SYNTAX_ERROR (mrb_class_get(mrb, "SyntaxError")) #define E_LOCALJUMP_ERROR (mrb_class_get(mrb, "LocalJumpError")) #define E_REGEXP_ERROR (mrb_class_get(mrb, "RegexpError")) #define E_SYSSTACK_ERROR (mrb_class_get(mrb, "SystemStackError")) #define E_NOTIMP_ERROR (mrb_class_get(mrb, "NotImplementedError")) #define E_FLOATDOMAIN_ERROR (mrb_class_get(mrb, "FloatDomainError")) #define E_KEY_ERROR (mrb_class_get(mrb, "KeyError")) MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c); /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj); /* mrb_gc_register() keeps the object from GC. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj); /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val); #define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val)) MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t); typedef enum call_type { CALL_PUBLIC, CALL_FCALL, CALL_VCALL, CALL_TYPE_MAX } call_type; MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2); MRB_API const char *mrb_class_name(mrb_state *mrb, struct RClass* klass); MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val); MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id); MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid); MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c); /* * Resume a Fiber * * @mrbgem mruby-fiber */ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv); /* * Yield a Fiber * * @mrbgem mruby-fiber */ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv); /* * FiberError reference * * @mrbgem mruby-fiber */ #define E_FIBER_ERROR (mrb_class_get(mrb, "FiberError")) /* memory pool implementation */ typedef struct mrb_pool mrb_pool; MRB_API struct mrb_pool* mrb_pool_open(mrb_state*); MRB_API void mrb_pool_close(struct mrb_pool*); MRB_API void* mrb_pool_alloc(struct mrb_pool*, size_t); MRB_API void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen); MRB_API mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t); MRB_API void* mrb_alloca(mrb_state *mrb, size_t); MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func); MRB_API void mrb_show_version(mrb_state *mrb); MRB_API void mrb_show_copyright(mrb_state *mrb); #ifdef MRB_DEBUG #include #define mrb_assert(p) assert(p) #define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max)))) #else #define mrb_assert(p) ((void)0) #define mrb_assert_int_fit(t1,n,t2,max) ((void)0) #endif #if __STDC_VERSION__ >= 201112L #define mrb_static_assert(exp, str) _Static_assert(exp, str) #else #define mrb_static_assert(exp, str) mrb_assert(exp) #endif MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...); MRB_END_DECL #endif /* MRUBY_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/000077500000000000000000000000001267140355100201675ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/include/mruby/array.h000066400000000000000000000070171267140355100214630ustar00rootroot00000000000000/* ** mruby/array.h - Array class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ARRAY_H #define MRUBY_ARRAY_H #include "common.h" /* * Array class */ MRB_BEGIN_DECL typedef struct mrb_shared_array { int refcnt; mrb_int len; mrb_value *ptr; } mrb_shared_array; struct RArray { MRB_OBJECT_HEADER; mrb_int len; union { mrb_int capa; mrb_shared_array *shared; } aux; mrb_value *ptr; }; #define mrb_ary_ptr(v) ((struct RArray*)(mrb_ptr(v))) #define mrb_ary_value(p) mrb_obj_value((void*)(p)) #define RARRAY(v) ((struct RArray*)(mrb_ptr(v))) #define RARRAY_LEN(a) (RARRAY(a)->len) #define RARRAY_PTR(a) ((const mrb_value*)RARRAY(a)->ptr) #define MRB_ARY_SHARED 256 #define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED) #define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED) #define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED) void mrb_ary_decref(mrb_state*, mrb_shared_array*); MRB_API void mrb_ary_modify(mrb_state*, struct RArray*); MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int); /* * Initializes a new array. * * Equivalent to: * * Array.new * * @param mrb The mruby state reference. * @return The initialized array */ MRB_API mrb_value mrb_ary_new(mrb_state *mrb); MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals); MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr); MRB_API void mrb_ary_concat(mrb_state*, mrb_value, mrb_value); MRB_API mrb_value mrb_ary_splat(mrb_state*, mrb_value); /* * Pushes value into array. * * Equivalent to: * * ary << value * * @param mrb The mruby state reference. * @param ary The array in which the value will be pushed * @param value The value to be pushed into array */ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value); /* * Pops the last element from the array. * * Equivalent to: * * ary.pop * * @param mrb The mruby state reference. * @param ary The array from which the value will be poped. * @return The poped value. */ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary); /* * Returns a reference to an element of the array on the given index. * * Equivalent to: * * ary[n] * * @param mrb The mruby state reference. * @param ary The target array. * @param n The array index being referenced * @return The referenced value. */ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n); /* * Sets a value on an array at the given index * * Equivalent to: * * ary[n] = val * * @param mrb The mruby state reference. * @param ary The target array. * @param n The array index being referenced. * @param val The value being setted. */ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val); MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b); MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item); MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset); MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep); MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int len); static inline mrb_int mrb_ary_len(mrb_state *mrb, mrb_value ary) { (void)mrb; mrb_assert(mrb_array_p(ary)); return RARRAY_LEN(ary); } MRB_END_DECL #endif /* MRUBY_ARRAY_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/boxing_nan.h000066400000000000000000000057011267140355100224650ustar00rootroot00000000000000/* ** mruby/boxing_nan.h - nan boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NAN_H #define MRUBY_BOXING_NAN_H #ifdef MRB_USE_FLOAT # error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<---- #endif #ifdef MRB_INT64 # error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<---- #endif #define MRB_FIXNUM_SHIFT 0 #define MRB_TT_HAS_BASIC MRB_TT_OBJECT #ifdef MRB_ENDIAN_BIG #define MRB_ENDIAN_LOHI(a,b) a b #else #define MRB_ENDIAN_LOHI(a,b) b a #endif /* value representation by nan-boxing: * float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF * object: 111111111111TTTT TTPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP * int : 1111111111110001 0000000000000000 IIIIIIIIIIIIIIII IIIIIIIIIIIIIIII * sym : 1111111111110001 0100000000000000 SSSSSSSSSSSSSSSS SSSSSSSSSSSSSSSS * In order to get enough bit size to save TT, all pointers are shifted 2 bits * in the right direction. Also, TTTTTT is the mrb_vtype + 1; */ typedef struct mrb_value { union { mrb_float f; union { void *p; struct { MRB_ENDIAN_LOHI( uint32_t ttt; ,union { mrb_int i; mrb_sym sym; }; ) }; } value; }; } mrb_value; #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #define mrb_tt(o) ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1) #define mrb_type(o) ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT) #define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2)) #define mrb_float(o) (o).f #define mrb_cptr(o) mrb_ptr(o) #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym #define BOXNAN_SET_VALUE(o, tt, attr, v) do {\ switch (tt) {\ case MRB_TT_FALSE:\ case MRB_TT_TRUE:\ case MRB_TT_UNDEF:\ case MRB_TT_FIXNUM:\ case MRB_TT_SYMBOL: (o).attr = (v); break;\ default: (o).value.i = 0; (o).value.p = (void*)((uintptr_t)(o).value.p | (((uintptr_t)(v))>>2)); break;\ }\ (o).value.ttt = (0xfff00000|(((tt)+1)<<14));\ } while (0) #define SET_FLOAT_VALUE(mrb,r,v) do { \ if (v != v) { \ (r).value.ttt = 0x7ff80000; \ (r).value.i = 0; \ } else { \ (r).f = v; \ }} while(0) #define SET_NIL_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXNAN_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_VALUE(r, MRB_TT_CPTR, value.p, v) #define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_NAN_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/boxing_no.h000066400000000000000000000030041267140355100223170ustar00rootroot00000000000000/* ** mruby/boxing_no.h - unboxed mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NO_H #define MRUBY_BOXING_NO_H #define MRB_FIXNUM_SHIFT 0 #define MRB_TT_HAS_BASIC MRB_TT_OBJECT typedef struct mrb_value { union { mrb_float f; void *p; mrb_int i; mrb_sym sym; } value; enum mrb_vtype tt; } mrb_value; #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) mrb_ptr(o) #define mrb_float(o) (o).value.f #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym #define mrb_type(o) (o).tt #define BOXNIX_SET_VALUE(o, ttt, attr, v) do {\ (o).tt = ttt;\ (o).attr = v;\ } while (0) #define SET_NIL_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXNIX_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_FLOAT_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) #define SET_SYM_VALUE(r,v) BOXNIX_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXNIX_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_CPTR_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_CPTR, value.p, v) #define SET_UNDEF_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_NO_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/boxing_word.h000066400000000000000000000065451267140355100226730ustar00rootroot00000000000000/* ** mruby/boxing_word.h - word boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_WORD_H #define MRUBY_BOXING_WORD_H #if defined(MRB_INT16) # error MRB_INT16 is too small for MRB_WORD_BOXING. #endif struct RFloat { MRB_OBJECT_HEADER; mrb_float f; }; struct RCptr { MRB_OBJECT_HEADER; void *p; }; #define MRB_FIXNUM_SHIFT 1 #define MRB_TT_HAS_BASIC MRB_TT_FLOAT enum mrb_special_consts { MRB_Qnil = 0, MRB_Qfalse = 2, MRB_Qtrue = 4, MRB_Qundef = 6, }; #define MRB_FIXNUM_FLAG 0x01 #define MRB_SYMBOL_FLAG 0x0e #define MRB_SPECIAL_SHIFT 8 typedef union mrb_value { union { void *p; struct { unsigned int i_flag : MRB_FIXNUM_SHIFT; mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT); }; struct { unsigned int sym_flag : MRB_SPECIAL_SHIFT; mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT); }; struct RBasic *bp; struct RFloat *fp; struct RCptr *vp; } value; unsigned long w; } mrb_value; MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*); MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float); MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float); #define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f) #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) (o).value.vp->p #define mrb_float(o) (o).value.fp->f #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym static inline enum mrb_vtype mrb_type(mrb_value o) { switch (o.w) { case MRB_Qfalse: case MRB_Qnil: return MRB_TT_FALSE; case MRB_Qtrue: return MRB_TT_TRUE; case MRB_Qundef: return MRB_TT_UNDEF; } if (o.value.i_flag == MRB_FIXNUM_FLAG) { return MRB_TT_FIXNUM; } if (o.value.sym_flag == MRB_SYMBOL_FLAG) { return MRB_TT_SYMBOL; } return o.value.bp->tt; } #define mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse) #define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG) #define mrb_undef_p(o) ((o).w == MRB_Qundef) #define mrb_nil_p(o) ((o).w == MRB_Qnil) #define BOXWORD_SET_VALUE(o, ttt, attr, v) do {\ switch (ttt) {\ case MRB_TT_FALSE: (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\ case MRB_TT_TRUE: (o).w = MRB_Qtrue; break;\ case MRB_TT_UNDEF: (o).w = MRB_Qundef; break;\ case MRB_TT_FIXNUM: (o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\ case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\ default: (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\ }\ } while (0) #define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v) #define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_value(mrb, v) #define SET_NIL_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXWORD_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXWORD_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_SYM_VALUE(r,v) BOXWORD_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXWORD_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_UNDEF_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_WORD_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/class.h000066400000000000000000000052051267140355100214470ustar00rootroot00000000000000/* ** mruby/class.h - Class class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_CLASS_H #define MRUBY_CLASS_H #include "common.h" /** * Class class */ MRB_BEGIN_DECL struct RClass { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) #define RCLASS_SUPER(v) (((struct RClass*)(mrb_ptr(v)))->super) #define RCLASS_IV_TBL(v) (((struct RClass*)(mrb_ptr(v)))->iv) #define RCLASS_M_TBL(v) (((struct RClass*)(mrb_ptr(v)))->mt) static inline struct RClass* mrb_class(mrb_state *mrb, mrb_value v) { switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_fixnum(v)) return mrb->false_class; return mrb->nil_class; case MRB_TT_TRUE: return mrb->true_class; case MRB_TT_SYMBOL: return mrb->symbol_class; case MRB_TT_FIXNUM: return mrb->fixnum_class; case MRB_TT_FLOAT: return mrb->float_class; case MRB_TT_CPTR: return mrb->object_class; case MRB_TT_ENV: return NULL; default: return mrb_obj_ptr(v)->c; } } // TODO: figure out where to put user flags #define MRB_FLAG_IS_PREPENDED (1 << 19) #define MRB_FLAG_IS_ORIGIN (1 << 20) #define MRB_CLASS_ORIGIN(c) do {\ if (c->flags & MRB_FLAG_IS_PREPENDED) {\ c = c->super;\ while (!(c->flags & MRB_FLAG_IS_ORIGIN)) {\ c = c->super;\ }\ }\ } while (0) #define MRB_INSTANCE_TT_MASK (0xFF) #define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt) #define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK) MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym); MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *); MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec); MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b); MRB_API struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *); MRB_API struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym); MRB_API struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym); MRB_API struct RClass* mrb_class_real(struct RClass* cl); void mrb_gc_mark_mt(mrb_state*, struct RClass*); size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*); void mrb_gc_free_mt(mrb_state*, struct RClass*); MRB_END_DECL #endif /* MRUBY_CLASS_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/common.h000066400000000000000000000025671267140355100216420ustar00rootroot00000000000000/* **"common.h - mruby common platform definition" ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMMON_H #define MRUBY_COMMON_H #ifdef __cplusplus # define MRB_BEGIN_DECL extern "C" { # define MRB_END_DECL } #else /** Start declarations in C mode */ # define MRB_BEGIN_DECL /** End declarations in C mode */ # define MRB_END_DECL #endif /** * Shared compiler macros */ MRB_BEGIN_DECL /** Declare a function that never returns. */ #if __STDC_VERSION__ >= 201112L # define mrb_noreturn _Noreturn #elif defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_noreturn __attribute__((noreturn)) #elif defined _MSC_VER # define mrb_noreturn __declspec(noreturn) #else # define mrb_noreturn #endif /** Mark a function as deprecated. */ #if defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_deprecated __attribute__((deprecated)) #elif defined _MSC_VER # define mrb_deprecated __declspec(deprecated) #else # define mrb_deprecated #endif /** Declare a function as always inlined. */ #if defined(_MSC_VER) # define MRB_INLINE static __inline #else # define MRB_INLINE static inline #endif /** Declare a public MRuby API function. */ #if defined(MRB_BUILD_AS_DLL) #if defined(MRB_CORE) || defined(MRB_LIB) # define MRB_API __declspec(dllexport) #else # define MRB_API __declspec(dllimport) #endif #else # define MRB_API extern #endif MRB_END_DECL #endif /* MRUBY_COMMON_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/compile.h000066400000000000000000000131021267140355100217650ustar00rootroot00000000000000/* ** mruby/compile.h - mruby parser ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILE_H #define MRUBY_COMPILE_H #include "common.h" /** * MRuby Compiler */ MRB_BEGIN_DECL #include struct mrb_jmpbuf; struct mrb_parser_state; /* load context */ typedef struct mrbc_context { mrb_sym *syms; int slen; char *filename; short lineno; int (*partial_hook)(struct mrb_parser_state*); void *partial_data; struct RClass *target_class; mrb_bool capture_errors:1; mrb_bool dump_result:1; mrb_bool no_exec:1; mrb_bool keep_lv:1; mrb_bool no_optimize:1; } mrbc_context; MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb); MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt); MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s); MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data); /* AST node structure */ typedef struct mrb_ast_node { struct mrb_ast_node *car, *cdr; uint16_t lineno, filename_index; } mrb_ast_node; /* lexer states */ enum mrb_lex_state_enum { EXPR_BEG, /* ignore newline, +/- is a sign. */ EXPR_END, /* newline significant, +/- is an operator. */ EXPR_ENDARG, /* ditto, and unbound braces. */ EXPR_ENDFN, /* ditto, and unbound braces. */ EXPR_ARG, /* newline significant, +/- is an operator. */ EXPR_CMDARG, /* newline significant, +/- is an operator. */ EXPR_MID, /* newline significant, +/- is an operator. */ EXPR_FNAME, /* ignore newline, no reserved words. */ EXPR_DOT, /* right after '.' or '::', no reserved words. */ EXPR_CLASS, /* immediate after 'class', no here document. */ EXPR_VALUE, /* alike EXPR_BEG but label is disallowed. */ EXPR_MAX_STATE }; /* saved error message */ struct mrb_parser_message { int lineno; int column; char* message; }; #define STR_FUNC_PARSING 0x01 #define STR_FUNC_EXPAND 0x02 #define STR_FUNC_REGEXP 0x04 #define STR_FUNC_WORD 0x08 #define STR_FUNC_SYMBOL 0x10 #define STR_FUNC_ARRAY 0x20 #define STR_FUNC_HEREDOC 0x40 #define STR_FUNC_XQUOTE 0x80 enum mrb_string_type { str_not_parsing = (0), str_squote = (STR_FUNC_PARSING), str_dquote = (STR_FUNC_PARSING|STR_FUNC_EXPAND), str_regexp = (STR_FUNC_PARSING|STR_FUNC_REGEXP|STR_FUNC_EXPAND), str_sword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY), str_dword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_ssym = (STR_FUNC_PARSING|STR_FUNC_SYMBOL), str_ssymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY), str_dsymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_heredoc = (STR_FUNC_PARSING|STR_FUNC_HEREDOC), str_xquote = (STR_FUNC_PARSING|STR_FUNC_XQUOTE|STR_FUNC_EXPAND), }; /* heredoc structure */ struct mrb_parser_heredoc_info { mrb_bool allow_indent:1; mrb_bool line_head:1; enum mrb_string_type type; const char *term; int term_len; mrb_ast_node *doc; }; #define MRB_PARSER_BUF_SIZE 1024 /* parser structure */ struct mrb_parser_state { mrb_state *mrb; struct mrb_pool *pool; mrb_ast_node *cells; const char *s, *send; #ifndef MRB_DISABLE_STDIO FILE *f; #endif mrbc_context *cxt; char const *filename; int lineno; int column; enum mrb_lex_state_enum lstate; mrb_ast_node *lex_strterm; /* (type nest_level beg . end) */ unsigned int cond_stack; unsigned int cmdarg_stack; int paren_nest; int lpar_beg; int in_def, in_single; mrb_bool cmd_start:1; mrb_ast_node *locals; mrb_ast_node *pb; char buf[MRB_PARSER_BUF_SIZE]; int bidx; mrb_ast_node *all_heredocs; /* list of mrb_parser_heredoc_info* */ mrb_ast_node *heredocs_from_nextline; mrb_ast_node *parsing_heredoc; mrb_ast_node *lex_strterm_before_heredoc; mrb_bool heredoc_end_now:1; /* for mirb */ void *ylval; size_t nerr; size_t nwarn; mrb_ast_node *tree; mrb_bool no_optimize:1; mrb_bool capture_errors:1; struct mrb_parser_message error_buffer[10]; struct mrb_parser_message warn_buffer[10]; mrb_sym* filename_table; size_t filename_table_length; int current_filename_index; struct mrb_jmpbuf* jmp; }; MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*); MRB_API void mrb_parser_free(struct mrb_parser_state*); MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*); MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*); MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx); /* utility functions */ #ifndef MRB_DISABLE_STDIO MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*); #endif MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*); MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*); MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*); /* program load functions */ #ifndef MRB_DISABLE_STDIO MRB_API mrb_value mrb_load_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt); #endif MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s); MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len); MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt); MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt); /** @} */ MRB_END_DECL #endif /* MRUBY_COMPILE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/data.h000066400000000000000000000042211267140355100212500ustar00rootroot00000000000000/* ** mruby/data.h - Data class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DATA_H #define MRUBY_DATA_H #include "common.h" /** * Custom C wrapped data. * * Defining Ruby wrappers around native objects. */ MRB_BEGIN_DECL /** * Custom data type description. */ typedef struct mrb_data_type { /** data type name */ const char *struct_name; /** data type release function pointer */ void (*dfree)(mrb_state *mrb, void*); } mrb_data_type; struct RData { MRB_OBJECT_HEADER; struct iv_tbl *iv; const mrb_data_type *type; void *data; }; MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type); #define Data_Wrap_Struct(mrb,klass,type,ptr)\ mrb_data_object_alloc(mrb,klass,ptr,type) #define Data_Make_Struct(mrb,klass,strct,type,sval,data) do { \ sval = mrb_malloc(mrb, sizeof(strct)); \ { static const strct zero = { 0 }; *sval = zero; };\ data = Data_Wrap_Struct(mrb,klass,type,sval);\ } while (0) #define RDATA(obj) ((struct RData *)(mrb_ptr(obj))) #define DATA_PTR(d) (RDATA(d)->data) #define DATA_TYPE(d) (RDATA(d)->type) MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*); MRB_API void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_get_ptr(mrb,obj,dtype) MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_CHECK_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_check_get_ptr(mrb,obj,dtype) /* obsolete functions and macros */ #define mrb_data_check_and_get(mrb,obj,dtype) mrb_data_get_ptr(mrb,obj,dtype) #define mrb_get_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define mrb_check_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define Data_Get_Struct(mrb,obj,type,sval) do {\ *(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \ } while (0) static inline void mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type) { mrb_assert(mrb_type(v) == MRB_TT_DATA); DATA_PTR(v) = ptr; DATA_TYPE(v) = type; } MRB_END_DECL #endif /* MRUBY_DATA_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/debug.h000066400000000000000000000027651267140355100214400ustar00rootroot00000000000000/* ** mruby/debug.h - mruby debug info ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DEBUG_H #define MRUBY_DEBUG_H #include "common.h" /** * MRuby Debugging. */ MRB_BEGIN_DECL typedef enum mrb_debug_line_type { mrb_debug_line_ary = 0, mrb_debug_line_flat_map = 1 } mrb_debug_line_type; typedef struct mrb_irep_debug_info_line { uint32_t start_pos; uint16_t line; } mrb_irep_debug_info_line; typedef struct mrb_irep_debug_info_file { uint32_t start_pos; const char *filename; mrb_sym filename_sym; uint32_t line_entry_count; mrb_debug_line_type line_type; union { void *ptr; mrb_irep_debug_info_line *flat_map; uint16_t *ary; } lines; } mrb_irep_debug_info_file; typedef struct mrb_irep_debug_info { uint32_t pc_count; uint16_t flen; mrb_irep_debug_info_file **files; } mrb_irep_debug_info; /* * get line from irep's debug info and program counter * @return returns NULL if not found */ MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, uint32_t pc); /* * get line from irep's debug info and program counter * @return returns -1 if not found */ MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc); MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file( mrb_state *mrb, mrb_irep *irep, uint32_t start_pos, uint32_t end_pos); MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep); MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d); MRB_END_DECL #endif /* MRUBY_DEBUG_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/dump.h000066400000000000000000000106551267140355100213140ustar00rootroot00000000000000/* ** mruby/dump.h - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DUMP_H #define MRUBY_DUMP_H #include #include #include "common.h" /** * Dumping compiled mruby script. */ MRB_BEGIN_DECL #define DUMP_DEBUG_INFO 1 #define DUMP_ENDIAN_BIG 2 #define DUMP_ENDIAN_LIL 4 #define DUMP_ENDIAN_NAT 6 #define DUMP_ENDIAN_MASK 6 int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); #ifndef MRB_DISABLE_STDIO int mrb_dump_irep_binary(mrb_state*, mrb_irep*, uint8_t, FILE*); int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, uint8_t flags, FILE *f, const char *initname); mrb_irep *mrb_read_irep_file(mrb_state*, FILE*); MRB_API mrb_value mrb_load_irep_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*); #endif MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*); /* dump/load error code * * NOTE: MRB_DUMP_GENERAL_FAILURE is caused by * unspecified issues like malloc failed. */ #define MRB_DUMP_OK 0 #define MRB_DUMP_GENERAL_FAILURE (-1) #define MRB_DUMP_WRITE_FAULT (-2) #define MRB_DUMP_READ_FAULT (-3) #define MRB_DUMP_CRC_ERROR (-4) #define MRB_DUMP_INVALID_FILE_HEADER (-5) #define MRB_DUMP_INVALID_IREP (-6) #define MRB_DUMP_INVALID_ARGUMENT (-7) /* null symbol length */ #define MRB_DUMP_NULL_SYM_LEN 0xFFFF /* Rite Binary File header */ #define RITE_BINARY_IDENT "RITE" #define RITE_BINARY_IDENT_LIL "ETIR" #define RITE_BINARY_FORMAT_VER "0003" #define RITE_COMPILER_NAME "MATZ" #define RITE_COMPILER_VERSION "0000" #define RITE_VM_VER "0000" #define RITE_BINARY_EOF "END\0" #define RITE_SECTION_IREP_IDENT "IREP" #define RITE_SECTION_LINENO_IDENT "LINE" #define RITE_SECTION_DEBUG_IDENT "DBG\0" #define RITE_SECTION_LV_IDENT "LVAR" #define MRB_DUMP_DEFAULT_STR_LEN 128 #define MRB_DUMP_ALIGNMENT sizeof(uint32_t) /* binary header */ struct rite_binary_header { uint8_t binary_ident[4]; /* Binary Identifier */ uint8_t binary_version[4]; /* Binary Format Version */ uint8_t binary_crc[2]; /* Binary CRC */ uint8_t binary_size[4]; /* Binary Size */ uint8_t compiler_name[4]; /* Compiler name */ uint8_t compiler_version[4]; }; /* section header */ #define RITE_SECTION_HEADER \ uint8_t section_ident[4]; \ uint8_t section_size[4] struct rite_section_header { RITE_SECTION_HEADER; }; struct rite_section_irep_header { RITE_SECTION_HEADER; uint8_t rite_version[4]; /* Rite Instruction Specification Version */ }; struct rite_section_lineno_header { RITE_SECTION_HEADER; }; struct rite_section_debug_header { RITE_SECTION_HEADER; }; struct rite_section_lv_header { RITE_SECTION_HEADER; }; #define RITE_LV_NULL_MARK UINT16_MAX struct rite_binary_footer { RITE_SECTION_HEADER; }; static inline int bigendian_p() { int i; char *p; i = 1; p = (char*)&i; return p[0]?0:1; } static inline size_t uint8_to_bin(uint8_t s, uint8_t *bin) { *bin = s; return sizeof(uint8_t); } static inline size_t uint16_to_bin(uint16_t s, uint8_t *bin) { *bin++ = (s >> 8) & 0xff; *bin = s & 0xff; return sizeof(uint16_t); } static inline size_t uint32_to_bin(uint32_t l, uint8_t *bin) { *bin++ = (l >> 24) & 0xff; *bin++ = (l >> 16) & 0xff; *bin++ = (l >> 8) & 0xff; *bin = l & 0xff; return sizeof(uint32_t); } static inline size_t uint32l_to_bin(uint32_t l, uint8_t *bin) { bin[3] = (l >> 24) & 0xff; bin[2] = (l >> 16) & 0xff; bin[1] = (l >> 8) & 0xff; bin[0] = l & 0xff; return sizeof(uint32_t); } static inline uint32_t bin_to_uint32(const uint8_t *bin) { return (uint32_t)bin[0] << 24 | (uint32_t)bin[1] << 16 | (uint32_t)bin[2] << 8 | (uint32_t)bin[3]; } static inline uint32_t bin_to_uint32l(const uint8_t *bin) { return (uint32_t)bin[3] << 24 | (uint32_t)bin[2] << 16 | (uint32_t)bin[1] << 8 | (uint32_t)bin[0]; } static inline uint16_t bin_to_uint16(const uint8_t *bin) { return (uint16_t)bin[0] << 8 | (uint16_t)bin[1]; } static inline uint8_t bin_to_uint8(const uint8_t *bin) { return (uint8_t)bin[0]; } MRB_END_DECL /** @internal crc.c */ uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc); #endif /* MRUBY_DUMP_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/error.h000066400000000000000000000034551267140355100215000ustar00rootroot00000000000000/* ** mruby/error.h - Exception class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ERROR_H #define MRUBY_ERROR_H #include "common.h" /** * MRuby error handling. */ MRB_BEGIN_DECL struct RException { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v)) MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg); MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str); #define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit)) MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv); MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc); MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb); MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...); /* declaration for fail method */ MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value); /** * Protect * * @mrbgem mruby-error */ MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state); /** * Ensure * * @mrbgem mruby-error */ 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); /** * Rescue * * @mrbgem mruby-error */ 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); /** * Rescue exception * * @mrbgem mruby-error */ 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); MRB_END_DECL #endif /* MRUBY_ERROR_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/gc.h000066400000000000000000000036321267140355100207350ustar00rootroot00000000000000/* ** mruby/gc.h - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_GC_H #define MRUBY_GC_H #include "common.h" /** * Uncommon memory management stuffs. */ MRB_BEGIN_DECL struct mrb_state; typedef void (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data); MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c); #ifndef MRB_GC_ARENA_SIZE #define MRB_GC_ARENA_SIZE 100 #endif typedef enum { MRB_GC_STATE_ROOT = 0, MRB_GC_STATE_MARK, MRB_GC_STATE_SWEEP } mrb_gc_state; typedef struct mrb_heap_page { struct RBasic *freelist; struct mrb_heap_page *prev; struct mrb_heap_page *next; struct mrb_heap_page *free_next; struct mrb_heap_page *free_prev; mrb_bool old:1; void *objects[]; } mrb_heap_page; typedef struct mrb_gc { mrb_heap_page *heaps; /* heaps for GC */ mrb_heap_page *sweeps; mrb_heap_page *free_heaps; size_t live; /* count of live objects */ #ifdef MRB_GC_FIXED_ARENA struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */ #else struct RBasic **arena; /* GC protection array */ int arena_capa; #endif int arena_idx; mrb_gc_state state; /* state of gc */ int current_white_part; /* make white object by white_part */ struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */ struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */ size_t live_after_mark; size_t threshold; int interval_ratio; int step_ratio; mrb_bool disabled :1; mrb_bool full :1; mrb_bool generational :1; mrb_bool out_of_memory :1; size_t majorgc_old_threshold; } mrb_gc; MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object); MRB_END_DECL #endif /* MRUBY_GC_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/hash.h000066400000000000000000000044071267140355100212700ustar00rootroot00000000000000/* ** mruby/hash.h - Hash class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_HASH_H #define MRUBY_HASH_H #include "common.h" #include /** * Hash class */ MRB_BEGIN_DECL struct RHash { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct kh_ht *ht; }; #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int); /* * Initializes a new hash. */ MRB_API mrb_value mrb_hash_new(mrb_state *mrb); /* * Sets a keys and values to hashes. */ MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val); /* * Gets a value from a key. */ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key); MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def); /* * Deletes hash key and value pair. */ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Gets an array of keys. */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); /* * Clears the hash. */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* declaration of struct kh_ht */ /* be careful when you touch the internal */ typedef struct { mrb_value v; mrb_int n; } mrb_hash_value; KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE) /* RHASH_TBL allocates st_table if not available. */ #define RHASH(obj) ((struct RHash*)(mrb_ptr(obj))) #define RHASH_TBL(h) (RHASH(h)->ht) #define RHASH_IFNONE(h) mrb_iv_get(mrb, (h), mrb_intern_lit(mrb, "ifnone")) #define RHASH_PROCDEFAULT(h) RHASH_IFNONE(h) MRB_API struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash); #define MRB_HASH_DEFAULT 1 #define MRB_HASH_PROC_DEFAULT 2 #define MRB_RHASH_DEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_DEFAULT) #define MRB_RHASH_PROCDEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_PROC_DEFAULT) /* GC functions */ void mrb_gc_mark_hash(mrb_state*, struct RHash*); size_t mrb_gc_mark_hash_size(mrb_state*, struct RHash*); void mrb_gc_free_hash(mrb_state*, struct RHash*); MRB_END_DECL #endif /* MRUBY_HASH_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/irep.h000066400000000000000000000023201267140355100212740ustar00rootroot00000000000000/* ** mruby/irep.h - mrb_irep structure ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_IREP_H #define MRUBY_IREP_H #include "common.h" #include /** * Compiled mruby scripts. */ MRB_BEGIN_DECL enum irep_pool_type { IREP_TT_STRING, IREP_TT_FIXNUM, IREP_TT_FLOAT, }; struct mrb_locals { mrb_sym name; uint16_t r; }; /* Program data array struct */ typedef struct mrb_irep { uint16_t nlocals; /* Number of local variables */ uint16_t nregs; /* Number of register variables */ uint8_t flags; mrb_code *iseq; mrb_value *pool; mrb_sym *syms; struct mrb_irep **reps; struct mrb_locals *lv; /* debug info */ const char *filename; uint16_t *lines; struct mrb_irep_debug_info* debug_info; size_t ilen, plen, slen, rlen, refcnt; } mrb_irep; #define MRB_ISEQ_NO_FREE 1 MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb); MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*); MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*); void mrb_irep_free(mrb_state*, struct mrb_irep*); void mrb_irep_incref(mrb_state*, struct mrb_irep*); void mrb_irep_decref(mrb_state*, struct mrb_irep*); MRB_END_DECL #endif /* MRUBY_IREP_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/khash.h000066400000000000000000000344061267140355100214450ustar00rootroot00000000000000/* ** mruby/khash.c - Hash for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_KHASH_H #define MRUBY_KHASH_H #include #include #include "common.h" /** * khash definitions used in mruby's hash table. */ MRB_BEGIN_DECL typedef uint32_t khint_t; typedef khint_t khiter_t; #ifndef KHASH_DEFAULT_SIZE # define KHASH_DEFAULT_SIZE 32 #endif #define KHASH_MIN_SIZE 8 #define UPPER_BOUND(x) ((x)>>2|(x)>>1) /* extern uint8_t __m[]; */ /* mask for flags */ static const uint8_t __m_empty[] = {0x02, 0x08, 0x20, 0x80}; static const uint8_t __m_del[] = {0x01, 0x04, 0x10, 0x40}; static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0}; #define __ac_isempty(ed_flag, i) (ed_flag[(i)/4]&__m_empty[(i)%4]) #define __ac_isdel(ed_flag, i) (ed_flag[(i)/4]&__m_del[(i)%4]) #define __ac_iseither(ed_flag, i) (ed_flag[(i)/4]&__m_either[(i)%4]) #define khash_power2(v) do { \ v--;\ v |= v >> 1;\ v |= v >> 2;\ v |= v >> 4;\ v |= v >> 8;\ v |= v >> 16;\ v++;\ } while (0) #define khash_mask(h) ((h)->n_buckets-1) #define khash_upper_bound(h) (UPPER_BOUND((h)->n_buckets)) /* declare struct kh_xxx and kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) */ #define KHASH_DECLARE(name, khkey_t, khval_t, kh_is_map) \ typedef struct kh_##name { \ khint_t n_buckets; \ khint_t size; \ khint_t n_occupied; \ uint8_t *ed_flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h); \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size); \ kh_##name##_t *kh_init_##name(mrb_state *mrb); \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h); \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h); \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key); \ khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x); \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h); static inline void kh_fill_flags(uint8_t *p, uint8_t c, size_t len) { while (len-- > 0) { *p++ = c; } } /* define kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) __hash_func: hash function __hash_equal: hash comparation function */ #define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ khint_t sz = h->n_buckets; \ size_t len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0); \ uint8_t *p = (uint8_t*)mrb_malloc(mrb, sizeof(uint8_t)*sz/4+len*sz); \ h->size = h->n_occupied = 0; \ h->keys = (khkey_t *)p; \ h->vals = kh_is_map ? (khval_t *)(p+sizeof(khkey_t)*sz) : NULL; \ h->ed_flags = p+len*sz; \ kh_fill_flags(h->ed_flags, 0xaa, sz/4); \ } \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) { \ kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \ if (size < KHASH_MIN_SIZE) \ size = KHASH_MIN_SIZE; \ khash_power2(size); \ h->n_buckets = size; \ kh_alloc_##name(mrb, h); \ return h; \ } \ kh_##name##_t *kh_init_##name(mrb_state *mrb) { \ return kh_init_##name##_size(mrb, KHASH_DEFAULT_SIZE); \ } \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ if (h) { \ mrb_free(mrb, h->keys); \ mrb_free(mrb, h); \ } \ } \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ (void)mrb; \ if (h && h->ed_flags) { \ kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4); \ h->size = h->n_occupied = 0; \ } \ } \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \ { \ khint_t k = __hash_func(mrb,key) & khash_mask(h), step = 0; \ (void)mrb; \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) return k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ return kh_end(h); \ } \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) \ { \ if (new_n_buckets < KHASH_MIN_SIZE) \ new_n_buckets = KHASH_MIN_SIZE; \ khash_power2(new_n_buckets); \ { \ kh_##name##_t hh; \ uint8_t *old_ed_flags = h->ed_flags; \ khkey_t *old_keys = h->keys; \ khval_t *old_vals = h->vals; \ khint_t old_n_buckets = h->n_buckets; \ khint_t i; \ hh.n_buckets = new_n_buckets; \ kh_alloc_##name(mrb, &hh); \ /* relocate */ \ for (i=0 ; in_occupied >= khash_upper_bound(h)) { \ kh_resize_##name(mrb, h, h->n_buckets*2); \ } \ k = __hash_func(mrb,key) & khash_mask(h); \ del_k = kh_end(h); \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) { \ if (ret) *ret = 0; \ return k; \ } \ } \ else if (del_k == kh_end(h)) { \ del_k = k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ if (del_k != kh_end(h)) { \ /* put at del */ \ h->keys[del_k] = key; \ h->ed_flags[del_k/4] &= ~__m_del[del_k%4]; \ h->size++; \ if (ret) *ret = 2; \ return del_k; \ } \ else { \ /* put at empty */ \ h->keys[k] = key; \ h->ed_flags[k/4] &= ~__m_empty[k%4]; \ h->size++; \ h->n_occupied++; \ if (ret) *ret = 1; \ return k; \ } \ } \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x) \ { \ (void)mrb; \ mrb_assert(x != h->n_buckets && !__ac_iseither(h->ed_flags, x)); \ h->ed_flags[x/4] |= __m_del[x%4]; \ h->size--; \ } \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ kh_##name##_t *h2; \ khiter_t k, k2; \ \ h2 = kh_init_##name(mrb); \ for (k = kh_begin(h); k != kh_end(h); k++) { \ if (kh_exist(h, k)) { \ k2 = kh_put_##name(mrb, h2, kh_key(h, k), NULL); \ if (kh_is_map) kh_value(h2, k2) = kh_value(h, k); \ } \ } \ return h2; \ } #define khash_t(name) kh_##name##_t #define kh_init_size(name,mrb,size) kh_init_##name##_size(mrb,size) #define kh_init(name,mrb) kh_init_##name(mrb) #define kh_destroy(name, mrb, h) kh_destroy_##name(mrb, h) #define kh_clear(name, mrb, h) kh_clear_##name(mrb, h) #define kh_resize(name, mrb, h, s) kh_resize_##name(mrb, h, s) #define kh_put(name, mrb, h, k) kh_put_##name(mrb, h, k, NULL) #define kh_put2(name, mrb, h, k, r) kh_put_##name(mrb, h, k, r) #define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k) #define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k) #define kh_copy(name, mrb, h) kh_copy_##name(mrb, h) #define kh_exist(h, x) (!__ac_iseither((h)->ed_flags, (x))) #define kh_key(h, x) ((h)->keys[x]) #define kh_val(h, x) ((h)->vals[x]) #define kh_value(h, x) ((h)->vals[x]) #define kh_begin(h) (khint_t)(0) #define kh_end(h) ((h)->n_buckets) #define kh_size(h) ((h)->size) #define kh_n_buckets(h) ((h)->n_buckets) #define kh_int_hash_func(mrb,key) (khint_t)((key)^((key)<<2)^((key)>>2)) #define kh_int_hash_equal(mrb,a, b) (a == b) #define kh_int64_hash_func(mrb,key) (khint_t)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(mrb,a, b) (a == b) static inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = *s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; return h; } #define kh_str_hash_func(mrb,key) __ac_X31_hash_string(key) #define kh_str_hash_equal(mrb,a, b) (strcmp(a, b) == 0) typedef const char *kh_cstr_t; MRB_END_DECL #endif /* MRUBY_KHASH_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/numeric.h000066400000000000000000000056111267140355100220050ustar00rootroot00000000000000/* ** mruby/numeric.h - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_NUMERIC_H #define MRUBY_NUMERIC_H #include "common.h" /** * Numeric class and it's sub-classes. * * Integer, Float and Fixnum */ MRB_BEGIN_DECL #define POSFIXABLE(f) ((f) <= MRB_INT_MAX) #define NEGFIXABLE(f) ((f) >= MRB_INT_MIN) #define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val); MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base); /* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */ MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt); MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x); mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y); #define MRB_UINT_MAKE2(n) uint ## n ## _t #define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n) #define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT) #define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT)) /* Idea from Potion: https://github.com/perl11/potion (MIT) */ #if (defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4))) \ || (defined(__GNUC__) && __GNUC__ >= 5) static inline mrb_bool mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum) { mrb_bool of; #ifdef MRB_INT64 long long val; of = __builtin_saddll_overflow(augend, addend, &val) || #else int val; of = __builtin_sadd_overflow(augend, addend, &val) || #endif (val > MRB_INT_MAX) || (val < MRB_INT_MIN); *sum = (mrb_int) val; return of; } static inline mrb_bool mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) { mrb_bool of; #ifdef MRB_INT64 long long val; of = __builtin_ssubll_overflow(minuend, subtrahend, &val) || #else int val; of = __builtin_ssub_overflow(minuend, subtrahend, &val) || #endif (val > MRB_INT_MAX) || (val < MRB_INT_MIN); *difference = (mrb_int) val; return of; } #else static inline mrb_bool mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum) { mrb_uint x = (mrb_uint)augend; mrb_uint y = (mrb_uint)addend; mrb_uint z = (mrb_uint)(x + y); *sum = (mrb_int)z; return !!(((x ^ z) & (y ^ z)) & MRB_INT_OVERFLOW_MASK); } static inline mrb_bool mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) { mrb_uint x = (mrb_uint)minuend; mrb_uint y = (mrb_uint)subtrahend; mrb_uint z = (mrb_uint)(x - y); *difference = (mrb_int)z; return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK); } #endif #undef MRB_INT_OVERFLOW_MASK #undef mrb_uint #undef MRB_UINT_MAKE #undef MRB_UINT_MAKE2 MRB_END_DECL #endif /* MRUBY_NUMERIC_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/object.h000066400000000000000000000013671267140355100216150ustar00rootroot00000000000000/* ** mruby/object.h - mruby object definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OBJECT_H #define MRUBY_OBJECT_H #define MRB_OBJECT_HEADER \ enum mrb_vtype tt:8;\ uint32_t color:3;\ uint32_t flags:21;\ struct RClass *c;\ struct RBasic *gcnext #define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag) struct RBasic { MRB_OBJECT_HEADER; }; #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v))) struct RObject { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v))) #define mrb_immediate_p(x) (mrb_type(x) < MRB_TT_HAS_BASIC) #define mrb_special_const_p(x) mrb_immediate_p(x) struct RFiber { MRB_OBJECT_HEADER; struct mrb_context *cxt; }; #endif /* MRUBY_OBJECT_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/opcode.h000066400000000000000000000215151267140355100216150ustar00rootroot00000000000000/* ** mruby/opcode.h - RiteVM operation codes ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OPCODE_H #define MRUBY_OPCODE_H #define MAXARG_Bx (0xffff) #define MAXARG_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */ /* instructions: packed 32 bit */ /* ------------------------------- */ /* A:B:C:OP = 9: 9: 7: 7 */ /* A:Bx:OP = 9:16: 7 */ /* Ax:OP = 25: 7 */ /* A:Bz:Cz:OP = 9:14: 2: 7 */ #define GET_OPCODE(i) ((int)(((mrb_code)(i)) & 0x7f)) #define GETARG_A(i) ((int)((((mrb_code)(i)) >> 23) & 0x1ff)) #define GETARG_B(i) ((int)((((mrb_code)(i)) >> 14) & 0x1ff)) #define GETARG_C(i) ((int)((((mrb_code)(i)) >> 7) & 0x7f)) #define GETARG_Bx(i) ((int)((((mrb_code)(i)) >> 7) & 0xffff)) #define GETARG_sBx(i) ((int)(GETARG_Bx(i)-MAXARG_sBx)) #define GETARG_Ax(i) ((int32_t)((((mrb_code)(i)) >> 7) & 0x1ffffff)) #define GETARG_UNPACK_b(i,n1,n2) ((int)((((mrb_code)(i)) >> (7+(n2))) & (((1<<(n1))-1)))) #define GETARG_UNPACK_c(i,n1,n2) ((int)((((mrb_code)(i)) >> 7) & (((1<<(n2))-1)))) #define GETARG_b(i) GETARG_UNPACK_b(i,14,2) #define GETARG_c(i) GETARG_UNPACK_c(i,14,2) #define MKOPCODE(op) ((op) & 0x7f) #define MKARG_A(c) ((mrb_code)((c) & 0x1ff) << 23) #define MKARG_B(c) ((mrb_code)((c) & 0x1ff) << 14) #define MKARG_C(c) (((c) & 0x7f) << 7) #define MKARG_Bx(v) ((mrb_code)((v) & 0xffff) << 7) #define MKARG_sBx(v) MKARG_Bx((v)+MAXARG_sBx) #define MKARG_Ax(v) ((mrb_code)((v) & 0x1ffffff) << 7) #define MKARG_PACK(b,n1,c,n2) ((((b) & ((1<R(A+1) (Syms[B]=:>,C=1) */ OP_GE,/* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1) */ OP_ARRAY,/* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ OP_ARYCAT,/* A B ary_cat(R(A),R(B)) */ OP_ARYPUSH,/* A B ary_push(R(A),R(B)) */ OP_AREF,/* A B C R(A) := R(B)[C] */ OP_ASET,/* A B C R(B)[C] := R(A) */ OP_APOST,/* A B C *R(A),R(A+1)..R(A+C) := R(A) */ OP_STRING,/* A Bx R(A) := str_dup(Lit(Bx)) */ OP_STRCAT,/* A B str_cat(R(A),R(B)) */ OP_HASH,/* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */ OP_LAMBDA,/* A Bz Cz R(A) := lambda(SEQ[Bz],Cz) */ OP_RANGE,/* A B C R(A) := range_new(R(B),R(B+1),C) */ OP_OCLASS,/* A R(A) := ::Object */ OP_CLASS,/* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ OP_MODULE,/* A B R(A) := newmodule(R(A),Syms(B)) */ OP_EXEC,/* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ OP_METHOD,/* A B R(A).newmethod(Syms(B),R(A+1)) */ OP_SCLASS,/* A B R(A) := R(B).singleton_class */ OP_TCLASS,/* A R(A) := target_class */ OP_DEBUG,/* A B C print R(A),R(B),R(C) */ OP_STOP,/* stop VM */ OP_ERR,/* Bx raise RuntimeError with message Lit(Bx) */ OP_RSVD1,/* reserved instruction #1 */ OP_RSVD2,/* reserved instruction #2 */ OP_RSVD3,/* reserved instruction #3 */ OP_RSVD4,/* reserved instruction #4 */ OP_RSVD5,/* reserved instruction #5 */ }; #define OP_L_STRICT 1 #define OP_L_CAPTURE 2 #define OP_L_METHOD OP_L_STRICT #define OP_L_LAMBDA (OP_L_STRICT|OP_L_CAPTURE) #define OP_L_BLOCK OP_L_CAPTURE #define OP_R_NORMAL 0 #define OP_R_BREAK 1 #define OP_R_RETURN 2 #endif /* MRUBY_OPCODE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/proc.h000066400000000000000000000042641267140355100213110ustar00rootroot00000000000000/* ** mruby/proc.h - Proc class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PROC_H #define MRUBY_PROC_H #include "common.h" #include /** * Proc class */ MRB_BEGIN_DECL struct REnv { MRB_OBJECT_HEADER; mrb_value *stack; mrb_sym mid; ptrdiff_t cioff; }; #define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len) #define MRB_ENV_STACK_LEN(e) ((mrb_int)(e)->flags) #define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1) #define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0) MRB_API void mrb_env_unshare(mrb_state*, struct REnv*); struct RProc { MRB_OBJECT_HEADER; union { mrb_irep *irep; mrb_func_t func; } body; struct RClass *target_class; struct REnv *env; }; /* aspec access */ #define MRB_ASPEC_REQ(a) (((a) >> 18) & 0x1f) #define MRB_ASPEC_OPT(a) (((a) >> 13) & 0x1f) #define MRB_ASPEC_REST(a) (((a) >> 12) & 0x1) #define MRB_ASPEC_POST(a) (((a) >> 7) & 0x1f) #define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f) #define MRB_ASPEC_KDICT(a) ((a) & (1<<1)) #define MRB_ASPEC_BLOCK(a) ((a) & 1) #define MRB_PROC_CFUNC 128 #define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC) != 0) #define MRB_PROC_STRICT 256 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0) #define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v))) struct RProc *mrb_proc_new(mrb_state*, mrb_irep*); struct RProc *mrb_closure_new(mrb_state*, mrb_irep*); MRB_API struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t); MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals); void mrb_proc_copy(struct RProc *a, struct RProc *b); /* implementation of #send method */ MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); /* following functions are defined in mruby-proc-ext so please include it when using */ MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*); MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int); /* old name */ #define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx) #include KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE) MRB_END_DECL #endif /* MRUBY_PROC_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/range.h000066400000000000000000000023151267140355100214350ustar00rootroot00000000000000/* ** mruby/range.h - Range class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RANGE_H #define MRUBY_RANGE_H #include "common.h" /** * Range class */ MRB_BEGIN_DECL typedef struct mrb_range_edges { mrb_value beg; mrb_value end; } mrb_range_edges; struct RRange { MRB_OBJECT_HEADER; mrb_range_edges *edges; mrb_bool excl : 1; }; #define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v))) #define mrb_range_value(p) mrb_obj_value((void*)(p)) /* * Initializes a Range. * * If the third parameter is FALSE then it includes the last value in the range. * If the third parameter is TRUE then it excludes the last value in the range. * * @param start the beginning value. * @param end the ending value. * @param exclude represents the inclusion or exclusion of the last value. */ MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude); MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len); mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)); MRB_END_DECL #endif /* MRUBY_RANGE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/re.h000066400000000000000000000003611267140355100207460ustar00rootroot00000000000000/* ** mruby/re.h - Regexp class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RE_H #define MRUBY_RE_H #ifdef __cplusplus extern "C" { #endif #define REGEXP_CLASS "Regexp" #ifdef __cplusplus } #endif #endif /* RE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/string.h000066400000000000000000000153521267140355100216540ustar00rootroot00000000000000/* ** mruby/string.h - String class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_STRING_H #define MRUBY_STRING_H #include "common.h" /** * String class */ MRB_BEGIN_DECL extern const char mrb_digitmap[]; #define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1)) struct RString { MRB_OBJECT_HEADER; union { struct { mrb_int len; union { mrb_int capa; struct mrb_shared_string *shared; } aux; char *ptr; } heap; char ary[RSTRING_EMBED_LEN_MAX + 1]; } as; }; #define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) #define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) #define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) #define RSTR_SET_EMBED_LEN(s, n) do {\ size_t tmp_n = (n);\ s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ } while (0) #define RSTR_SET_LEN(s, n) do {\ if (RSTR_EMBED_P(s)) {\ RSTR_SET_EMBED_LEN((s),(n));\ } else {\ s->as.heap.len = (mrb_int)(n);\ }\ } while (0) #define RSTR_EMBED_LEN(s)\ (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) #define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) #define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len) #define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa) #define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) #define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) #define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) #define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE) #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) #define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN) #define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN) #define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN) /* * Returns a pointer from a Ruby string */ #define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING(s) mrb_str_ptr(s) #define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) #define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s)) #define RSTRING_LEN(s) RSTR_LEN(RSTRING(s)) #define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s)) #define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) mrb_int mrb_str_strlen(mrb_state*, struct RString*); #define MRB_STR_SHARED 1 #define MRB_STR_NOFREE 2 #define MRB_STR_FROZEN 4 #define MRB_STR_NO_UTF 8 #define MRB_STR_EMBED 16 #define MRB_STR_EMBED_LEN_MASK 0x3e0 #define MRB_STR_EMBED_LEN_SHIFT 5 void mrb_gc_free_str(mrb_state*, struct RString*); MRB_API void mrb_str_modify(mrb_state*, struct RString*); MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value); /* * Adds two strings together. */ MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value); /* * Converts pointer into a Ruby string. */ MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*); /* * Returns an object as a Ruby string. */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj); /* * Resizes the string's length. */ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len); /* * Returns a sub string. */ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); /* * Returns a Ruby string type. */ MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa); MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr); MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str); MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str); /* * Duplicates a string object. */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); /* * Returns a symbol from a passed in string. */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck); MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck); /* * Returns a converted string type. */ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str); /* * Returns true if the strings match and false if the strings don't match. */ MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2); /* * Returns a concated string comprised of a Ruby string and a C string. * * @see mrb_str_cat_cstr */ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); /* * Returns a concated string comprised of a Ruby string and a C string. * * @see mrb_str_cat */ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr); MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2); #define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit)) /* * Adds str2 to the end of str1. */ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); /* * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1. */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2); /* * Returns a newly allocated C string from a Ruby string. * This is an utility function to pass a Ruby string to C library functions. * * - Returned string does not contain any NUL characters (but terminator). * - It raises an ArgumentError exception if Ruby string contains * NUL characters. * - Retured string will be freed automatically on next GC. * - Caller can modify returned string without affecting Ruby string * (e.g. it can be used for mkstemp(3)). * * @param [mrb_state *] mrb The current mruby state. * @param [mrb_value] str Ruby string. Must be an instance of String. * @return [char *] A newly allocated C string. */ MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str); mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str); mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str); mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); /* * Returns a printable version of str, surrounded by quote marks, with special characters escaped. */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); void mrb_noregexp(mrb_state *mrb, mrb_value self); void mrb_regexp_check(mrb_state *mrb, mrb_value obj); /* For backward compatibility */ #define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr) #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) #define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2) MRB_END_DECL #endif /* MRUBY_STRING_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/throw.h000066400000000000000000000020421267140355100215010ustar00rootroot00000000000000/* ** mruby/throw.h - mruby exception throwing handler ** ** See Copyright Notice in mruby.h */ #ifndef MRB_THROW_H #define MRB_THROW_H #ifdef MRB_ENABLE_CXX_EXCEPTION #define MRB_TRY(buf) do { try { #define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; } #define MRB_END_EXC(buf) } } while(0) #define MRB_THROW(buf) throw((buf)->impl) typedef mrb_int mrb_jmpbuf_impl; #else #include #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MRB_SETJMP _setjmp #define MRB_LONGJMP _longjmp #else #define MRB_SETJMP setjmp #define MRB_LONGJMP longjmp #endif #define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) { #define MRB_CATCH(buf) } else { #define MRB_END_EXC(buf) } } while(0) #define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1); #define mrb_jmpbuf_impl jmp_buf #endif struct mrb_jmpbuf { mrb_jmpbuf_impl impl; #ifdef MRB_ENABLE_CXX_EXCEPTION static mrb_int jmpbuf_id; mrb_jmpbuf() : impl(jmpbuf_id++) {} #endif }; #endif /* MRB_THROW_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/value.h000066400000000000000000000137551267140355100214670ustar00rootroot00000000000000/* ** mruby/value.h - mruby value definitions ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VALUE_H #define MRUBY_VALUE_H #include "common.h" /** * MRuby Value definition functions and macros. */ MRB_BEGIN_DECL typedef uint32_t mrb_sym; typedef uint8_t mrb_bool; struct mrb_state; #if defined(MRB_INT16) && defined(MRB_INT64) # error "You can't define MRB_INT16 and MRB_INT64 at the same time." #endif #include #if defined(MRB_INT64) typedef int64_t mrb_int; # define MRB_INT_BIT 64 # define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo64 # define MRB_PRId PRId64 # define MRB_PRIx PRIx64 #elif defined(MRB_INT16) typedef int16_t mrb_int; # define MRB_INT_BIT 16 # define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo16 # define MRB_PRId PRId16 # define MRB_PRIx PRIx16 #else typedef int32_t mrb_int; # define MRB_INT_BIT 32 # define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo32 # define MRB_PRId PRId32 # define MRB_PRIx PRIx32 #endif #ifdef MRB_USE_FLOAT typedef float mrb_float; # define str_to_mrb_float(buf) strtof(buf, NULL) #else typedef double mrb_float; # define str_to_mrb_float(buf) strtod(buf, NULL) #endif #if defined _MSC_VER && _MSC_VER < 1900 # ifndef __cplusplus # define inline __inline # endif # include MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg); MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...); # define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg) # define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__) # if _MSC_VER < 1800 # include # define isfinite(n) _finite(n) # define isnan _isnan # define isinf(n) (!_finite(n) && !_isnan(n)) # define signbit(n) (_copysign(1.0, (n)) < 0.0) # define strtof (float)strtod static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000; # define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE) # define NAN ((float)(INFINITY - INFINITY)) # endif #endif enum mrb_vtype { MRB_TT_FALSE = 0, /* 0 */ MRB_TT_FREE, /* 1 */ MRB_TT_TRUE, /* 2 */ MRB_TT_FIXNUM, /* 3 */ MRB_TT_SYMBOL, /* 4 */ MRB_TT_UNDEF, /* 5 */ MRB_TT_FLOAT, /* 6 */ MRB_TT_CPTR, /* 7 */ MRB_TT_OBJECT, /* 8 */ MRB_TT_CLASS, /* 9 */ MRB_TT_MODULE, /* 10 */ MRB_TT_ICLASS, /* 11 */ MRB_TT_SCLASS, /* 12 */ MRB_TT_PROC, /* 13 */ MRB_TT_ARRAY, /* 14 */ MRB_TT_HASH, /* 15 */ MRB_TT_STRING, /* 16 */ MRB_TT_RANGE, /* 17 */ MRB_TT_EXCEPTION, /* 18 */ MRB_TT_FILE, /* 19 */ MRB_TT_ENV, /* 20 */ MRB_TT_DATA, /* 21 */ MRB_TT_FIBER, /* 22 */ MRB_TT_MAXDEFINE /* 23 */ }; #include #ifdef MRB_DOCUMENTATION_BLOCK /** * @abstract * MRuby value boxing. * * Actual implementation depends on configured boxing type. * * @see mruby/boxing_no.h Default boxing representation * @see mruby/boxing_word.h Word representation * @see mruby/boxing_nan.h Boxed double representation */ typedef void mrb_value; #endif #if defined(MRB_NAN_BOXING) #include "boxing_nan.h" #elif defined(MRB_WORD_BOXING) #include "boxing_word.h" #else #include "boxing_no.h" #endif #ifndef mrb_fixnum_p #define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM) #endif #ifndef mrb_undef_p #define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF) #endif #ifndef mrb_nil_p #define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o)) #endif #ifndef mrb_bool #define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE) #endif #define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT) #define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL) #define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY) #define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING) #define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH) #define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR) #define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION) #define mrb_test(o) mrb_bool(o) MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value); /* * Returns a float in Ruby. */ MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; (void) mrb; SET_FLOAT_VALUE(mrb, v, f); return v; } static inline mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p) { mrb_value v; (void) mrb; SET_CPTR_VALUE(mrb,v,p); return v; } /* * Returns a fixnum in Ruby. */ MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i) { mrb_value v; SET_INT_VALUE(v, i); return v; } static inline mrb_value mrb_symbol_value(mrb_sym i) { mrb_value v; SET_SYM_VALUE(v, i); return v; } static inline mrb_value mrb_obj_value(void *p) { mrb_value v; SET_OBJ_VALUE(v, (struct RBasic*)p); return v; } /* * Get a nil mrb_value object. * * @return * nil mrb_value object reference. */ MRB_INLINE mrb_value mrb_nil_value(void) { mrb_value v; SET_NIL_VALUE(v); return v; } /* * Returns false in Ruby. */ MRB_INLINE mrb_value mrb_false_value(void) { mrb_value v; SET_FALSE_VALUE(v); return v; } /* * Returns true in Ruby. */ MRB_INLINE mrb_value mrb_true_value(void) { mrb_value v; SET_TRUE_VALUE(v); return v; } static inline mrb_value mrb_bool_value(mrb_bool boolean) { mrb_value v; SET_BOOL_VALUE(v, boolean); return v; } static inline mrb_value mrb_undef_value(void) { mrb_value v; SET_UNDEF_VALUE(v); return v; } #ifdef MRB_USE_ETEXT_EDATA extern char _etext[]; #ifdef MRB_NO_INIT_ARRAY_START extern char _edata[]; static inline mrb_bool mrb_ro_data_p(const char *p) { return _etext < p && p < _edata; } #else extern char __init_array_start[]; static inline mrb_bool mrb_ro_data_p(const char *p) { return _etext < p && p < (char*)&__init_array_start; } #endif #else # define mrb_ro_data_p(p) FALSE #endif MRB_END_DECL #endif /* MRUBY_VALUE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/variable.h000066400000000000000000000065601267140355100221340ustar00rootroot00000000000000/* ** mruby/variable.h - mruby variables ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VARIABLE_H #define MRUBY_VARIABLE_H #include "common.h" /** * Functions to access mruby variables. */ MRB_BEGIN_DECL typedef struct global_variable { int counter; mrb_value *data; mrb_value (*getter)(void); void (*setter)(void); /* void (*marker)(); */ /* int block_trace; */ /* struct trace_var *trace; */ } global_variable; struct global_entry { global_variable *var; mrb_sym id; }; mrb_value mrb_vm_special_get(mrb_state*, mrb_sym); void mrb_vm_special_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_iv_get(mrb_state*, mrb_sym); void mrb_vm_iv_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym); void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_const_get(mrb_state*, mrb_sym); void mrb_vm_const_set(mrb_state*, mrb_sym, mrb_value); MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value); MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_remove(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_bool mrb_iv_p(mrb_state *mrb, mrb_sym sym); MRB_API void mrb_iv_check(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src); MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id); MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym); MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val); MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym); MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v); MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym); mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*); mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod); mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self); mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value); mrb_value mrb_mod_class_variables(mrb_state*, mrb_value); mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym); mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym); mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer); /* GC functions */ void mrb_gc_mark_gv(mrb_state*); void mrb_gc_free_gv(mrb_state*); void mrb_gc_mark_iv(mrb_state*, struct RObject*); size_t mrb_gc_mark_iv_size(mrb_state*, struct RObject*); void mrb_gc_free_iv(mrb_state*, struct RObject*); MRB_END_DECL #endif /* MRUBY_VARIABLE_H */ mruby-1.2.0+20160315+git4f20d58a/include/mruby/version.h000066400000000000000000000036541267140355100220350ustar00rootroot00000000000000/* ** mruby/version.h - mruby version definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VERSION_H #define MRUBY_VERSION_H #include "common.h" /** * mruby version definition macros */ MRB_BEGIN_DECL /* * A passed in expression. */ #define MRB_STRINGIZE0(expr) #expr /* * Passes in an expression to MRB_STRINGIZE0. */ #define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr) /* * The version of Ruby used by mruby. */ #define MRUBY_RUBY_VERSION "1.9" /* * Ruby engine. */ #define MRUBY_RUBY_ENGINE "mruby" /* * Major release version number. */ #define MRUBY_RELEASE_MAJOR 1 /* * Minor release version number. */ #define MRUBY_RELEASE_MINOR 2 /* * Tiny release version number. */ #define MRUBY_RELEASE_TEENY 0 /* * The mruby version. */ #define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY) /* * Release number. */ #define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) /* * Release year. */ #define MRUBY_RELEASE_YEAR 2015 /* * Release month. */ #define MRUBY_RELEASE_MONTH 11 /* * Release day. */ #define MRUBY_RELEASE_DAY 17 /* * Release date as a string. */ #define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY) /* * The year mruby was first created. */ #define MRUBY_BIRTH_YEAR 2010 /* * MRuby's authors. */ #define MRUBY_AUTHOR "mruby developers" /* * mruby's version, and release date. */ #define MRUBY_DESCRIPTION \ "mruby " MRUBY_VERSION \ " (" MRUBY_RELEASE_DATE ") " \ /* * mruby's copyright information. */ #define MRUBY_COPYRIGHT \ "mruby - Copyright (c) " \ MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-" \ MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \ MRUBY_AUTHOR \ MRB_END_DECL #endif /* MRUBY_VERSION_H */ mruby-1.2.0+20160315+git4f20d58a/lib/000077500000000000000000000000001267140355100161545ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/lib/mruby/000077500000000000000000000000001267140355100173125ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/lib/mruby/source.rb000066400000000000000000000026521267140355100211440ustar00rootroot00000000000000require "pathname" module MRuby module Source # MRuby's source root directory ROOT = Pathname.new(File.expand_path('../../../',__FILE__)) # Reads a constant defined at version.h MRUBY_READ_VERSION_CONSTANT = Proc.new { |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?$/)[1] } MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION'] MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE'] MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR']) MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR']) MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY']) MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.') MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR']) MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH']) MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY']) MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.') MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR']) MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR'] end end mruby-1.2.0+20160315+git4f20d58a/minirake000077500000000000000000000322341267140355100171370ustar00rootroot00000000000000#!/usr/bin/env ruby # Original is https://github.com/jimweirich/rake/ # Copyright (c) 2003 Jim Weirich # License: MIT-LICENSE require 'getoptlong' require 'fileutils' class String def ext(newext='') return self.dup if ['.', '..'].include? self if newext != '' newext = (newext =~ /^\./) ? newext : ("." + newext) end self.chomp(File.extname(self)) << newext end def pathmap(spec=nil, &block) return self if spec.nil? result = '' spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| case frag when '%f' result << File.basename(self) when '%n' result << File.basename(self).ext when '%d' result << File.dirname(self) when '%x' result << File.extname(self) when '%X' result << self.ext when '%p' result << self when '%s' result << (File::ALT_SEPARATOR || File::SEPARATOR) when '%-' # do nothing when '%%' result << "%" when /%(-?\d+)d/ result << pathmap_partial($1.to_i) when /^%\{([^}]*)\}(\d*[dpfnxX])/ patterns, operator = $1, $2 result << pathmap('%' + operator).pathmap_replace(patterns, &block) when /^%/ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" else result << frag end end result end end module MiniRake class Task TASKS = Hash.new RULES = Array.new # List of prerequisites for a task. attr_reader :prerequisites # Source dependency for rule synthesized tasks. Nil if task was not # sythesized from a rule. attr_accessor :source # Create a task named +task_name+ with no actions or prerequisites.. # use +enhance+ to add actions and prerequisites. def initialize(task_name) @name = task_name @prerequisites = [] @actions = [] end # Enhance a task with prerequisites or actions. Returns self. def enhance(deps=nil, &block) @prerequisites |= deps if deps @actions << block if block_given? self end # Name of the task. def name @name.to_s end # Invoke the task if it is needed. Prerequites are invoked first. def invoke puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace return if @already_invoked @already_invoked = true prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten prerequisites.each { |n| Task[n].invoke } execute if needed? end # Execute the actions associated with this task. def execute puts "Execute #{name}" if $trace self.class.enhance_with_matching_rule(name) if @actions.empty? unless $dryrun @actions.each { |act| act.call(self) } end end # Is this task needed? def needed? true end # Timestamp for this task. Basic tasks return the current time for # their time stamp. Other tasks can be more sophisticated. def timestamp prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten prerequisites.collect { |n| Task[n].timestamp }.max || Time.now end # Class Methods ---------------------------------------------------- class << self # Clear the task list. This cause rake to immediately forget all # the tasks that have been assigned. (Normally used in the unit # tests.) def clear TASKS.clear RULES.clear end # List of all defined tasks. def tasks TASKS.keys.sort.collect { |tn| Task[tn] } end # Return a task with the given name. If the task is not currently # known, try to synthesize one from the defined rules. If no # rules are found, but an existing file matches the task name, # assume it is a file task with no dependencies or actions. def [](task_name) task_name = task_name.to_s if task = TASKS[task_name] return task end if task = enhance_with_matching_rule(task_name) return task end if File.exist?(task_name) return FileTask.define_task(task_name) end fail "Don't know how to rake #{task_name}" end # Define a task given +args+ and an option block. If a rule with # the given name already exists, the prerequisites and actions are # added to the existing task. def define_task(args, &block) task_name, deps = resolve_args(args) lookup(task_name).enhance([deps].flatten, &block) end # Define a rule for synthesizing tasks. def create_rule(args, &block) pattern, deps = resolve_args(args) pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern RULES << [pattern, deps, block] end # Lookup a task. Return an existing task if found, otherwise # create a task of the current type. def lookup(task_name) name = task_name.to_s TASKS[name] ||= self.new(name) end # If a rule can be found that matches the task name, enhance the # task with the prerequisites and actions from the rule. Set the # source attribute of the task appropriately for the rule. Return # the enhanced task or nil of no rule was found. def enhance_with_matching_rule(task_name) RULES.each do |pattern, extensions, block| if pattern.match(task_name) ext = extensions.first deps = extensions[1..-1] case ext when String source = task_name.sub(/\.[^.]*$/, ext) when Proc source = ext.call(task_name) else fail "Don't know how to handle rule dependent: #{ext.inspect}" end if File.exist?(source) task = FileTask.define_task({task_name => [source]+deps}, &block) task.source = source return task end end end nil end private # Resolve the arguments for a task/rule. def resolve_args(args) case args when Hash fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1 fail "No Task Name Given" if args.size < 1 task_name = args.keys[0] deps = args[task_name] deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps) else task_name = args deps = [] end [task_name, deps] end end end ###################################################################### class FileTask < Task # Is this file task needed? Yes if it doesn't exist, or if its time # stamp is out of date. def needed? return true unless File.exist?(name) prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten latest_prereq = prerequisites.collect{|n| Task[n].timestamp}.max return false if latest_prereq.nil? timestamp < latest_prereq end # Time stamp for file task. def timestamp stat = File::stat(name.to_s) stat.directory? ? Time.at(0) : stat.mtime end end module DSL # Declare a basic task. def task(args, &block) MiniRake::Task.define_task(args, &block) end # Declare a file task. def file(args, &block) MiniRake::FileTask.define_task(args, &block) end # Declare a set of files tasks to create the given directories on # demand. def directory(args, &block) MiniRake::FileTask.define_task(args) do |t| block.call(t) unless block.nil? dir = args.is_a?(Hash) ? args.keys.first : args (dir.split(File::SEPARATOR) + ['']).inject do |acc, part| (acc + File::SEPARATOR).tap do |d| Dir.mkdir(d) unless File.exists? d end + part end end end # Declare a rule for auto-tasks. def rule(args, &block) MiniRake::Task.create_rule(args, &block) end # Write a message to standard out if $verbose is enabled. def log(msg) print " " if $trace && $verbose puts msg if $verbose end # Run the system command +cmd+. def sh(cmd) puts cmd if $verbose system(cmd) or fail "Command Failed: [#{cmd}]" end def desc(text) end end end Rake = MiniRake extend MiniRake::DSL ###################################################################### # Task Definition Functions ... ###################################################################### # Rake main application object. When invoking +rake+ from the command # line, a RakeApp object is created and run. # class RakeApp RAKEFILES = ['rakefile', 'Rakefile'] OPTIONS = [ ['--dry-run', '-n', GetoptLong::NO_ARGUMENT, "Do a dry run without executing actions."], ['--help', '-H', GetoptLong::NO_ARGUMENT, "Display this help message."], ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT, "Include LIBDIR in the search path for required modules."], ['--nosearch', '-N', GetoptLong::NO_ARGUMENT, "Do not search parent directories for the Rakefile."], ['--quiet', '-q', GetoptLong::NO_ARGUMENT, "Do not log messages to standard output (default)."], ['--rakefile', '-f', GetoptLong::REQUIRED_ARGUMENT, "Use FILE as the rakefile."], ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT, "Require MODULE before executing rakefile."], ['--tasks', '-T', GetoptLong::NO_ARGUMENT, "Display the tasks and dependencies, then exit."], ['--pull-gems','-p', GetoptLong::NO_ARGUMENT, "Pull all git mrbgems."], ['--trace', '-t', GetoptLong::NO_ARGUMENT, "Turn on invoke/execute tracing."], ['--usage', '-h', GetoptLong::NO_ARGUMENT, "Display usage."], ['--verbose', '-v', GetoptLong::NO_ARGUMENT, "Log message to standard output."], ['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT, "Change executing directory of rakefiles."] ] # Create a RakeApp object. def initialize @rakefile = nil @nosearch = false end # True if one of the files in RAKEFILES is in the current directory. # If a match is found, it is copied into @rakefile. def have_rakefile RAKEFILES.each do |fn| if File.exist?(fn) @rakefile = fn return true end end return false end # Display the program usage line. def usage puts "rake [-f rakefile] {options} targets..." end # Display the rake command line help. def help usage puts puts "Options are ..." puts OPTIONS.sort.each do |long, short, mode, desc| if mode == GetoptLong::REQUIRED_ARGUMENT if desc =~ /\b([A-Z]{2,})\b/ long = long + "=#{$1}" end end printf " %-20s (%s)\n", long, short printf " %s\n", desc end end # Display the tasks and dependencies. def display_tasks MiniRake::Task.tasks.each do |t| puts "#{t.class} #{t.name}" t.prerequisites.each { |pre| puts " #{pre}" } end end # Return a list of the command line options supported by the # program. def command_line_options OPTIONS.collect { |lst| lst[0..-2] } end # Do the option defined by +opt+ and +value+. def do_option(opt, value) case opt when '--dry-run' $dryrun = true $trace = true when '--help' help exit when '--libdir' $:.push(value) when '--nosearch' @nosearch = true when '--quiet' $verbose = false when '--rakefile' RAKEFILES.clear RAKEFILES << value when '--require' require value when '--tasks' $show_tasks = true when '--pull-gems' $pull_gems = true when '--trace' $trace = true when '--usage' usage exit when '--verbose' $verbose = true when '--version' puts "rake, version #{RAKEVERSION}" exit when '--directory' Dir.chdir value else fail "Unknown option: #{opt}" end end # Read and handle the command line options. def handle_options $verbose = false $pull_gems = false opts = GetoptLong.new(*command_line_options) opts.each { |opt, value| do_option(opt, value) } end # Run the +rake+ application. def run handle_options begin here = Dir.pwd while ! have_rakefile Dir.chdir("..") if Dir.pwd == here || @nosearch fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})" end here = Dir.pwd end tasks = [] ARGV.each do |task_name| if /^(\w+)=(.*)/.match(task_name) ENV[$1] = $2 else tasks << task_name end end puts "(in #{Dir.pwd})" $rakefile = @rakefile load @rakefile if $show_tasks display_tasks else tasks.push("default") if tasks.size == 0 tasks.each do |task_name| MiniRake::Task[task_name].invoke end end rescue Exception => ex puts "rake aborted!" puts ex.message if $trace puts ex.backtrace.join("\n") else puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" end exit 1 end end end if __FILE__ == $0 then RakeApp.new.run end mruby-1.2.0+20160315+git4f20d58a/mrbgems/000077500000000000000000000000001267140355100170425ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/default.gembox000066400000000000000000000034171267140355100216760ustar00rootroot00000000000000MRuby::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 Enumerable::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 mruby-compiler to build other mrbgems conf.gem :core => "mruby-compiler" end mruby-1.2.0+20160315+git4f20d58a/mrbgems/full-core.gembox000066400000000000000000000004331267140355100221350ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/000077500000000000000000000000001267140355100221125ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/mrbgem.rake000066400000000000000000000002431267140355100242260ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Array class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/mrblib/000077500000000000000000000000001267140355100233615ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/mrblib/array.rb000066400000000000000000000473001267140355100250300ustar00rootroot00000000000000class 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 nil 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 if block ary.uniq!(&block) else ary.uniq! end 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 # other_ary. (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 = [] elem.each { |x| hash[x] = true } self.each { |x| array << x unless hash[x] } array end ## # call-seq: # ary | other_ary -> new_ary # # Set Union---Returns a new array by joining this array with # other_ary, 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 = [] elem.each{|v| hash[v] = true } self.each do |v| if hash[v] array << v hash.delete v end 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 # level 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) ar = [] self.each do |e| if e.is_a?(Array) && (depth.nil? || depth > 0) ar += e.flatten(depth.nil? ? nil : depth - 1) else ar << e end end ar end ## # call-seq: # ary.flatten! -> ary or nil # ary.flatten!(level) -> array or nil # # Flattens +self+ in place. # Returns nil if no modifications were made (i.e., # ary contains no subarrays.) If the optional level # 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 = [] self.each do |e| if e.is_a?(Array) && (depth.nil? || depth > 0) ar += e.flatten(depth.nil? ? nil : depth - 1) modified = true else ar << e end 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 # ary. # # [ "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_given? 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_given? 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_given? 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_given? low = 0 high = self.size satisfied = false while low < high mid = low + ((high - low) / 2).truncate val = self[mid] v = block.call(val) if v.is_a?(Integer) return val if v == 0 smaller = v < 0 elsif v == true satisfied = true smaller = true elsif v == false || v.nil? smaller = false end if smaller high = mid else low = mid + 1 end end return nil if low == self.size return nil unless satisfied self[low] 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_given? 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_given? 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_given? result = [] self.each do |x| result << x if block.call(x) end return nil if self.size == 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 # == 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 self.each do |*e| return idx if block.call(*e) 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 end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/src/000077500000000000000000000000001267140355100227015ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/src/array.c000066400000000000000000000100461267140355100241640ustar00rootroot00000000000000#include #include #include #include #include /* * 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 Array#rassoc. * * 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 * ==. Returns the first contained array that matches. See * also Array#assoc. * * 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 Array#[]. * * 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 aray as an array of * [key, value] 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) { v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); 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, RARRAY_PTR(ary)[i])), 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; } 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)); } void mrb_mruby_array_ext_gem_final(mrb_state* mrb) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/test/000077500000000000000000000000001267140355100230715ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-array-ext/test/array.rb000066400000000000000000000216041267140355100245370ustar00rootroot00000000000000## # 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, 4, 7, 10, 12] assert_include [4, 7], a.bsearch {|x| x >= 4 } assert_equal 7, a.bsearch {|x| x >= 6 } assert_equal 0, a.bsearch {|x| x >= -1 } assert_nil a.bsearch {|x| x >= 100 } # Find any mode a = [0, 4, 7, 10, 12] assert_include [4, 7], a.bsearch {|x| 1 - (x / 4).truncate } assert_nil a.bsearch {|x| 4 - (x / 2).truncate } assert_equal(nil, a.bsearch {|x| 1 }) assert_equal(nil, a.bsearch {|x| -1 }) 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#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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/000077500000000000000000000000001267140355100225305ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/bintest/000077500000000000000000000000001267140355100242005ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/bintest/mrdb.rb000066400000000000000000000207631267140355100254610ustar00rootroot00000000000000require '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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/bintest/print.rb000066400000000000000000000421331267140355100256640ustar00rootroot00000000000000require '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 = #"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 = #"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.to_s}"} tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000.to_s}"} 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'$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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/mrbgem.rake000066400000000000000000000003731267140355100246500ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/000077500000000000000000000000001267140355100236705ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/000077500000000000000000000000001267140355100246145ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c000066400000000000000000000265641267140355100265530ustar00rootroot00000000000000/* ** apibreak.c ** */ #include #include #include #include "mrdb.h" #include #include #include #include #include #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, int32_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 const char* get_class_name( mrb_state *mrb, struct RClass *class_obj ) { struct RClass *outer; mrb_sym class_sym; outer = mrb_class_outer_module(mrb, class_obj); class_sym = mrb_class_sym(mrb, class_obj, outer); return mrb_sym2name(mrb, class_sym); } 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 = get_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 = get_class_name(mrb, class_obj); sn = get_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 ) { uint32_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, (uint32_t)(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; ibpnum; 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; ibpnum; 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h000066400000000000000000000023601267140355100265440ustar00rootroot00000000000000/* ** apibreak.h ** */ #ifndef APIBREAK_H_ #define APIBREAK_H_ #include #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_ */ mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c000066400000000000000000000105071267140355100264300ustar00rootroot00000000000000/* * apilist.c */ #include #include #include #include "mrdb.h" #include "mrdberror.h" #include "apilist.h" #include #include #include #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 ? 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; search_path[0] = srcpath; search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->root_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], filename)) == 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; } } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h000066400000000000000000000004441267140355100264340ustar00rootroot00000000000000/* * apilist.h */ #ifndef APILIST_H_ #define APILIST_H_ #include #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_ */ mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c000066400000000000000000000031501267140355100266050ustar00rootroot00000000000000/* ** apiprint.c ** */ #include #include "mrdb.h" #include #include #include #include #include #include #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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h000066400000000000000000000003311267140355100266100ustar00rootroot00000000000000/* * apiprint.h */ #ifndef APIPRINT_H_ #define APIPRINT_H_ #include #include "mrdb.h" mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*); #endif /* APIPRINT_H_ */ mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c000066400000000000000000000254461267140355100265430ustar00rootroot00000000000000/* ** cmdbreak.c ** */ #include #include #include #include #include #include #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; iwcnt; 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; iwcnt; 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, (uint32_t)(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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c000066400000000000000000000252371267140355100264100ustar00rootroot00000000000000/* ** cmdmisc.c - mruby debugger miscellaneous command functions ** */ #include #include #include #include "apilist.h" #include 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] ::= | ':' | */ 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c000066400000000000000000000022611267140355100266010ustar00rootroot00000000000000/* ** cmdprint.c - mruby debugger print command functions ** */ #include #include "mrdb.h" #include #include #include #include #include #include #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; wcntwcnt; 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); } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c000066400000000000000000000024001267140355100262440ustar00rootroot00000000000000/* ** cmdrun.c - mruby debugger run command functions ** */ #include #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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c000066400000000000000000000421461267140355100257130ustar00rootroot00000000000000/* ** mrdb.c - mruby debugger ** */ #include #include #include #include #include #include #include #include #include #include #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; icommand[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->wcntwcnt++) { 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; iwcnt; i++) { printf("%d: %s\n", i, mrdb->words[i]); } #endif if (!cmd) { printf("invalid command ("); for (i=0; iwcnt; 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, (uint32_t)(pc - irep->iseq)); line = mrb_debug_get_line(irep, (uint32_t)(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((uint32_t)(dbg->prvci) < (uint32_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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h000066400000000000000000000064131267140355100257150ustar00rootroot00000000000000/* ** mrdb.h - mruby debugger ** */ #ifndef MRDB_H #define MRDB_H #include #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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h000066400000000000000000000004141267140355100265560ustar00rootroot00000000000000/* ** 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h000066400000000000000000000007141267140355100267650ustar00rootroot00000000000000/* ** 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/000077500000000000000000000000001267140355100216755ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/bintest/000077500000000000000000000000001267140355100233455ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/bintest/mirb.rb000066400000000000000000000005211267140355100246210ustar00rootroot00000000000000require '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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/mrbgem.rake000066400000000000000000000020511267140355100240100ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/tools/000077500000000000000000000000001267140355100230355ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/tools/mirb/000077500000000000000000000000001267140355100237665ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c000066400000000000000000000317661267140355100251000ustar00rootroot00000000000000/* ** 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 #include #include #include #include #include #ifdef ENABLE_READLINE #include #include #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 #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 #include #include #include #include #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 evaulation */ /* 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mrbc/000077500000000000000000000000001267140355100216675ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mrbc/mrbgem.rake000066400000000000000000000011431267140355100240030ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mrbc/tools/000077500000000000000000000000001267140355100230275ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mrbc/tools/mrbc/000077500000000000000000000000001267140355100237525ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c000066400000000000000000000174301267140355100250460ustar00rootroot00000000000000#include #include #include #include #include #include #include #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 place the output into ", "-v print version number, then turn on verbose mode", "-g produce debugging information", "-B binary 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; ioutfile) { 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby-config/000077500000000000000000000000001267140355100233455ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby-config/mrbgem.rake000066400000000000000000000017261267140355100254700ustar00rootroot00000000000000module 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)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"} }) FileUtils.chmod(0755, t.name) end end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby-config/mruby-config000066400000000000000000000012251267140355100256710ustar00rootroot00000000000000#!/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;; --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 passwd to linker before linked libraries" echo " --libs print linked libraries" exit 0;; esac shift done mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby-config/mruby-config.bat000066400000000000000000000014101267140355100264320ustar00rootroot00000000000000@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 "--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 :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 passwd to linker before linked libraries echo --libs print linked libraries mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/000077500000000000000000000000001267140355100221025ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/bintest/000077500000000000000000000000001267140355100235525ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/bintest/mruby.rb000066400000000000000000000023361267140355100252410ustar00rootroot00000000000000require 'tempfile' assert('regression for #1564') do o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1` assert_equal o, "-e:1:2: syntax error, unexpected tLSHFT\n" o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1` assert_equal o, "-e:1:3: syntax error, unexpected tLSHFT\n" 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 < 'mruby-compiler') end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/tools/000077500000000000000000000000001267140355100232425ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/tools/mruby/000077500000000000000000000000001267140355100244005ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c000066400000000000000000000132261267140355100257060ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #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; } 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); } mrbc_context_free(mrb, c); if (mrb->exc) { if (!mrb_undef_p(v)) { 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-strip/000077500000000000000000000000001267140355100221055ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-strip/bintest/000077500000000000000000000000001267140355100235555ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb000066400000000000000000000043621267140355100264040ustar00rootroot00000000000000require '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 < #include #include #include #include #include struct strip_args { int argc_start; int argc; char **argv; mrb_bool lvar; }; static void irep_remove_lv(mrb_state *mrb, mrb_irep *irep) { size_t 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/000077500000000000000000000000001267140355100220105ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/bintest/000077500000000000000000000000001267140355100234605ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/bintest/mrbc.rb000066400000000000000000000006501267140355100247310ustar00rootroot00000000000000require '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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/000077500000000000000000000000001267140355100227405ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/codegen.c000066400000000000000000001754571267140355100245330ustar00rootroot00000000000000/* ** codegen.c - mruby code generator ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include "node.h" #include #include #include 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; size_t pcapa; size_t scapa; size_t rcapa; uint16_t nlocals; uint16_t nregs; int ai; int debug_start_pos; uint16_t filename_index; parser_state* parser; } 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_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; } 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, size_t 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) { size_t i; mrb_value *pv; switch (mrb_type(val)) { case MRB_TT_STRING: for (i=0; iirep->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; iirep->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; iirep->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; } static inline int new_msym(codegen_scope *s, mrb_sym sym) { size_t i, len; mrb_assert(s->irep); len = s->irep->slen; if (len > 256) len = 256; for (i=0; iirep->syms[i] == sym) return i; if (s->irep->syms[i] == 0) break; } if (i == 256) { codegen_error(s, "too many symbols (max 256)"); } s->irep->syms[i] = sym; if (i == s->irep->slen) s->irep->slen++; return i; } static inline int new_sym(codegen_scope *s, mrb_sym sym) { size_t i; for (i=0; iirep->slen; i++) { if (s->irep->syms[i] == sym) return i; } if (s->irep->slen > 125 && s->irep->slen < 256) { s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); for (i = 0; i < 256 - s->irep->slen; i++) { static const mrb_sym mrb_sym_zero = { 0 }; s->irep->syms[i + s->irep->slen] = mrb_sym_zero; } s->irep->slen = 256; } 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 sym(x) ((mrb_sym)(intptr_t)(x)) #define lv_name(lv) sym((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 */ lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); /* 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); } 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)); 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; 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 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, (mrb_sym)(intptr_t)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; } static mrb_bool nosplat(node *t) { while (t) { if ((intptr_t)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); } static int gen_values(codegen_scope *s, node *t, int val) { int n = 0; int is_splat; while (t) { is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ if (n >= 127 || is_splat) { if (val) { 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 ((intptr_t)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 { codegen(s, t->car->cdr, NOVAL); t = t->cdr; 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; } #define CALL_MAXARGS 127 static void gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) { mrb_sym sym = name ? name : sym(tree->cdr->car); int idx; int n = 0, noop = 0, sendv = 0, blk = 0; codegen(s, tree->car, VAL); /* receiver */ idx = new_msym(s, sym); tree = tree->cdr->cdr->car; if (tree) { n = gen_values(s, tree->car, VAL); 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] == '+') { genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); } else if (!noop && symlen == 1 && symname[0] == '-') { genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); } else if (!noop && symlen == 1 && symname[0] == '*') { genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); } else if (!noop && symlen == 1 && symname[0] == '/') { genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); } else if (!noop && symlen == 1 && symname[0] == '<') { genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); } else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') { genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); } else if (!noop && symlen == 1 && symname[0] == '>') { genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); } else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') { genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); } else if (!noop && symlen == 2 && symname[0] == '=' && symname[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 (val) { push(); } } static void gen_assignment(codegen_scope *s, node *tree, int sp, int val) { int idx; int type = (intptr_t)tree->car; tree = tree->cdr; switch ((intptr_t)type) { case NODE_GVAR: idx = new_sym(s, sym(tree)); genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); break; case NODE_LVAR: idx = lv_idx(s, sym(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, sym(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, sym(tree)); genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); break; case NODE_CVAR: idx = new_sym(s, sym(tree)); genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); break; case NODE_CONST: idx = new_sym(s, sym(tree)); genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); break; case NODE_COLON2: idx = new_sym(s, sym(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: push(); gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); 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 printf("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++; } } push(); } } static void gen_send_intern(codegen_scope *s) { 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 ((intptr_t)tree->car->car) { case NODE_STR: if ((tree->cdr == NULL) && ((intptr_t)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 ((intptr_t)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= 2 && base <= 36); if (*p == '+') p++; while (p < e) { char c = *p; c = tolower((unsigned char)c); for (n=0; n 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 codegen(codegen_scope *s, node *tree, int val) { int nt; if (!tree) { if (val) { genop(s, MKOP_A(OP_LOADNIL, cursp())); push(); } return; } 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 = (intptr_t)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; onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); lp = loop_push(s, LOOP_BEGIN); lp->pc1 = onerr; if (tree->car) { codegen(s, tree->car, val); if (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_A(OP_RESCUE, exc)); push(); while (n2) { node *n3 = n2->car; node *n4 = n3->car; if (pos1) dispatch(s, pos1); pos2 = 0; do { 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(); } genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); pop(); if (n4 && n4->car && (intptr_t)n4->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)); } 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: { 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); } break; case NODE_LAMBDA: { int idx = lambda_body(s, tree, 1); genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); push(); } break; case NODE_BLOCK: { 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; switch ((intptr_t)tree->car->car) { case NODE_TRUE: case NODE_INT: case NODE_STR: codegen(s, tree->cdr->car, val); return; case NODE_FALSE: case NODE_NIL: codegen(s, e, val); return; } 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)); pop(); if ((intptr_t)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(); 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); 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, sym(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, sym(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); 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 ((intptr_t)t->car == NODE_ARRAY && 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) { gen_assignment(s, t->car, rhs+n, 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 (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 (ncar, 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 = sym(tree->cdr->car); mrb_int len; const char *name = mrb_sym2name_len(s->mrb, sym, &len); int idx; codegen(s, tree->car, VAL); if (len == 2 && ((name[0] == '|' && name[1] == '|') || (name[0] == '&' && name[1] == '&'))) { int pos; pop(); pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL); codegen(s, tree->cdr->cdr->car, VAL); pop(); gen_assignment(s, tree->car, cursp(), val); dispatch(s, pos); break; } 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)); } } gen_assignment(s, tree->car, cursp(), val); break; case NODE_SUPER: { int n = 0, noop = 0, sendv = 0; push(); /* room for receiver */ if (tree) { node *args = tree->car; if (args) { n = gen_values(s, args, VAL); 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) { codegen(s, tree, VAL); pop(); } 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; genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); push(); if (tree) { n = gen_values(s, tree, VAL); if (n < 0) { n = sendv = 1; push(); } } pop_n(n+1); 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) { 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)); } 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) { while (n--) { genop_peep(s, MKOP_A(OP_POPERR, 1), 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)); } } } break; case NODE_LVAR: if (val) { int idx = lv_idx(s, sym(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, sym(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, sym(tree)); genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); push(); } break; case NODE_IVAR: if (val) { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); push(); } break; case NODE_CVAR: if (val) { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); push(); } break; case NODE_CONST: { int sym = new_sym(s, sym(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[2] = { '$' }; mrb_value str; int sym; buf[1] = (char)(intptr_t)tree; str = mrb_str_new(s->mrb, buf, 2); sym = new_sym(s, mrb_intern_str(s->mrb, str)); genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); push(); } break; case NODE_NTH_REF: if (val) { int sym; mrb_state *mrb = s->mrb; mrb_value fix = mrb_fixnum_value((intptr_t)tree); mrb_value str = mrb_str_buf_new(mrb, 4); mrb_str_cat_lit(mrb, str, "$"); mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10)); 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 = (intptr_t)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 = str_to_mrb_float(p); 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 = (intptr_t)tree->car; tree = tree->cdr; switch (nt) { case NODE_FLOAT: { char *p = (char*)tree; mrb_float f = str_to_mrb_float(p); int off = new_lit(s, mrb_float_value(s->mrb, -f)); genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); push(); } break; case NODE_INT: { char *p = (char*)tree->car; int base = (intptr_t)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: { 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)); } 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) 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 ((intptr_t)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")); if (val == NOVAL) { push(); } genop(s, MKOP_A(OP_OCLASS, cursp())); genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); push(); codegen(s, tree->car, VAL); n = tree->cdr; while (n) { if ((intptr_t)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; } pop(); pop(); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); if (val == NOVAL) { pop(); } else { 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 sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); int off = new_lit(s, mrb_str_new(s->mrb, p, len)); if (val == NOVAL) { push(); } genop(s, MKOP_A(OP_OCLASS, cursp())); genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); push(); genop(s, MKOP_ABx(OP_STRING, cursp(), off)); pop(); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); if (val == NOVAL) { pop(); } else { 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)); if (p2 || p3) { push(); if (p2) { 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())); } argc++; if (p3) { push(); off = new_lit(s, mrb_str_new(s->mrb, p3, 1)); genop(s, MKOP_ABx(OP_STRING, cursp(), off)); argc++; pop(); } pop(); } pop(); 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) { 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); } if (n->cdr) { char *p2 = (char*)n->cdr; push(); off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); genop(s, MKOP_ABx(OP_STRING, cursp(), off)); argc++; pop(); } pop(); 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 ((intptr_t)n->car->car != NODE_STR) { codegen(s, n->car, NOVAL); } n = n->cdr; } } break; case NODE_SYM: if (val) { int sym = new_sym(s, sym(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, sym(tree->car)); int b = new_msym(s, sym(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())); pop_n(3); 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 = new_msym(s, sym(t->car)); genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); push(); t = t->cdr; num++; } pop_n(num + 1); 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, sym(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, sym(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, sym(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, sym(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; } } 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 = p->iseq; p->pcapa = 32; p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); p->irep->plen = 0; p->scapa = 256; 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; 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) { s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); mrb_debug_info_append_file(mrb, s->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->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; if (tree) { codegen(s, tree, VAL); pop(); } loop = s->loop; while (loop && loop->type == LOOP_BEGIN) { genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); loop = loop->prev; } while (loop && loop->type == LOOP_RESCUE) { loop = loop->prev; } if (!loop) { codegen_error(s, "unexpected break"); } 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 { 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; 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) { /* 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); return proc; } MRB_CATCH(&scope->jmp) { if (scope->filename == scope->irep->filename) { scope->irep->filename = NULL; } mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); return NULL; } MRB_END_EXC(&scope->jmp); } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/keywords000066400000000000000000000057141267140355100245410ustar00rootroot00000000000000%{ 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 %% mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/lex.def000066400000000000000000000253771267140355100242260ustar00rootroot00000000000000/* 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 ." #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" mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/node.h000066400000000000000000000032151267140355100240370ustar00rootroot00000000000000/* ** 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_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 */ mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/core/parse.y000066400000000000000000004753461267140355100242670ustar00rootroot00000000000000/* ** 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 #include #include #include #include #include #include #include #include #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 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)) 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); } /* (: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) { 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) { return list4((node*)NODE_IF, a, c, b); } /* (:while cond body) */ static node* new_while(parser_state *p, node *a, node *b) { return cons((node*)NODE_WHILE, cons(a, b)); } /* (:until cond body) */ static node* new_until(parser_state *p, node *a, node *b) { 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) { 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; 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) { node *n = list4((node*)NODE_CALL, a, nsym(b), c); 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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), (node*)(intptr_t)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, int len) { return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)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), (node*)(intptr_t)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)); } /* (:str . (a . a)) */ 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) */ 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, (node*)(intptr_t)n); } /* (:nthref . n) */ static node* new_nth_ref(parser_state *p, int n) { return cons((node*)NODE_NTH_REF, (node*)(intptr_t)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) { return new_call(p, recv, intern_cstr(m), 0); } /* (: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))); } 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)(intptr_t)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: 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 ((int)(intptr_t)lhs->car == NODE_LVAR) { local_add(p, sym(lhs->cdr)); } } static node* var_reference(parser_state *p, node *lhs) { node *n; if ((int)(intptr_t)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((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)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 = (node*)(intptr_t)parsing_heredoc_inf(p)->type; } } #define is_strterm_type(p,str_func) ((int)(intptr_t)((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 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 tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL %token tINTEGER tFLOAT tCHAR tXSTRING tREGEXP %token tSTRING tSTRING_PART tSTRING_MID %token tNTH_REF tBACK_REF %token tREGEXP_END %type singleton string string_rep string_interp xstring regexp %type literal numeric cpath symbol %type top_compstmt top_stmts top_stmt %type bodystmt compstmt stmts stmt expr arg primary command command_call method_call %type expr_value arg_value primary_value %type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure %type args call_args opt_call_args %type paren_args opt_paren_args variable %type command_args aref_args opt_block_arg block_arg var_ref var_lhs %type command_asgn mrhs superclass block_call block_command %type f_block_optarg f_block_opt %type f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs %type assoc_list assocs assoc undef_list backref for_var %type block_param opt_block_param block_param_def f_opt %type bv_decls opt_bv_decl bvar f_larglist lambda_body %type brace_block cmd_brace_block do_block lhs none f_bad_arg %type mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner %type fsym sym basic_symbol operation operation2 operation3 %type cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn %type heredoc words symbols %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 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 tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG %token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG %token tHEREDOC_BEG /* <<, <<- */ %token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM %token 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 { $$ = local_switch(p); } '{' top_compstmt '}' { yyerror(p, "BEGIN not supported"); local_resume(p, $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_rescue(p, $1, list1(list3(0, 0, $3)), 0); } | keyword_END '{' compstmt '}' { yyerror(p, "END not supported"); $$ = new_postexe(p, $3); } | command_asgn | mlhs '=' command_call { $$ = new_masgn(p, $1, $3); } | var_lhs tOP_ASGN command_call { $$ = new_op_asgn(p, $1, $2, $3); } | primary_value '[' opt_call_args rbracket tOP_ASGN command_call { $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); } | primary_value '.' tIDENTIFIER tOP_ASGN command_call { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | primary_value '.' tCONSTANT tOP_ASGN command_call { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call { yyerror(p, "constant re-assignment"); $$ = 0; } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | backref tOP_ASGN command_call { backref_error(p, $1); $$ = new_begin(p, 0); } | lhs '=' mrhs { $$ = new_asgn(p, $1, new_array(p, $3)); } | mlhs '=' arg_value { $$ = new_masgn(p, $1, $3); } | mlhs '=' mrhs { $$ = new_masgn(p, $1, new_array(p, $3)); } | expr ; command_asgn : lhs '=' command_call { $$ = new_asgn(p, $1, $3); } | lhs '=' command_asgn { $$ = new_asgn(p, $1, $3); } ; 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 dot_or_colon operation2 command_args ; 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 '.' operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4); } | primary_value '.' operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4); } | primary_value tCOLON2 operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4); } | primary_value tCOLON2 operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4); } | 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 '.' tIDENTIFIER { $$ = new_call(p, $1, $3, 0); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0); } | primary_value '.' tCONSTANT { $$ = new_call(p, $1, $3, 0); } | 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 '.' tIDENTIFIER { $$ = new_call(p, $1, $3, 0); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0); } | primary_value '.' tCONSTANT { $$ = new_call(p, $1, $3, 0); } | 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 { $$ = cons($1, nsym($3)); } ; fname : tIDENTIFIER | tCONSTANT | tFID | op { p->lstate = EXPR_ENDFN; $$ = $1; } | reswords { p->lstate = EXPR_ENDFN; $$ = $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 { $$ = new_asgn(p, $1, $3); } | lhs '=' arg modifier_rescue arg { $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); } | var_lhs tOP_ASGN arg { $$ = new_op_asgn(p, $1, $2, $3); } | var_lhs tOP_ASGN arg modifier_rescue arg { $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); } | primary_value '[' opt_call_args rbracket tOP_ASGN arg { $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); } | primary_value '.' tIDENTIFIER tOP_ASGN arg { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | primary_value '.' tCONSTANT tOP_ASGN arg { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg { $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN arg { yyerror(p, "constant re-assignment"); $$ = new_begin(p, 0); } | tCOLON3 tCONSTANT tOP_ASGN arg { yyerror(p, "constant re-assignment"); $$ = new_begin(p, 0); } | backref tOP_ASGN arg { 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; } ; arg_value : arg { $$ = $1; if (!$$) $$ = new_nil(p); } ; aref_args : none | args trailer { $$ = $1; NODE_LINENO($$, $1); } | args ',' assocs trailer { $$ = push($1, new_hash(p, $3)); } | assocs trailer { $$ = cons(new_hash(p, $1), 0); NODE_LINENO($$, $1); } ; 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 ',' 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 { $$ = 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 ',' 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 : { $$ = p->cmdarg_stack; CMDARG_PUSH(1); } call_args { p->cmdarg_stack = $1; $$ = $2; } ; block_arg : tAMPER arg_value { $$ = new_block_arg(p, $2); } ; opt_block_arg : ',' block_arg { $$ = $2; } | none { $$ = 0; } ; args : arg_value { $$ = cons($1, 0); NODE_LINENO($$, $1); } | tSTAR arg_value { $$ = cons(new_splat(p, $2), 0); NODE_LINENO($$, $2); } | args ',' arg_value { $$ = push($1, $3); } | args ',' tSTAR arg_value { $$ = push($1, new_splat(p, $4)); } | args ',' heredoc_bodies arg_value { $$ = push($1, $4); } | args ',' heredoc_bodies tSTAR arg_value { $$ = push($1, new_splat(p, $5)); } ; mrhs : args ',' arg_value { $$ = push($1, $3); } | args ',' tSTAR arg_value { $$ = push($1, new_splat(p, $4)); } | tSTAR arg_value { $$ = list1(new_splat(p, $2)); } ; primary : literal | string | xstring | regexp | heredoc | var_ref | backref | tFID { $$ = new_fcall(p, $1, 0); } | keyword_begin { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } bodystmt keyword_end { p->cmdarg_stack = $2; $$ = $3; } | tLPAREN_ARG { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } expr {p->lstate = EXPR_ENDARG;} rparen { p->cmdarg_stack = $2; $$ = $3; } | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen { $$ = 0; } | 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 '(' call_args rparen { $$ = new_yield(p, $3); } | keyword_yield '(' rparen { $$ = new_yield(p, 0); } | keyword_yield { $$ = new_yield(p, 0); } | 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); $$ = p->lpar_beg; p->lpar_beg = ++p->paren_nest; } f_larglist { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } lambda_body { p->lpar_beg = $2; $$ = new_lambda(p, $3, $5); local_unnest(p); p->cmdarg_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"); $$ = local_switch(p); } bodystmt keyword_end { $$ = new_class(p, $2, $3, $5); SET_LINENO($$, $1); local_resume(p, $4); } | keyword_class tLSHFT expr { $$ = p->in_def; p->in_def = 0; } term { $$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); p->in_single = 0; } bodystmt keyword_end { $$ = new_sclass(p, $3, $7); SET_LINENO($$, $1); local_resume(p, $6->car); p->in_def = $4; p->in_single = (int)(intptr_t)$6->cdr; } | keyword_module cpath { if (p->in_def || p->in_single) yyerror(p, "module definition in method body"); $$ = local_switch(p); } bodystmt keyword_end { $$ = new_module(p, $2, $4); SET_LINENO($$, $1); local_resume(p, $3); } | keyword_def fname { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } { p->in_def++; $$ = local_switch(p); } f_arglist bodystmt keyword_end { $$ = new_def(p, $2, $5, $6); SET_LINENO($$, $1); local_resume(p, $4); p->in_def--; p->cmdarg_stack = $3; } | keyword_def singleton dot_or_colon { p->lstate = EXPR_FNAME; $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } fname { p->in_single++; p->lstate = EXPR_ENDFN; /* force for args */ $$ = local_switch(p); } f_arglist bodystmt keyword_end { $$ = new_sdef(p, $2, $5, $7, $8); SET_LINENO($$, $1); local_resume(p, $6); p->in_single--; p->cmdarg_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, 1, 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 dot_or_colon operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4); } | block_call dot_or_colon operation2 opt_paren_args brace_block { $$ = new_call(p, $1, $3, $4); call_with_block(p, $$, $5); } | block_call dot_or_colon operation2 command_args do_block { $$ = new_call(p, $1, $3, $4); call_with_block(p, $$, $5); } ; method_call : operation paren_args { $$ = new_fcall(p, $1, $2); } | primary_value '.' operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4); } | primary_value tCOLON2 operation2 paren_args { $$ = new_call(p, $1, $3, $4); } | primary_value tCOLON2 operation3 { $$ = new_call(p, $1, $3, 0); } | primary_value '.' paren_args { $$ = new_call(p, $1, intern("call",4), $3); } | primary_value tCOLON2 paren_args { $$ = new_call(p, $1, intern("call",4), $3); } | 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); $$ = p->lineno; } opt_block_param compstmt '}' { $$ = new_block(p,$3,$4); SET_LINENO($$, $2); local_unnest(p); } | keyword_do { local_nest(p); $$ = p->lineno; } opt_block_param compstmt keyword_end { $$ = new_block(p,$3,$4); SET_LINENO($$, $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_value { $$ = 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 { $$ = p->lex_strterm; p->lex_strterm = NULL; } compstmt '}' { p->lex_strterm = $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 ; opt_heredoc_bodies : /* none */ | heredoc_bodies ; 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 { $$ = p->lex_strterm; p->lex_strterm = NULL; } compstmt '}' { parser_heredoc_info * inf = parsing_heredoc_inf(p); p->lex_strterm = $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__ { if (!p->filename) { p->filename = "(null)"; } $$ = new_str(p, p->filename, strlen(p->filename)); } | keyword__LINE__ { char buf[16]; snprintf(buf, sizeof(buf), "%d", p->lineno); $$ = new_int(p, buf, 10); } ; 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_value { $$ = cons(nsym($1), $2); } ; f_block_opt : f_opt_asgn primary_value { $$ = 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)(int)(intptr_t)$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_value tASSOC arg_value { $$ = cons($1, $3); } | tLABEL arg_value { $$ = cons(new_sym(p, $1), $2); } ; operation : tIDENTIFIER | tCONSTANT | tFID ; operation2 : tIDENTIFIER | tCONSTANT | tFID | op ; operation3 : tIDENTIFIER | tFID | op ; dot_or_colon : '.' | tCOLON2 ; opt_terms : /* none */ | terms ; opt_nl : /* none */ | nl ; rparen : opt_nl ')' ; rbracket : opt_nl ']' ; trailer : /* none */ | nl | ',' ; term : ';' {yyerrok;} | nl ; nl : '\n' { p->lineno++; p->column = 0; } opt_heredoc_bodies terms : term | terms ';' {yyerrok;} ; none : /* none */ { $$ = 0; } ; %% #define yylval (*((YYSTYPE*)(p->ylval))) static void yyerror(parser_state *p, const char *s) { char* c; int 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; int 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 $%d", (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 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) { int 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 c; if (c == '\n') { p->lineno++; p->column = 0; } if (c == *s) break; } s++; if (peeks(p, s)) { int 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) { p->bidx = 0; return p->column - 1; } static void tokadd(parser_state *p, int32_t c) { char utf8[4]; unsigned 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->bidx+len <= MRB_PARSER_BUF_SIZE) { unsigned i; for (i = 0; i < len; i++) { p->buf[p->bidx++] = utf8[i]; } } } static int toklast(parser_state *p) { return p->buf[p->bidx-1]; } static void tokfix(parser_state *p) { int i = p->bidx, imax = MRB_PARSER_BUF_SIZE - 1; if (i > imax) { i = imax; yyerror(p, "string too long (truncated)"); } p->buf[i] = '\0'; } static const char* tok(parser_state *p) { return p->buf; } static int toklen(parser_state *p) { return p->bidx; } #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 int scan_oct(const int *start, int len, int *retlen) { const int *s = start; int retval = 0; /* mrb_assert(len <= 3) */ while (len-- && *s >= '0' && *s <= '7') { retval <<= 3; retval |= *s++ - '0'; } *retlen = s - start; return retval; } static int32_t scan_hex(const int *start, int len, int *retlen) { static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; const int *s = start; int32_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 = s - start; return retval; } /* 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; } } c = scan_hex(buf, i, &i); if (i == 0) { yyerror(p, "Invalid escape character syntax"); return 0; } } return c; case 'u': /* Unicode */ { int buf[9]; int i; /* Look for opening brace */ i = 0; buf[0] = nextc(p); if (buf[0] < 0) goto eof; if (buf[0] == '{') { /* \u{xxxxxxxx} form */ for (i=0; i<9; i++) { buf[i] = nextc(p); if (buf[i] < 0) goto eof; if (buf[i] == '}') { break; } else if (!ISXDIGIT(buf[i])) { yyerror(p, "Invalid escape character syntax"); pushback(p, buf[i]); return 0; } } } else if (ISXDIGIT(buf[0])) { /* \uxxxx form */ for (i=1; i<4; 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]); } c = scan_hex(buf, i, &i); if (i == 0) { yyerror(p, "Invalid escape character syntax"); return 0; } if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { yyerror(p, "Invalid Unicode code point"); 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 = (intptr_t)p->lex_strterm->cdr->car; int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; 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)) { 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; } yylval.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 { 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; yylval.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); yylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING_MID; } } tokadd(p, c); } tokfix(p); p->lstate = EXPR_END; end_strterm(p); if (type & STR_FUNC_XQUOTE) { yylval.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; } yylval.nd = new_regx(p, s, dup, encp); return tREGEXP; } yylval.nd = new_str(p, tok(p), toklen(p)); 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; yylval.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)) == '=') { yylval.id = intern("**",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); c = tPOW; } else { if (c == '=') { yylval.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)) == '=') { yylval.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)) == '=') { yylval.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); yylval.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)) == '=') { yylval.id = intern("&&",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tANDOP; } else if (c == '=') { yylval.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)) == '=') { yylval.id = intern("||",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tOROP; } if (c == '=') { yylval.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 == '=') { yylval.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 == '=') { yylval.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 . 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; yylval.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; yylval.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; yylval.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; yylval.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); yylval.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 = strtod(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; } yylval.nd = new_float(p, tok(p)); return tFLOAT; } yylval.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)) == '=') { yylval.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)) == '=') { yylval.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)) == '=') { yylval.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); yylval.id = intern_cstr(tok(p)); return tGVAR; case '-': tokadd(p, '$'); tokadd(p, c); c = nextc(p); pushback(p, c); gvar: tokfix(p); yylval.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; } yylval.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; } yylval.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->bidx == 1) { yyerror(p, "incomplete instance variable syntax"); } else { yyerror(p, "incomplete class variable syntax"); } return 0; } else if (isdigit(c)) { if (p->bidx == 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); yylval.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; yylval.num = p->lineno; p->lstate = kw->state; if (state == EXPR_FNAME) { yylval.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)); yylval.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; islen; 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 buf; p->jmp = &buf; MRB_TRY(p->jmp) { 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); yyparse(p); 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->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) { 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->syms); mrb_free(mrb, cxt); } MRB_API const char* mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) { if (s) { int len = strlen(s); char *p = (char *)mrb_alloca(mrb, len + 1); memcpy(p, s, len + 1); 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 = i; return; } } p->current_filename_index = 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, int 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); } static mrb_value load_exec(mrb_state *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 (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 { 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) { 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 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, int len, mrbc_context *c) { return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); } MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int 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: printf("NODE_CALL:\n"); 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: $%d\n", (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); dump_prefix(tree, offset); printf("opt: %s\n", (char*)tree->cdr->cdr->cdr); break; case NODE_SYM: printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(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:\n"); mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; default: printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); break; } #endif } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-compiler/mrbgem.rake000066400000000000000000000027171267140355100241340ustar00rootroot00000000000000MRuby::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_abi_enabled? and f =~ /(codegen).c$/ objfile(f.pathmap("#{current_build_dir}/core/%n")) }.compact if build.cxx_abi_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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/000077500000000000000000000000001267140355100217405ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/mrbgem.rake000066400000000000000000000002501267140355100240520ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable module extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/mrblib/000077500000000000000000000000001267140355100232075ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/mrblib/enum.rb000066400000000000000000000427161267140355100245120ustar00rootroot00000000000000## # Enumerable # module Enumerable ## # call-seq: # enum.drop(n) -> array # # Drops first n elements from enum, 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 enum. # # 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) raise ArgumentError, "attempt to take negative size" if n < 0 n = n.to_int ary = [] self.each do |*val| break if ary.size >= n ary << val.__svalue 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 # 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 end ## # Iterates the given block for each slice of 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? 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 enum # 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 enum using a set of keys generated by mapping the # values in enum 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, ::Array.new(ary.size), 0, 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 nil, and the # second form returns an empty array. def first(n=NONE) if n == NONE self.each do |*val| return val.__svalue end return nil else a = [] i = 0 self.each do |*val| break if n<=i a.push val.__svalue i += 1 end a 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 # block once for every element in enum. # # 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 enum 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 enum 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 Comparable; the second uses the # block to return a <=> b. # # 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 if block max = val.__svalue if block.call(*val, max) > 0 min = val.__svalue if block.call(*val, min) < 0 else val = val.__svalue 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 # enum 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 true if the block never returns true # for all elements. If the block is not given, none? will return # true 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 true if the block returns true # exactly once. If the block is not given, one? will return # true 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 block for each element of enum 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 enum 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(n=nil, &block) return to_enum(:cycle, n) if !block && n.nil? ary = [] if n.nil? self.each do|*val| ary.push val block.call(*val) end loop do ary.each do|e| block.call(*e) end end else raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) n = n.to_int self.each do|*val| ary.push val end count = 0 while count < n ary.each do|e| block.call(*e) end count += 1 end end 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 enum with value or passes # to block. Returns the index for the first for which the # evaluated value is non-false. If no object matches, returns # nil # # 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 enum and merges corresponding # elements from each args. This generates a sequence of # n-element arrays, where n is one more than the # count of arguments. The length of the resulting sequence will be # enum#size. If the size of any argument is less than # enum#size, nil 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 enum as a list of # [key, value] 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/test/000077500000000000000000000000001267140355100227175ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-ext/test/enum.rb000066400000000000000000000102631267140355100242120ustar00rootroot00000000000000## # 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 = [] (1..5).each_cons(3){|e| a << e} assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a end assert("Enumerable#each_slice") do a = [] (1..10).each_slice(3){|e| a << e} assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a 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 } } 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/000077500000000000000000000000001267140355100221175ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/mrbgem.rake000066400000000000000000000004561267140355100242410ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable::Lazy class' spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext') end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/mrblib/000077500000000000000000000000001267140355100233665ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb000066400000000000000000000056131267140355100246770ustar00rootroot00000000000000module Enumerable # = Enumerable#lazy implementation # # Enumerable#lazy returns an instance of Enumerable::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 Lazy.new(self) end # == 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 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| if not 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/test/000077500000000000000000000000001267140355100230765ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enum-lazy/test/lazy.rb000066400000000000000000000017131267140355100244040ustar00rootroot00000000000000assert("Enumerable::Lazy") do a = [1, 2] assert_equal Enumerable::Lazy, a.lazy.class end assert("Enumerable::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("Enumerable::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/000077500000000000000000000000001267140355100223575ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/mrbgem.rake000066400000000000000000000004361267140355100244770ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/mrblib/000077500000000000000000000000001267140355100236265ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/mrblib/enumerator.rb000066400000000000000000000372021267140355100263400ustar00rootroot00000000000000## # 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_given? obj = Generator.new(&block) else raise ArgumentError unless obj 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) return to_enum :with_index, offset unless block_given? raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int) n = offset.to_int - 1 enumerator_block_call do |i| n += 1 yield [i,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 with_index 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) return to_enum(:with_object, object) unless block_given? enumerator_block_call do |i| yield [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_given? 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 yield and yield # nil. # # === 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 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_given? @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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/test/000077500000000000000000000000001267140355100233365ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-enumerator/test/enumerator.rb000066400000000000000000000257441267140355100260600ustar00rootroot00000000000000@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) 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#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("#", e.inspect) e = Enumerator.new("FooObject", :foo, 1) assert_equal("#", e.inspect) e = Enumerator.new("FooObject", :foo, 1, 2, 3) assert_equal("#", 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/000077500000000000000000000000001267140355100213275ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/mrbgem.rake000066400000000000000000000005731267140355100234510ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-error') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'extensional error handling' if build.cxx_abi_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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/src/000077500000000000000000000000001267140355100221165ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/src/exception.c000066400000000000000000000044261267140355100242660ustar00rootroot00000000000000#include #include #include 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/test/000077500000000000000000000000001267140355100223065ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/test/exception.c000066400000000000000000000030451267140355100244520ustar00rootroot00000000000000#include #include #include 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)); } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-error/test/exception.rb000066400000000000000000000026531267140355100246370ustar00rootroot00000000000000assert '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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/000077500000000000000000000000001267140355100211255ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/mrbgem.rake000066400000000000000000000003421267140355100232410ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/src/000077500000000000000000000000001267140355100217145ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/src/eval.c000066400000000000000000000144751267140355100230220ustar00rootroot00000000000000#include #include #include #include #include #include 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; proc = c->cibase[e->cioff].proc; if (!proc || MRB_PROC_CFUNC_P(proc)) { return NULL; } return proc->body.irep; } 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 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) { size_t i; mrb_code c; int argc = 0; for (i = 0; i < irep->ilen; i++) { c = irep->iseq[i]; switch(GET_OPCODE(c)){ case OP_ENTER: { mrb_aspec ax = GETARG_Ax(c); /* extra 1 means a slot for block */ argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; } break; case OP_EPUSH: patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1); 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); } } 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; } } } 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; if (!file) { file = "(eval)"; } mrbc_filename(mrb, cxt, file); 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 */ 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_parser_free(p); mrbc_context_free(mrb, cxt); mrb_exc_raise(mrb, mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); } 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->mid = c->ci[-1].mid; e->cioff = c->ci - c->cibase - 1; e->stack = c->ci->stackent; MRB_SET_ENV_STACK_LEN(e, c->ci[-1].proc->body.irep->nlocals); c->ci->env = e; proc->env = e; patch_irep(mrb, proc->body.irep, 0); mrb_parser_free(p); mrbc_context_free(mrb, cxt); return 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; mrb_value ret; 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); 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; } mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); #define CI_ACC_SKIP -1 static mrb_value f_instance_eval(mrb_state *mrb, mrb_value self) { struct mrb_context *c = mrb->c; 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); c->ci->acc = CI_ACC_SKIP; cv = mrb_singleton_class(mrb, self); c->ci->target_class = mrb_class_ptr(cv); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); return mrb_top_run(mrb, proc, mrb->c->stack[0], 0); } 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/test/000077500000000000000000000000001267140355100221045ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-eval/test/eval.rb000066400000000000000000000043511267140355100233630ustar00rootroot00000000000000assert('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 } } 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-exit/000077500000000000000000000000001267140355100211475ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-exit/mrbgem.rake000066400000000000000000000002331267140355100232620ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-exit') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Kernel#exit method' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-exit/src/000077500000000000000000000000001267140355100217365ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-exit/src/mruby-exit.c000066400000000000000000000006151267140355100242110ustar00rootroot00000000000000#include #include 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/000077500000000000000000000000001267140355100212655ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/mrbgem.rake000066400000000000000000000002251267140355100234010ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-fiber') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Fiber class' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/src/000077500000000000000000000000001267140355100220545ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/src/fiber.c000066400000000000000000000250561267140355100233170ustar00rootroot00000000000000#include #include #include #include #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 Fiber#resume method. * The code running inside the fiber can give up control by calling * Fiber.yield in which case it yields control back to caller * (the caller of the Fiber#resume). * * 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 * * produces * * 1 * 2 * resuming dead fiber (FiberError) * * The Fiber#resume method accepts an arbitrary number of * parameters, if it is the first call to resume then they * will be passed as block arguments. Otherwise they will be the return * value of the call to Fiber.yield * * Example: * * fiber = Fiber.new do |first| * second = Fiber.yield first + 2 * end * * puts fiber.resume 10 * puts fiber.resume 14 * puts fiber.resume 18 * * produces * * 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 (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"); } f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *f->cxt = mrb_context_zero; c = f->cxt; /* 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; 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 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 = c->stack+1; mrb_value *e = b + len; while (bcibase->argc = len; value = c->stack[0] = c->ci->proc->env->stack[0]; } else { value = fiber_result(mrb, a, len); } mrb_write_barrier(mrb, (struct RBasic*)c->fib); c->status = MRB_FIBER_RUNNING; mrb->c = 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 Fiber.yield * was called, or starts running it if it is the first call to * resume. Arguments passed to resume will be the value of * the Fiber.yield expression or will be passed as block * parameters to the fiber's block if this is the first resume. * * Alternatively, when resume is called it evaluates to the arguments passed * to the next Fiber.yield statement inside the fiber's block * or to the block value if it runs to completion without any * Fiber.yield */ 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 resume 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 Fiber.yield 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; mrb->c = c; c->status = MRB_FIBER_RUNNING; MARK_CONTEXT_MODIFY(c); mrb_write_barrier(mrb, (struct RBasic*)c->fib); 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"); } c->prev->status = MRB_FIBER_RUNNING; c->status = MRB_FIBER_SUSPENDED; mrb->c = c->prev; c->prev = NULL; if (c->vmexec) { c->vmexec = FALSE; mrb->c->ci->acc = CI_ACC_RESUMED; } mrb_write_barrier(mrb, (struct RBasic*)c->fib); 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 resume is called next. * Any arguments passed to the next resume 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/test/000077500000000000000000000000001267140355100222445ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-fiber/test/fiber.rb000066400000000000000000000076221267140355100236670ustar00rootroot00000000000000assert('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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/000077500000000000000000000000001267140355100217175ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/mrbgem.rake000066400000000000000000000004471267140355100240410ustar00rootroot00000000000000MRuby::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' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/mrblib/000077500000000000000000000000001267140355100231665ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/mrblib/hash.rb000066400000000000000000000225101267140355100244360ustar00rootroot00000000000000class 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 = Hash.new object[0].to_hash.each { |k, v| h[k] = v } return h elsif o.respond_to?(:to_a) h = Hash.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 = Hash.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 obj into a hash, using to_hash method. # Returns converted hash or nil if obj 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.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 KeyError exception; if default 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") # # produces: # # 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 hsh for which block # evaluates to true. # # 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_given? 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 # level 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 hsh'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 = Hash.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 hsh for which block # evaluates to false. # # If no block is given, an enumerator is returned instead. # def keep_if(&block) return to_enum :keep_if unless block_given? 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 nil. # # 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 true if hash is subset of # other. # # 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 true if hash is subset of # other or equals to other. # # 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 true if other is subset of # hash. # # 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 true if other is subset of # hash or equals to hash. # # 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 end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/src/000077500000000000000000000000001267140355100225065ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/src/hash-ext.c000066400000000000000000000020401267140355100243670ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include /* * call-seq: * hsh.values_at(key, ...) -> array * * Return an array containing the values associated with the given keys. * Also see Hash.select. * * 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; } 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()); } void mrb_mruby_hash_ext_gem_final(mrb_state *mrb) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/test/000077500000000000000000000000001267140355100226765ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-hash-ext/test/hash.rb000066400000000000000000000124461267140355100241550ustar00rootroot00000000000000## # 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.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#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#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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/000077500000000000000000000000001267140355100222545ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/mrbgem.rake000066400000000000000000000002461267140355100243730ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Kernel module extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/src/000077500000000000000000000000001267140355100230435ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/src/kernel.c000066400000000000000000000116051267140355100244720ustar00rootroot00000000000000#include #include #include #include /* * 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 nil. * */ 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 arg to a Fixnum. * Numeric types are converted directly (with floating point numbers * being truncated). base (0, or between 2 and 36) is a base for * integer string representation. If arg is a String, * when base is omitted or equals to zero, radix indicators * (0, 0b, and 0x) are honored. * In any case, strings should be strictly conformed to numeric * representation. This behavior is different from that of * String#to_i. Non string values will be converted using * to_int, and to_i. Passing nil * 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 arg converted to a float. Numeric types are converted * directly, the rest are converted using arg.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 arg as an String. * * First tries to call its to_str 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 arg to a Hash by calling * arg.to_hash. Returns an empty Hash when * arg is nil or []. * * 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_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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/test/000077500000000000000000000000001267140355100232335ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-kernel-ext/test/kernel.rb000066400000000000000000000030121267140355100250340ustar00rootroot00000000000000assert('Kernel.fail, Kernel#fail') do assert_raise(RuntimeError) { fail } assert_raise(RuntimeError) { Kernel.fail } 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/000077500000000000000000000000001267140355100211275ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/mrbgem.rake000066400000000000000000000002351267140355100232440ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-math') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Math module' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/src/000077500000000000000000000000001267140355100217165ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/src/math.c000066400000000000000000000370641267140355100230250ustar00rootroot00000000000000/* ** math.c - Math module ** ** See Copyright Notice in mruby.h */ #include #include #include 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 #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 x (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 x (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 x (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 x. * @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 x. 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 x. 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 y and x. 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 x (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 x (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 x (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 x. */ 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 x. */ 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 x. */ 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 numeric. * 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 numeric. * * 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 numeric. * * 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 numeric. * */ 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 numeric. * * -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 * Float) and exponent (a Fixnum) of * numeric. * * 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 flt*(2**int). * * 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 x and y. * * 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/test/000077500000000000000000000000001267140355100221065ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-math/test/math.rb000066400000000000000000000054541267140355100233740ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/000077500000000000000000000000001267140355100224365ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/mrbgem.rake000066400000000000000000000002471267140355100245560ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Numeric class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/mrblib/000077500000000000000000000000001267140355100237055ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb000066400000000000000000000002631267140355100265550ustar00rootroot00000000000000module 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/src/000077500000000000000000000000001267140355100232255ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/src/numeric_ext.c000066400000000000000000000010301267140355100257050ustar00rootroot00000000000000#include #include 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/test/000077500000000000000000000000001267140355100234155ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-numeric-ext/test/numeric.rb000066400000000000000000000007731267140355100254130ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/000077500000000000000000000000001267140355100222425ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/mrbgem.rake000066400000000000000000000002451267140355100243600ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-object-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Object class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/mrblib/000077500000000000000000000000001267140355100235115ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/mrblib/object.rb000066400000000000000000000011511267140355100253020ustar00rootroot00000000000000class Object ## # call-seq: # obj.tap{|x|...} -> obj # # Yields x to the block, and then returns x. # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/src/000077500000000000000000000000001267140355100230315ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/src/object.c000066400000000000000000000042031267140355100244420ustar00rootroot00000000000000#include #include #include /* * 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) { 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; } return mrb_yield_with_class(mrb, blk, argc, argv, self, c); } 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->object_class, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); } void mrb_mruby_object_ext_gem_final(mrb_state* mrb) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/test/000077500000000000000000000000001267140355100232215ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/test/nil.rb000066400000000000000000000002631267140355100243310ustar00rootroot00000000000000assert('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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-object-ext/test/object.rb000066400000000000000000000013471267140355100250210ustar00rootroot00000000000000assert('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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/000077500000000000000000000000001267140355100224605ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/mrbgem.rake000066400000000000000000000002411267140355100245720ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-objectspace') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'ObjectSpace class' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/src/000077500000000000000000000000001267140355100232475ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/src/mruby_objectspace.c000066400000000000000000000106041267140355100271140ustar00rootroot00000000000000#include #include #include #include struct os_count_struct { mrb_int total; mrb_int freed; mrb_int counts[MRB_TT_MAXDEFINE+1]; }; static void 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]++; } } /* * 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 }; enum mrb_vtype 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 void 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; } /* filter internal objects */ switch (obj->tt) { case MRB_TT_ENV: case MRB_TT_ICLASS: return; default: break; } /* filter half baked (or internal) objects */ if (!obj->c) return; /* 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_yield(mrb, d->block, mrb_obj_value(obj)); ++d->count; } /* * 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/test/000077500000000000000000000000001267140355100234375ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-objectspace/test/objectspace.rb000066400000000000000000000030221267140355100262430ustar00rootroot00000000000000assert('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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/000077500000000000000000000000001267140355100213325ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/mrbgem.rake000066400000000000000000000002371267140355100234510ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-print') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard print/puts/p' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/mrblib/000077500000000000000000000000001267140355100226015ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/mrblib/print.rb000066400000000000000000000021241267140355100242610ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/src/000077500000000000000000000000001267140355100221215ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-print/src/print.c000066400000000000000000000025221267140355100234220ustar00rootroot00000000000000#include #include #include #include #include #if defined(__MINGW32__) || defined(__MINGW64__) # include # include #endif static void printstr(mrb_state *mrb, mrb_value obj) { if (mrb_string_p(obj)) { #if defined(__MINGW32__) || defined(__MINGW64__) 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 = 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); } } /* 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/000077500000000000000000000000001267140355100217375ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/mrbgem.rake000066400000000000000000000002411267140355100240510ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-proc-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Proc class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/mrblib/000077500000000000000000000000001267140355100232065ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/mrblib/proc.rb000066400000000000000000000014471267140355100245040ustar00rootroot00000000000000class 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/src/000077500000000000000000000000001267140355100225265ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/src/proc.c000066400000000000000000000104231267140355100236350ustar00rootroot00000000000000#include #include #include #include #include #include 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, "#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) { mrb_str_append(mrb, 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 parameters; int i, j; if (MRB_PROC_CFUNC_P(proc)) { // TODO cfunc aspec is not implemented yet 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++) { mrb_value sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name)); for (j = 0; j < p->size; i++, j++) { mrb_assert(i < (irep->nlocals-1)); mrb_ary_push(mrb, parameters, mrb_assoc_new(mrb, sname, mrb_symbol_value(irep->lv[i].name) )); } } 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/test/000077500000000000000000000000001267140355100227165ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/test/proc.c000066400000000000000000000027531267140355100240340ustar00rootroot00000000000000#include #include #include 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()); } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-proc-ext/test/proc.rb000066400000000000000000000047231267140355100242140ustar00rootroot00000000000000## # 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, :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? 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/000077500000000000000000000000001267140355100214565ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/mrbgem.rake000066400000000000000000000002271267140355100235740ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-random') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Random class' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/src/000077500000000000000000000000001267140355100222455ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/src/mt19937ar.c000066400000000000000000000130341267140355100237720ustar00rootroot00000000000000/* ** mt19937ar.c - MT Random functions ** ** See Copyright Notice in mruby.h */ #include #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->mtimti++) { 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;kkmt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK); t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; } for (;kkmt[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> 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> 1) ^ mag01[y & 0x1UL]; } for (;kk> 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 */ mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/src/mt19937ar.h000066400000000000000000000024431267140355100240010ustar00rootroot00000000000000/* ** mt19937ar.h - MT Random functions ** ** See Copyright Notice in mruby.h */ #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); mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/src/random.c000066400000000000000000000175411267140355100237010ustar00rootroot00000000000000/* ** random.c - Random module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include "mt19937ar.h" #include 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; /* 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 = get_opt(mrb); 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 tmp; j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary)))); tmp = RARRAY_PTR(ary)[i]; mrb_ary_ptr(ary)->ptr[i] = RARRAY_PTR(ary)[j]; mrb_ary_ptr(ary)->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 = RARRAY_LEN(ary); 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); 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; iarray_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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/src/random.h000066400000000000000000000002601267140355100236740ustar00rootroot00000000000000/* ** 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/test/000077500000000000000000000000001267140355100224355ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-random/test/random.rb000066400000000000000000000033061267140355100242440ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/000077500000000000000000000000001267140355100220705ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/mrbgem.rake000066400000000000000000000002431267140355100242040ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-range-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Range class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/src/000077500000000000000000000000001267140355100226575ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/src/range.c000066400000000000000000000104671267140355100241270ustar00rootroot00000000000000#include #include #include 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 true if +obj+ is between the begin and end of * the range. * * This tests begin <= obj <= end when #exclude_end? is +false+ * and begin <= obj < end 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(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.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] */ static mrb_value mrb_range_first(mrb_state *mrb, mrb_value range) { mrb_int num; mrb_value array; struct RRange *r = mrb_range_ptr(range); if (mrb_get_args(mrb, "|i", &num) == 0) { return r->edges->beg; } array = mrb_funcall(mrb, range, "to_a", 0); return mrb_funcall(mrb, array, "first", 1, mrb_fixnum_value(num)); } /* * 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(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(range); mrb_value beg, end; double beg_f, end_f; mrb_bool num_p = TRUE; beg = r->edges->beg; end = r->edges->end; if (mrb_fixnum_p(beg)) { beg_f = (double)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 = (double)mrb_fixnum(end); } else if (mrb_float_p(end)) { end_f = mrb_float(end); } else { num_p = FALSE; } if (num_p) { double f; if (beg_f > end_f) return mrb_fixnum_value(0); f = end_f - beg_f; if (!r->excl) { return mrb_fixnum_value((mrb_int)ceil(f + 1)); } return mrb_fixnum_value((mrb_int)ceil(f)); } 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, "first", mrb_range_first, MRB_ARGS_OPT(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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/test/000077500000000000000000000000001267140355100230475ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-range-ext/test/range.rb000066400000000000000000000012571267140355100244750ustar00rootroot00000000000000## # 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) 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_nil ('a'..'z').size end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/000077500000000000000000000000001267140355100216635ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/mrbgem.rake000066400000000000000000000002521267140355100237770ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-sprintf') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Kernel#sprintf method' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/mrblib/000077500000000000000000000000001267140355100231325ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/mrblib/string.rb000066400000000000000000000002031267140355100247600ustar00rootroot00000000000000class String def %(args) if args.is_a? Array sprintf(self, *args) else sprintf(self, args) end end end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/src/000077500000000000000000000000001267140355100224525ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/src/kernel.c000066400000000000000000000011701267140355100240750ustar00rootroot00000000000000/* ** kernel.c - Kernel module suppliment ** ** See Copyright Notice in mruby.h */ #include 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. */ } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/src/sprintf.c000066400000000000000000001016011267140355100243020ustar00rootroot00000000000000/* ** sprintf.c - Kernel.#sprintf ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ #define BITSPERDIG MRB_INT_BIT #define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (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 (blen + (l) >= bsiz) {\ bsiz*=2;\ }\ 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 += (l);\ } while (0) #define FILL(c, l) do { \ CHECK(l);\ memset(&buf[blen], c, l);\ blen += (l);\ } while (0) #define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : \ posarg == -1 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \ posarg == -2 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \ (posarg = nextarg++, GETNTHARG(posarg))) #define GETPOSARG(n) (posarg > 0 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", mrb_fixnum_value(n), mrb_fixnum_value(posarg)), mrb_undef_value()) : \ posarg == -2 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)), mrb_undef_value()) : \ ((n < 1) ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)), mrb_undef_value()) : \ (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) ( \ posarg > 0 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)), mrb_undef_value()) : \ posarg == -1 ? \ (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))), mrb_undef_value()) : \ (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 = GETARG(); \ p = t; \ } \ num = mrb_fixnum(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 format_string 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 * sprintf 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 %10.10s 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. * %s style uses format style, but %{name} style doesn't. * * Exapmles: * sprintf("%d : %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 flags = FNONE; 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_buf_new(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; for (t = p; t < end && *t != '%'; 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 (mrb_fixnum(mrb_funcall(mrb, tmp, "size", 0)) != 1 ) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_fixnum_p(val)) { tmp = mrb_funcall(mrb, val, "chr", 0); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } c = RSTRING_PTR(tmp); n = RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { CHECK(n); memcpy(buf+blen, c, n); blen += n; } else if ((flags & FMINUS)) { CHECK(n); memcpy(buf+blen, c, n); blen += n; FILL(' ', width-1); } else { FILL(' ', width-1); CHECK(n); memcpy(buf+blen, c, n); blen += 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 = p - RSTRING_PTR(str); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { CHECK(width); while (width--) { buf[blen++] = ' '; } } CHECK(len); memcpy(&buf[blen], RSTRING_PTR(str), len); blen += len; if (flags&FMINUS) { CHECK(width); while (width--) { buf[blen++] = ' '; } } 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; switch (*p) { case 'd': case 'i': case 'u': sign = 1; break; case 'o': case 'x': case 'X': case 'b': case 'B': if (flags&(FPLUS|FSPACE)) sign = 1; break; default: break; } 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': default: base = 10; break; } if (base == 2) { if (v < 0 && !sign) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); dots = 1; } else { val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); } } if (sign) { if (v < 0) { v = -v; sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } switch (base) { case 2: strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf)); break; case 8: snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIo, v); break; case 10: snprintf(nbuf, sizeof(nbuf), "%"MRB_PRId, v); break; case 16: snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIx, v); break; } s = nbuf; } else { s = nbuf; if (v < 0) { dots = 1; } switch (base) { case 2: strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIo, v); break; case 10: snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRId, 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 (dots) { prec -= 2; width -= 2; } 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)) { CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } CHECK(prec - len); if (dots) PUSH("..", 2); if (v < 0) { char c = sign_bits(base, p); while (len < prec--) { buf[blen++] = c; } } else if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; while (len < prec--) { buf[blen++] = c; } } PUSH(s, len); CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } 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; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) need++; if ((flags & FWIDTH) && need < width) need = width; CHECK(need + 1); snprintf(&buf[blen], need + 1, "%*s", need, ""); if (flags & FMINUS) { if (!isnan(fval) && fval < 0.0) buf[blen++] = '-'; else if (flags & FPLUS) buf[blen++] = '+'; else if (flags & FSPACE) blen++; memcpy(&buf[blen], expr, elen); } else { if (!isnan(fval) && fval < 0.0) buf[blen + need - elen - 1] = '-'; else if (flags & FPLUS) buf[blen + need - elen - 1] = '+'; else if ((flags & FSPACE) && need > width) blen++; memcpy(&buf[blen + need - elen], expr, elen); } blen += strlen(&buf[blen]); 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; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); 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'; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/test/000077500000000000000000000000001267140355100226425ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-sprintf/test/sprintf.rb000066400000000000000000000004241267140355100246540ustar00rootroot00000000000000#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} < %s" % { num: 123, str: "456" } assert_equal 16, ("%b" % (1<<15)).size end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/000077500000000000000000000000001267140355100223025ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/mrbgem.rake000066400000000000000000000002451267140355100244200ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-string-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'String class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/mrblib/000077500000000000000000000000001267140355100235515ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/mrblib/string.rb000066400000000000000000000241451267140355100254120ustar00rootroot00000000000000class String ## # call-seq: # String.try_convert(obj) -> string or nil # # Try to convert obj into a String, using to_str method. # Returns converted string or nil if obj 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 str with leading whitespace removed. See also # String#rstrip and String#strip. # # " hello ".lstrip #=> "hello " # "hello".lstrip #=> "hello" # def lstrip a = 0 z = self.size - 1 a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.rstrip -> new_str # # Returns a copy of str with trailing whitespace removed. See also # String#lstrip and String#strip. # # " hello ".rstrip #=> " hello" # "hello".rstrip #=> "hello" # def rstrip a = 0 z = self.size - 1 z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.strip -> new_str # # Returns a copy of str 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 " \f\n\r\t\v".include?(self[a]) and a <= z z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.lstrip! -> self or nil # # Removes leading whitespace from str, returning nil if no # change was made. See also String#rstrip! and # String#strip!. # # " hello ".lstrip #=> "hello " # "hello".lstrip! #=> nil # def lstrip! s = self.lstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.rstrip! -> self or nil # # Removes trailing whitespace from str, returning nil if # no change was made. See also String#lstrip! and # String#strip!. # # " hello ".rstrip #=> " hello" # "hello".rstrip! #=> nil # def rstrip! s = self.rstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.strip! -> str or nil # # Removes leading and trailing whitespace from str. Returns # nil if str was not altered. # def strip! 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 String#<=>. # # "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 str, 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 "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 other_str before the character at the given # index, modifying str. Negative indices count from the # end of the string, and insert after the given character. # The intent is insert aString so that it starts at the given # index. # # "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) pos = idx.to_i pos += self.size + 1 if pos < 0 raise IndexError, "index #{idx.to_i} out of string" if pos < 0 || pos > self.size return self + str if pos == -1 return str + self if pos == 0 return self[0..pos - 1] + str + self[pos..-1] end ## # call-seq: # str.ljust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str left justified # and padded with padstr; otherwise, returns str. # # "hello".ljust(4) #=> "hello" # "hello".ljust(20) #=> "hello " # "hello".ljust(20, '1234') #=> "hello123412341234123" def ljust(idx, padstr = ' ') if idx <= self.size return self end newstr = self.dup newstr << padstr while newstr.size <= idx newstr << padstr end return newstr.slice(0,idx) end ## # call-seq: # str.rjust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str right justified # and padded with padstr; otherwise, returns str. # # "hello".rjust(4) #=> "hello" # "hello".rjust(20) #=> " hello" # "hello".rjust(20, '1234') #=> "123412341234123hello" def rjust(idx, padstr = ' ') if idx <= self.size return self end padsize = idx - self.size newstr = padstr.dup while newstr.size <= padsize newstr << padstr end return newstr.slice(0,padsize) + self end # str.upto(other_str, exclusive=false) {|s| block } -> str # str.upto(other_str, exclusive=false) -> an_enumerator # # Iterates through successive values, starting at str and # ending at other_str inclusive, passing each value in turn to # the block. The String#succ 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 # # produces: # # a8 a9 b0 b1 b2 b3 b4 b5 b6 # a8 a9 b0 b1 b2 b3 b4 b5 b6 # # If str and other_str 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"] # def upto(other_str, excl=false, &block) return to_enum :upto, other_str, excl unless block str = self n = self.<=>other_str return self if n > 0 || (self == other_str && excl) while true block.call(str) return self if !excl && str == other_str str = str.succ return self if excl && str == other_str end end def chars(&block) if block_given? self.split('').map do |i| block.call(i) end self else self.split('') end end alias each_char chars def codepoints(&block) len = self.size if block_given? self.split('').map do|x| block.call(x.ord) end self else self.split('').map{|x| x.ord} end end alias each_codepoint codepoints end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/src/000077500000000000000000000000001267140355100230715ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/src/string.c000066400000000000000000000350111267140355100245430ustar00rootroot00000000000000#include #include #include #include #include #include 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 = RSTRING_LEN(str); mrb_get_args(mrb, "ii", &pos, &byte); 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); if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) { return mrb_str_substr(mrb, str, beg, len); } 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 String#swapcase, but modifies the receiver in * place, returning str, or nil 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 str 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; } /* * call-seq: * str << integer -> str * str.concat(integer) -> str * str << obj -> str * str.concat(obj) -> str * * Append---Concatenates the given object to str. If the object is a * Integer, 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_concat2(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "S", &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 *p = RSTRING_PTR(self), *t; char *e = p + RSTRING_LEN(self); mrb_get_args(mrb, "&", &blk); result = mrb_ary_new(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); } return self; } while (p < e) { ai = mrb_gc_arena_save(mrb); 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); size_t 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; } /* * call-seq: * str.prepend(other_str) -> str * * Prepend---Prepend the given string to str. * * a = "world" * a.prepend("hello ") #=> "hello world" * a #=> "hello world" */ static mrb_value mrb_str_prepend(mrb_state *mrb, mrb_value self) { struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s; mrb_int len; mrb_value other, temp_str; mrb_get_args(mrb, "S", &other); mrb_str_modify(mrb, s1); if (!mrb_string_p(other)) { other = mrb_str_to_str(mrb, other); } s2 = mrb_str_ptr(other); len = RSTR_LEN(s1) + RSTR_LEN(s2); temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1)); temp_s = mrb_str_ptr(temp_str); memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1)); if (RSTRING_CAPA(self) < len) { mrb_str_resize(mrb, self, len); } memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2)); memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s)); RSTR_SET_LEN(s1, len); RSTR_PTR(s1)[len] = '\0'; return self; } #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(RSTRING_PTR(str)[0]); } #endif 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_concat2, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "<<", mrb_str_concat2, 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_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1)); 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, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } void mrb_mruby_string_ext_gem_final(mrb_state* mrb) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/test/000077500000000000000000000000001267140355100232615ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-string-ext/test/string.rb000066400000000000000000000326051267140355100251220ustar00rootroot00000000000000## # 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#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 s = "Hello " s.concat "World!" t = "Hello " t << "World!" assert_equal "Hello World!", t assert_equal "Hello World!", s 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') } 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 "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 "123412341234123hello", "hello".rjust(20, '1234') assert_equal "hello", "hello".rjust(-3) end assert('String#upto') do 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) end assert('String#ord') do got = "hello!".split('').map {|x| x.ord} expect = [104, 101, 108, 108, 111, 33] 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/000077500000000000000000000000001267140355100215225ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/mrbgem.rake000066400000000000000000000002401267140355100236330ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Struct class' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/mrblib/000077500000000000000000000000001267140355100227715ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/mrblib/struct.rb000066400000000000000000000031671267140355100246510ustar00rootroot00000000000000## # 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 = "#" 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 "#" end end ## # 15.2.18.4.11(x) # alias to_s inspect end end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/src/000077500000000000000000000000001267140355100223115ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/src/struct.c000066400000000000000000000542511267140355100240100ustar00rootroot00000000000000/* ** struct.c - Struct class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len #define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr 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)) { 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; } /* 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 mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) { mrb_value members, slot, *ptr; const mrb_value *ptr_members; mrb_int i, len; ptr = RSTRUCT_PTR(obj); members = struct_members(mrb, obj); ptr_members = RARRAY_PTR(members); slot = mrb_symbol_value(id); len = RARRAY_LEN(members); for (i=0; ic->ci->mid); } static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];} static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];} static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];} static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];} static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];} static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];} static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];} static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];} static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];} static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];} #define numberof(array) (int)(sizeof(array) / sizeof((array)[0])) #define N_REF_FUNC numberof(ref_func) static const mrb_func_t ref_func[] = { mrb_struct_ref0, mrb_struct_ref1, mrb_struct_ref2, mrb_struct_ref3, mrb_struct_ref4, mrb_struct_ref5, mrb_struct_ref6, mrb_struct_ref7, mrb_struct_ref8, mrb_struct_ref9, }; 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_set(mrb_state *mrb, mrb_value obj, mrb_value val) { const char *name; mrb_int i, len, slen; mrb_sym mid; mrb_value members, slot, *ptr; const mrb_value *ptr_members; /* get base id */ name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen); mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */ members = struct_members(mrb, obj); ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); ptr = RSTRUCT_PTR(obj); for (i=0; ibasic.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 aString, containing accessor * methods for the given symbols. If the name aString is * omitted, an anonymous structure class will be created. Otherwise, * the name of this struct will appear as a constant in class * Struct, so it must be unique for all * Structs 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. * * Struct::new returns a new Class 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 nil. Passing too many * parameters will raise an ArgumentError. * * 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") #=> # * * # Create a structure named by its constant * Customer = Struct.new(:name, :address) #=> Customer * Customer.new("Dave", "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(); rest = 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]; if (argc > 1) rest = argv[1]; if (mrb_array_p(rest)) { if (!mrb_nil_p(name) && mrb_symbol_p(name)) { /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ mrb_ary_unshift(mrb, rest, name); name = mrb_nil_value(); } } else { 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 anObject * struct[fixnum] -> anObject * * Attribute Reference---Returns the value of the instance variable * named by symbol, or indexed (0..length-1) by * fixnum. Will raise NameError if the named * variable does not exist, or IndexError 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); if (RSTRUCT_LEN(s) != len) { mrb_raisef(mrb, E_TYPE_ERROR, "struct size differs (%S required %S given)", mrb_fixnum_value(len), mrb_fixnum_value(RSTRUCT_LEN(s))); } ptr = RSTRUCT_PTR(s); ptr_members = RARRAY_PTR(members); for (i=0; i obj * struct[fixnum] = obj -> obj * * Attribute Assignment---Assigns to the instance variable named by * symbol or fixnum the value obj and * returns it. Will raise a NameError if the named * variable does not exist, or an IndexError 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))); } return RSTRUCT_PTR(s)[i] = val; } /* 15.2.18.4.1 */ /* * call-seq: * struct == other_struct -> true or false * * Equality---Returns true if other_struct is * equal to this one: they must be of the same class as generated by * Struct::new, and the values of all instance variables * must be equal (according to Object#==). * * 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 true or false * * Two structures are equal if they are the same object, or if all their * fields are equal (using eql?). */ 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 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_s_members(mrb, mrb_class(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 Struct is a convenient way to bundle a number of * attributes together, using accessor methods, without having to write * an explicit class. * * The Struct 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 * "CustomerClass," and we'll show an example instance of that * class as "CustomerInst." * * In the descriptions that follow, the parameter symbol refers * to a symbol, which is either a quoted string or a * Symbol (such as :name). */ void mrb_mruby_struct_gem_init(mrb_state* mrb) { struct RClass *st; st = mrb_define_class(mrb, "Struct", mrb->object_class); 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_NONE()); } void mrb_mruby_struct_gem_final(mrb_state* mrb) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/test/000077500000000000000000000000001267140355100225015ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-struct/test/struct.rb000066400000000000000000000066161267140355100243630ustar00rootroot00000000000000## # 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 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 "#", 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/000077500000000000000000000000001267140355100223015ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/mrbgem.rake000066400000000000000000000002451267140355100244170ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Symbol class extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/mrblib/000077500000000000000000000000001267140355100235505ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb000066400000000000000000000020771267140355100254100ustar00rootroot00000000000000class 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 sym.to_s.capitalize.intern. def capitalize (self.to_s.capitalize! || self).to_sym end ## # call-seq: # sym.downcase -> symbol # # Same as sym.to_s.downcase.intern. def downcase (self.to_s.downcase! || self).to_sym end ## # call-seq: # sym.upcase -> symbol # # Same as sym.to_s.upcase.intern. def upcase (self.to_s.upcase! || self).to_sym end ## # call-seq: # sym.casecmp(other) -> -1, 0, +1 or nil # # Case-insensitive version of Symbol#<=>. 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/src/000077500000000000000000000000001267140355100230705ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/src/symbol.c000066400000000000000000000031541267140355100245440ustar00rootroot00000000000000#include #include #include 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 integer * * Same as sym.to_s.length. */ 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/test/000077500000000000000000000000001267140355100232605ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-symbol-ext/test/symbol.rb000066400000000000000000000021101267140355100251040ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-test/000077500000000000000000000000001267140355100211555ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-test/README.md000066400000000000000000000001721267140355100224340ustar00rootroot00000000000000Running Tests ============= To run the tests, execute the following from the project's root directory. $ make test mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-test/driver.c000066400000000000000000000077451267140355100226310ustar00rootroot00000000000000/* ** mrbtest - Test for Embeddable Ruby ** ** This program runs Ruby test programs in test/t directory ** against the current mruby implementation. */ #include #include #include #include #include #include #include #include #include #include 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)); 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; } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-test/init_mrbtest.c000066400000000000000000000016421267140355100240270ustar00rootroot00000000000000#include #include #include #include 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); } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-test/mrbgem.rake000066400000000000000000000164121267140355100232760ustar00rootroot00000000000000MRuby::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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/000077500000000000000000000000001267140355100211345ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/mrbgem.rake000066400000000000000000000002341267140355100232500ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-time') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Time class' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/mrblib/000077500000000000000000000000001267140355100224035ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/mrblib/time.rb000066400000000000000000000003511267140355100236650ustar00rootroot00000000000000class 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/src/000077500000000000000000000000001267140355100217235ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/src/time.c000066400000000000000000000560071267140355100230350ustar00rootroot00000000000000/* ** time.c - Time class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #if _MSC_VER < 1800 double round(double x) { if (x >= 0.0) { return (double)((int)(x + 0.5)); } else { return (double)((int)(x - 0.5)); } } #endif #if !defined(__MINGW64__) && defined(_WIN32) # define llround(x) round(x) #endif #if defined(__MINGW64__) || defined(__MINGW32__) # include #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 /* 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 # 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 # 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 }, }; 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", }; 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* mrb_time_update_datetime(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) 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)); } /* 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; tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); 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; } tm->sec = (time_t)sec; if ((sec > 0 && tm->sec < 0) || (sec < 0 && (double)tm->sec > sec)) { out_of_range: mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec)); } tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); while (tm->usec < 0) { tm->sec--; tm->usec += 1000000; } while (tm->usec >= 1000000) { tm->sec++; tm->usec -= 1000000; } tm->timezone = timezone; mrb_time_update_datetime(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; mrb_time_update_datetime(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 (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 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_CHECK_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_CHECK_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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = 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 (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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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; struct tm *d; char buf[256]; int len; tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); d = &tm->datetime; 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); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); if (!tm) return mrb_nil_value(); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_UTC; mrb_time_update_datetime(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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_LOCAL; mrb_time_update_datetime(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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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; tm = (struct mrb_time*)DATA_PTR(self); if (tm) { mrb_free(mrb, tm); } mrb_data_init(self, NULL, &mrb_time_type); n = mrb_get_args(mrb, "|iiiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); 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; 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"); } if (!DATA_PTR(copy)) { mrb_data_init(copy, mrb_malloc(mrb, sizeof(struct mrb_time)), &mrb_time_type); } *(struct mrb_time *)DATA_PTR(copy) = *(struct mrb_time *)DATA_PTR(src); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm->timezone = MRB_TIMEZONE_LOCAL; mrb_time_update_datetime(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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm->timezone = MRB_TIMEZONE_UTC; mrb_time_update_datetime(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 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); 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) { } mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/test/000077500000000000000000000000001267140355100221135ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-time/test/time.rb000066400000000000000000000103431267140355100233770ustar00rootroot00000000000000## # 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 Time.at(1300000000.0) 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) t2.utc.asctime == "Sun Mar 13 07:07:40 UTC 2011" end assert('Time#-', '15.2.19.7.2') do t1 = Time.at(1300000000.0) t2 = t1.-(60) t2.utc.asctime == "Sun Mar 13 07:05:40 UTC 2011" 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 mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/000077500000000000000000000000001267140355100226265ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/mrbgem.rake000066400000000000000000000002711267140355100247430ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'toplevel object (main) methods extension' end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/mrblib/000077500000000000000000000000001267140355100240755ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb000066400000000000000000000002371267140355100262560ustar00rootroot00000000000000 def self.include (*modules) self.class.include(*modules) end def self.private(*methods) end def self.protected(*methods) end def self.public(*methods) end mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/test/000077500000000000000000000000001267140355100236055ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrbgems/mruby-toplevel-ext/test/toplevel.rb000066400000000000000000000007501267140355100257660ustar00rootroot00000000000000## # 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 mruby-1.2.0+20160315+git4f20d58a/mrblib/000077500000000000000000000000001267140355100166555ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/mrblib/array.rb000066400000000000000000000103311267140355100203160ustar00rootroot00000000000000## # Array # # ISO 15.2.12 class Array ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.12.5.10 def each(&block) return to_enum :each unless block_given? idx = 0 while idx < length block.call(self[idx]) idx += 1 end self end ## # Calls the given block for each element of +self+ # and pass the index of the respective element. # # ISO 15.2.12.5.11 def each_index(&block) return to_enum :each_index unless block_given? idx = 0 while idx < length block.call(idx) idx += 1 end self end ## # Calls the given block for each element of +self+ # and pass the respective element. Each element will # be replaced by the resulting values. # # ISO 15.2.12.5.7 def collect!(&block) return to_enum :collect! unless block_given? self.each_index { |idx| self[idx] = block.call(self[idx]) } self end ## # Alias for collect! # # ISO 15.2.12.5.20 alias map! collect! ## # Private method for Array creation. # # ISO 15.2.12.5.15 def initialize(size=0, obj=nil, &block) raise TypeError, "expected Integer for 1st argument" unless size.kind_of? Integer raise ArgumentError, "negative array size" if size < 0 self.clear if size > 0 self[size - 1] = nil # allocate idx = 0 while idx < size self[idx] = (block)? block.call(idx): obj idx += 1 end end self end def _inspect return "[]" if self.size == 0 "["+self.map{|x|x.inspect}.join(", ")+"]" end ## # Return the contents of this array as a string. # # ISO 15.2.12.5.31 (x) def inspect begin self._inspect rescue SystemStackError "[...]" end end # ISO 15.2.12.5.32 (x) alias to_s inspect ## # Equality---Two arrays are equal if they contain the same number # of elements and if each element is equal to (according to # Object.==) the corresponding element in the other array. # # ISO 15.2.12.5.33 (x) def ==(other) other = self.__ary_eq(other) return false if other == false return true if other == true len = self.size i = 0 while i < len return false if self[i] != other[i] i += 1 end return true end ## # Returns true if +self+ and _other_ are the same object, # or are both arrays with the same content. # # ISO 15.2.12.5.34 (x) def eql?(other) other = self.__ary_eq(other) return false if other == false return true if other == true len = self.size i = 0 while i < len return false unless self[i].eql?(other[i]) i += 1 end return true end ## # Comparison---Returns an integer (-1, 0, or +1) # if this array is less than, equal to, or greater than other_ary. # Each object in each array is compared (using <=>). If any value isn't # equal, then that inequality is the return value. If all the # values found are equal, then the return is based on a # comparison of the array lengths. Thus, two arrays are # "equal" according to Array#<=> if and only if they have # the same length and the value of each element is equal to the # value of the corresponding element in the other array. # # ISO 15.2.12.5.36 (x) def <=>(other) other = self.__ary_cmp(other) return 0 if 0 == other return nil if nil == other len = self.size n = other.size len = n if len > n i = 0 while i < len n = (self[i] <=> other[i]) return n if n.nil? || n != 0 i += 1 end len = self.size - other.size if len == 0 0 elsif len > 0 1 else -1 end end ## # Delete element with index +key+ def delete(key, &block) while i = self.index(key) self.delete_at(i) ret = key end return block.call if ret.nil? && block ret end # internal method to convert multi-value to single value def __svalue return self.first if self.size < 2 self end end ## # Array is enumerable class Array # ISO 15.2.12.3 include Enumerable ## # Sort all elements and replace +self+ with these # elements. def sort!(&block) self.replace(self.sort(&block)) end end mruby-1.2.0+20160315+git4f20d58a/mrblib/class.rb000066400000000000000000000002611267140355100203060ustar00rootroot00000000000000class Module # 15.2.2.4.12 def attr_accessor(*names) attr_reader(*names) attr_writer(*names) end # 15.2.2.4.11 def attr(name) attr_reader(name) end end mruby-1.2.0+20160315+git4f20d58a/mrblib/compar.rb000066400000000000000000000030431267140355100204630ustar00rootroot00000000000000## # Comparable # # ISO 15.3.3 module Comparable ## # Return true if +self+ is less # than +other+. Otherwise return # false. # # ISO 15.3.3.2.1 def < other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp < 0 end ## # Return true if +self+ is less # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.2 def <= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp <= 0 end ## # Return true if +self+ is equal # to +other+. Otherwise return # false. # # ISO 15.3.3.2.3 def == other cmp = self <=> other cmp == 0 end ## # Return true if +self+ is greater # than +other+. Otherwise return # false. # # ISO 15.3.3.2.4 def > other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp > 0 end ## # Return true if +self+ is greater # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.5 def >= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp >= 0 end ## # Return true if +self+ is greater # than or equal to +min+ and # less than or equal to +max+. # Otherwise return false. # # ISO 15.3.3.2.6 def between?(min, max) self >= min and self <= max end end mruby-1.2.0+20160315+git4f20d58a/mrblib/enum.rb000066400000000000000000000204501267140355100201470ustar00rootroot00000000000000## # Enumerable # # The Enumerable mixin provides collection classes with # several traversal and searching methods, and with the ability to # sort. The class must provide a method `each`, which # yields successive members of the collection. If # {Enumerable#max}, {#min}, or # {#sort} is used, the objects in the collection must also # implement a meaningful `<=>` operator, as these methods # rely on an ordering between members of the collection. # # @ISO 15.3.2 module Enumerable ## # Call the given block for each element # which is yield by +each+. Return false # if one block value is false. Otherwise # return true. If no block is given and # +self+ is false return false. # # ISO 15.3.2.2.1 def all?(&block) if block self.each{|*val| return false unless block.call(*val)} else self.each{|*val| return false unless val.__svalue} end true end ## # Call the given block for each element # which is yield by +each+. Return true # if one block value is true. Otherwise # return false. If no block is given and # +self+ is true object return true. # # ISO 15.3.2.2.2 def any?(&block) if block self.each{|*val| return true if block.call(*val)} else self.each{|*val| return true if val.__svalue} end false end ## # Call the given block for each element # which is yield by +each+. Append all # values of each block together and # return this value. # # ISO 15.3.2.2.3 def collect(&block) return to_enum :collect unless block ary = [] self.each{|*val| ary.push(block.call(*val))} ary end ## # Call the given block for each element # which is yield by +each+. Return # +ifnone+ if no block value was true. # Otherwise return the first block value # which had was true. # # ISO 15.3.2.2.4 def detect(ifnone=nil, &block) ret = ifnone self.each{|*val| if block.call(*val) ret = val.__svalue break end } ret end ## # Call the given block for each element # which is yield by +each+. Pass an # index to the block which starts at 0 # and increase by 1 for each element. # # ISO 15.3.2.2.5 def each_with_index(&block) return to_enum :each_with_index unless block i = 0 self.each{|*val| block.call(val.__svalue, i) i += 1 } self end ## # Return an array of all elements which # are yield by +each+. # # ISO 15.3.2.2.6 def entries ary = [] self.each{|*val| # __svalue is an internal method ary.push val.__svalue } ary end ## # Alias for find # # ISO 15.3.2.2.7 alias find detect ## # Call the given block for each element # which is yield by +each+. Return an array # which contains all elements whose block # value was true. # # ISO 15.3.2.2.8 def find_all(&block) return to_enum :find_all unless block ary = [] self.each{|*val| ary.push(val.__svalue) if block.call(*val) } ary end ## # Call the given block for each element # which is yield by +each+ and which return # value was true when invoking === with # +pattern+. Return an array with all # elements or the respective block values. # # ISO 15.3.2.2.9 def grep(pattern, &block) ary = [] self.each{|*val| sv = val.__svalue if pattern === sv ary.push((block)? block.call(*val): sv) end } ary end ## # Return true if at least one element which # is yield by +each+ returns a true value # by invoking == with +obj+. Otherwise return # false. # # ISO 15.3.2.2.10 def include?(obj) self.each{|*val| return true if val.__svalue == obj } false end ## # Call the given block for each element # which is yield by +each+. Return value # is the sum of all block values. Pass # to each block the current sum and the # current element. # # ISO 15.3.2.2.11 def inject(*args, &block) raise ArgumentError, "too many arguments" if args.size > 2 if Symbol === args[-1] sym = args[-1] block = ->(x,y){x.__send__(sym,y)} args.pop end if args.empty? flag = true # no initial argument result = nil else flag = false result = args[0] end self.each{|*val| val = val.__svalue if flag # push first element as initial flag = false result = val else result = block.call(result, val) end } result end alias reduce inject ## # Alias for collect # # ISO 15.3.2.2.12 alias map collect ## # Return the maximum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.13 def max(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) > 0 else result = val if (val <=> result) > 0 end end } result end ## # Return the minimum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.14 def min(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) < 0 else result = val if (val <=> result) < 0 end end } result end ## # Alias for include? # # ISO 15.3.2.2.15 alias member? include? ## # Call the given block for each element # which is yield by +each+. Return an # array which contains two arrays. The # first array contains all elements # whose block value was true. The second # array contains all elements whose # block value was false. # # ISO 15.3.2.2.16 def partition(&block) ary_T = [] ary_F = [] self.each{|*val| if block.call(*val) ary_T.push(val.__svalue) else ary_F.push(val.__svalue) end } [ary_T, ary_F] end ## # Call the given block for each element # which is yield by +each+. Return an # array which contains only the elements # whose block value was false. # # ISO 15.3.2.2.17 def reject(&block) ary = [] self.each{|*val| ary.push(val.__svalue) unless block.call(*val) } ary end ## # Alias for find_all. # # ISO 15.3.2.2.18 alias select find_all ## # TODO # Does this OK? Please test it. def __sort_sub__(sorted, work, src_ary, head, tail, &block) if head == tail sorted[head] = work[head] if src_ary == 1 return end # on current step, which is a src ary? if src_ary == 0 src, dst = sorted, work else src, dst = work, sorted end key = src[head] # key value for dividing values i, j = head, tail # position to store on the dst ary (head + 1).upto(tail){|idx| if ((block)? block.call(src[idx], key): (src[idx] <=> key)) > 0 # larger than key dst[j] = src[idx] j -= 1 else dst[i] = src[idx] i += 1 end } sorted[i] = key # sort each sub-array src_ary = (src_ary + 1) % 2 # exchange a src ary __sort_sub__(sorted, work, src_ary, head, i - 1, &block) if i > head __sort_sub__(sorted, work, src_ary, i + 1, tail, &block) if i < tail end # private :__sort_sub__ ## # Return a sorted array of all elements # which are yield by +each+. If no block # is given <=> will be invoked on each # element to define the order. Otherwise # the given block will be used for # sorting. # # ISO 15.3.2.2.19 def sort(&block) ary = [] self.each{|*val| ary.push(val.__svalue)} if ary.size > 1 __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1, &block) end ary end ## # Alias for entries. # # ISO 15.3.2.2.20 alias to_a entries # redefine #hash 15.3.1.3.15 def hash h = 12347 i = 0 self.each do |e| n = (e.hash & (0x7fffffff >> (i % 16))) << (i % 16) h ^= n i += 1 end h end end mruby-1.2.0+20160315+git4f20d58a/mrblib/error.rb000066400000000000000000000014701267140355100203350ustar00rootroot00000000000000# ISO 15.2.24 class ArgumentError < StandardError end # ISO 15.2.25 class LocalJumpError < StandardError end # ISO 15.2.26 class RangeError < StandardError end class FloatDomainError < RangeError end # ISO 15.2.26 class RegexpError < StandardError end # ISO 15.2.29 class TypeError < StandardError end # ISO 15.2.31 class NameError < StandardError attr_accessor :name def initialize(message=nil, name=nil) @name = name super(message) end end # ISO 15.2.32 class NoMethodError < NameError attr_reader :args def initialize(message=nil, name=nil, args=nil) @args = args super message, name end end # ISO 15.2.33 class IndexError < StandardError end class KeyError < IndexError end class NotImplementedError < ScriptError end class StopIteration < IndexError attr_accessor :result end mruby-1.2.0+20160315+git4f20d58a/mrblib/hash.rb000066400000000000000000000176721267140355100201420ustar00rootroot00000000000000## # Hash # # ISO 15.2.13 class Hash ## # Equality---Two hashes are equal if they each contain the same number # of keys and if each key-value pair is equal to (according to # Object#==) the corresponding elements in the other # hash. # # ISO 15.2.13.4.1 def ==(hash) return true if self.equal?(hash) begin hash = hash.to_hash rescue NoMethodError return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k] == hash[k] end return true end ## # Returns true if hash and other are # both hashes with the same content compared by eql?. # # ISO 15.2.13.4.32 (x) def eql?(hash) return true if self.equal?(hash) begin hash = hash.to_hash rescue NoMethodError return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k].eql?(hash[k]) end return true end ## # Delete the element with the key +key+. # Return the value of the element if +key+ # was found. Return nil if nothing was # found. If a block is given, call the # block with the value of the element. # # ISO 15.2.13.4.8 def delete(key, &block) if block && !self.has_key?(key) block.call(key) else self.__delete(key) end end ## # Calls the given block for each element of +self+ # and pass the key and value of each element. # # call-seq: # hsh.each {| key, value | block } -> hsh # hsh.each_pair {| key, value | block } -> hsh # hsh.each -> an_enumerator # hsh.each_pair -> an_enumerator # # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each {|key, value| puts "#{key} is #{value}" } # # produces: # # a is 100 # b is 200 # # ISO 15.2.13.4.9 def each(&block) return to_enum :each unless block_given? keys = self.keys vals = self.values len = self.size i = 0 while i < len block.call [keys[i], vals[i]] i += 1 end self end ## # Calls the given block for each element of +self+ # and pass the key of each element. # # call-seq: # hsh.each_key {| key | block } -> hsh # hsh.each_key -> an_enumerator # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_key {|key| puts key } # # produces: # # a # b # # ISO 15.2.13.4.10 def each_key(&block) return to_enum :each_key unless block_given? self.keys.each{|k| block.call(k)} self end ## # Calls the given block for each element of +self+ # and pass the value of each element. # # call-seq: # hsh.each_value {| value | block } -> hsh # hsh.each_value -> an_enumerator # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_value {|value| puts value } # # produces: # # 100 # 200 # # ISO 15.2.13.4.11 def each_value(&block) return to_enum :each_value unless block_given? self.keys.each{|k| block.call(self[k])} self end ## # Replaces the contents of hsh with the contents of other hash # # ISO 15.2.13.4.23 def replace(hash) raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash) self.clear hash = hash.to_hash hash.each_key{|k| self[k] = hash[k] } if hash.default_proc self.default_proc = hash.default_proc else self.default = hash.default end self end # ISO 15.2.13.4.17 alias initialize_copy replace ## # Return a hash which contains the content of # +self+ and +other+. If a block is given # it will be called for each element with # a duplicate key. The value of the block # will be the final value of this element. # # ISO 15.2.13.4.22 def merge(other, &block) h = {} raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash) other = other.to_hash self.each_key{|k| h[k] = self[k]} if block other.each_key{|k| h[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k] } else other.each_key{|k| h[k] = other[k]} end h end # internal method for Hash inspection def _inspect return "{}" if self.size == 0 "{"+self.map {|k,v| k._inspect + "=>" + v._inspect }.join(", ")+"}" end ## # Return the contents of this hash as a string. # # ISO 15.2.13.4.30 (x) def inspect begin self._inspect rescue SystemStackError "{...}" end end # ISO 15.2.13.4.31 (x) alias to_s inspect ## # call-seq: # hsh.reject! {| key, value | block } -> hsh or nil # hsh.reject! -> an_enumerator # # Equivalent to Hash#delete_if, but returns # nil if no changes were made. # # 1.8/1.9 Hash#reject! returns Hash; ISO says nothing. # def reject!(&b) return to_enum :reject! unless block_given? keys = [] self.each{|k,v| if b.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.reject {|key, value| block} -> a_hash # hsh.reject -> an_enumerator # # Returns a new hash consisting of entries for which the block returns false. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.reject {|k,v| k < "b"} #=> {"b" => 200, "c" => 300} # h.reject {|k,v| v > 100} #=> {"a" => 100} # # 1.8/1.9 Hash#reject returns Hash; ISO says nothing. # def reject(&b) return to_enum :reject unless block_given? h = {} self.each{|k,v| unless b.call([k, v]) h[k] = v end } h end ## # call-seq: # hsh.select! {| key, value | block } -> hsh or nil # hsh.select! -> an_enumerator # # Equivalent to Hash#keep_if, but returns # nil if no changes were made. # # 1.9 Hash#select! returns Hash; ISO says nothing. # def select!(&b) return to_enum :select! unless block_given? keys = [] self.each{|k,v| unless b.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.select {|key, value| block} -> a_hash # hsh.select -> an_enumerator # # Returns a new hash consisting of entries for which the block returns true. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300} # h.select {|k,v| v < 200} #=> {"a" => 100} # # 1.9 Hash#select returns Hash; ISO says nothing # def select(&b) return to_enum :select unless block_given? h = {} self.each{|k,v| if b.call([k, v]) h[k] = v end } h end ## # call-seq: # hsh.rehash -> hsh # # Rebuilds the hash based on the current hash values for each key. If # values of key objects have changed since they were inserted, this # method will reindex hsh. # # h = {"AAA" => "b"} # h.keys[0].chop! # h #=> {"AA"=>"b"} # h["AA"] #=> nil # h.rehash #=> {"AA"=>"b"} # h["AA"] #=> "b" # def rehash h = {} self.each{|k,v| h[k] = v } self.replace(h) end def __update(h) h.each_key{|k| self[k] = h[k]} self end end ## # Hash is enumerable # # ISO 15.2.13.3 class Hash include Enumerable end mruby-1.2.0+20160315+git4f20d58a/mrblib/init_mrblib.c000066400000000000000000000002361267140355100213140ustar00rootroot00000000000000#include #include extern const uint8_t mrblib_irep[]; void mrb_init_mrblib(mrb_state *mrb) { mrb_load_irep(mrb, mrblib_irep); } mruby-1.2.0+20160315+git4f20d58a/mrblib/kernel.rb000066400000000000000000000014371267140355100204670ustar00rootroot00000000000000## # Kernel # # ISO 15.3.1 module Kernel # 15.3.1.2.1 Kernel.` # provided by Kernel#` # 15.3.1.3.5 def `(s) raise NotImplementedError.new("backquotes not implemented") end ## # 15.3.1.2.3 Kernel.eval # 15.3.1.3.12 Kernel#eval # NotImplemented by mruby core; use mruby-eval gem ## # ISO 15.3.1.2.8 Kernel.loop # provided by Kernel#loop ## # Calls the given block repetitively. # # ISO 15.3.1.3.29 def loop(&block) return to_enum :loop unless block while true yield end rescue StopIteration => e e.result end # 11.4.4 Step c) def !~(y) !(self =~ y) end # internal method for inspect def _inspect self.inspect end def to_enum(*a) raise NotImplementedError.new("fiber required for enumerator") end end mruby-1.2.0+20160315+git4f20d58a/mrblib/mrblib.rake000066400000000000000000000013201267140355100207640ustar00rootroot00000000000000MRuby.each_target do current_dir = File.dirname(__FILE__) relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) current_build_dir = "#{build_dir}/#{relative_from_root}" self.libmruby << objfile("#{current_build_dir}/mrblib") file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c" file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t| _, _, *rbfiles = t.prerequisites FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| _pp "GEN", "*.rb", "#{t.name.relative_path}" f.puts File.read("#{current_dir}/init_mrblib.c") mrbc.run f, rbfiles, 'mrblib_irep' end end end mruby-1.2.0+20160315+git4f20d58a/mrblib/numeric.rb000066400000000000000000000055761267140355100206610ustar00rootroot00000000000000## # Numeric # # ISO 15.2.7 class Numeric include Comparable ## # Returns the receiver simply. # # ISO 15.2.7.4.1 def +@ self end ## # Returns the receiver's value, negated. # # ISO 15.2.7.4.2 def -@ 0 - self end ## # Returns the absolute value of the receiver. # # ISO 15.2.7.4.3 def abs if self < 0 -self else self end end end ## # Integral # # mruby special - module to share methods between Floats and Integers # to make them compatible module Integral ## # Calls the given block once for each Integer # from +self+ downto +num+. # # ISO 15.2.8.3.15 def downto(num, &block) return to_enum(:downto, num) unless block_given? i = self.to_i while i >= num block.call(i) i -= 1 end self end ## # Returns self + 1 # # ISO 15.2.8.3.19 def next self + 1 end # ISO 15.2.8.3.21 alias succ next ## # Calls the given block +self+ times. # # ISO 15.2.8.3.22 def times &block return to_enum :times unless block_given? i = 0 while i < self block.call i i += 1 end self end ## # Calls the given block once for each Integer # from +self+ upto +num+. # # ISO 15.2.8.3.27 def upto(num, &block) return to_enum(:upto, num) unless block_given? i = self.to_i while i <= num block.call(i) i += 1 end self end ## # Calls the given block from +self+ to +num+ # incremented by +step+ (default 1). # def step(num, step = 1, &block) raise ArgumentError, "step can't be 0" if step == 0 return to_enum(:step, num, step) unless block_given? i = if num.kind_of? Float then self.to_f else self end if step > 0 while i <= num block.call(i) i += step end else while i >= num block.call(i) i += step end end self end end ## # Integer # # ISO 15.2.8 class Integer include Integral ## # Returns the receiver simply. # # ISO 15.2.8.3.14 def ceil self end ## # Returns the receiver simply. # # ISO 15.2.8.3.17 def floor self end ## # Returns the receiver simply. # # ISO 15.2.8.3.24 alias round floor ## # Returns the receiver simply. # # ISO 15.2.8.3.26 alias truncate floor end ## # Float # # ISO 15.2.9 class Float include Integral # mruby special - since mruby integers may be upgraded to floats, # floats should be compatible to integers. def >> other n = self.to_i other = other.to_i if other < 0 n << -other else other.times { n /= 2 } if n.abs < 1 if n >= 0 0 else -1 end else n.to_i end end end def << other n = self.to_i other = other.to_i if other < 0 n >> -other else other.times { n *= 2 } n end end end mruby-1.2.0+20160315+git4f20d58a/mrblib/range.rb000066400000000000000000000017071267140355100203030ustar00rootroot00000000000000## # Range # # ISO 15.2.14 class Range ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.14.4.4 def each(&block) return to_enum :each unless block_given? val = self.first last = self.last if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special lim = last lim += 1 unless exclude_end? i = val while i < lim block.call(i) i += 1 end return self end raise TypeError, "can't iterate" unless val.respond_to? :succ return self if (val <=> last) > 0 while (val <=> last) < 0 block.call(val) val = val.succ end block.call(val) if !exclude_end? && (val <=> last) == 0 self end # redefine #hash 15.3.1.3.15 def hash h = first.hash ^ last.hash h += 1 if self.exclude_end? h end end ## # Range is enumerable # # ISO 15.2.14.3 class Range include Enumerable end mruby-1.2.0+20160315+git4f20d58a/mrblib/string.rb000066400000000000000000000125671267140355100205230ustar00rootroot00000000000000## # String # # ISO 15.2.10 class String include Comparable ## # Calls the given block for each line # and pass the respective line. # # ISO 15.2.10.5.15 def each_line(&block) offset = 0 while pos = self.index("\n", offset) block.call(self[offset, pos + 1 - offset]) offset = pos + 1 end block.call(self[offset, self.size - offset]) if self.size > offset self end # private method for gsub/sub def __sub_replace(pre, m, post) s = "" i = 0 while j = index("\\", i) break if j == length-1 t = case self[j+1] when "\\" "\\" when "`" pre when "&", "0" m when "'" post else self[j, 2] end s += self[i, j-i] + t i = j + 2 end s + self[i, length-i] end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Return the # final value. # # ISO 15.2.10.5.18 def gsub(*args, &block) if args.size == 2 s = "" i = 0 while j = index(args[0], i) seplen = args[0].length k = j + seplen pre = self[0, j] post = self[k, length-k] s += self[i, j-i] + args[1].__sub_replace(pre, args[0], post) i = k end s + self[i, length-i] elsif args.size == 1 && block split(args[0], -1).join(block.call(args[0])) else raise ArgumentError, "wrong number of arguments" end end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Modify # +self+ with the final value. # # ISO 15.2.10.5.19 def gsub!(*args, &block) str = self.gsub(*args, &block) return nil if str == self self.replace(str) end ## # Calls the given block for each match of +pattern+ # If no block is given return an array with all # matches of +pattern+. # # ISO 15.2.10.5.32 def scan(reg, &block) ### *** TODO *** ### unless Object.const_defined?(:Regexp) raise NotImplementedError, "scan not available (yet)" end end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Return the final value. # # ISO 15.2.10.5.36 def sub(*args, &block) if args.size == 2 pre, post = split(args[0], 2) return self unless post # The sub target wasn't found in the string pre + args[1].__sub_replace(pre, args[0], post) + post elsif args.size == 1 && block split(args[0], 2).join(block.call(args[0])) else raise ArgumentError, "wrong number of arguments" end end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Modify +self+ with the final value. # # ISO 15.2.10.5.37 def sub!(*args, &block) str = self.sub(*args, &block) return nil if str == self self.replace(str) end ## # Call the given block for each character of # +self+. def each_char(&block) pos = 0 while pos < self.size block.call(self[pos]) pos += 1 end self end ## # Call the given block for each byte of +self+. def each_byte(&block) bytes = self.bytes pos = 0 while pos < bytes.size block.call(bytes[pos]) pos += 1 end self end ## # Modify +self+ by replacing the content of +self+. # The portion of the string affected is determined using the same criteria as +String#[]+. def []=(*args) anum = args.size if anum == 2 pos, value = args if pos.kind_of? String posnum = self.index(pos) if posnum b = self[0, posnum.to_i] a = self[(posnum + pos.length)..-1] self.replace([b, value, a].join('')) return value else raise IndexError, "string not matched" end else pos += self.length if pos < 0 if pos < 0 || pos > self.length raise IndexError, "index #{args[0]} out of string" end b = self[0, pos.to_i] a = self[pos + 1..-1] self.replace([b, value, a].join('')) return value end elsif anum == 3 pos, len, value = args pos += self.length if pos < 0 if pos < 0 || pos > self.length raise IndexError, "index #{args[0]} out of string" end if len < 0 raise IndexError, "negative length #{len}" end b = self[0, pos.to_i] a = self[pos + len..-1] self.replace([b, value, a].join('')) return value else raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)" end end ## # ISO 15.2.10.5.3 def =~(re) raise TypeError, "type mismatch: String given" if re.respond_to? :to_str re =~ self end ## # ISO 15.2.10.5.27 def match(re, &block) if re.respond_to? :to_str if Object.const_defined?(:Regexp) r = Regexp.new(re) r.match(self, &block) else raise NotImplementedError, "String#match needs Regexp class" end else re.match(self, &block) end end end ## # String is comparable # # ISO 15.2.10.3 module Comparable; end class String include Comparable end mruby-1.2.0+20160315+git4f20d58a/mruby-source.gemspec000066400000000000000000000011471267140355100214120ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'mruby/source' Gem::Specification.new do |spec| spec.name = "mruby-source" spec.version = MRuby::Source::MRUBY_VERSION spec.authors = [ MRuby::Source::MRUBY_AUTHOR ] spec.summary = %q{MRuby source code wrapper.} spec.description = %q{MRuby source code wrapper for use with Ruby libs.} spec.homepage = "http://www.mruby.org/" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.require_paths = ["lib"] end mruby-1.2.0+20160315+git4f20d58a/src/000077500000000000000000000000001267140355100161755ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/src/array.c000066400000000000000000000653151267140355100174710ustar00rootroot00000000000000/* ** array.c - Array class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include "value_array.h" #define ARY_DEFAULT_LEN 4 #define ARY_SHRINK_RATIO 5 /* must be larger than 2 */ #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value)) #define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1) static inline mrb_value ary_elt(mrb_value ary, mrb_int offset) { if (offset < 0 || RARRAY_LEN(ary) <= offset) { return mrb_nil_value(); } return RARRAY_PTR(ary)[offset]; } static struct RArray* ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a; mrb_int blen; if (capa > ARY_MAX_SIZE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } blen = capa * sizeof(mrb_value); if (blen < capa) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); a->ptr = (mrb_value *)mrb_malloc(mrb, blen); a->aux.capa = capa; a->len = 0; return a; } MRB_API mrb_value mrb_ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a = ary_new_capa(mrb, capa); return mrb_obj_value(a); } MRB_API mrb_value mrb_ary_new(mrb_state *mrb) { return mrb_ary_new_capa(mrb, 0); } /* * to copy array, use this instead of memcpy because of portability * * gcc on ARM may fail optimization of memcpy * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html * * gcc on MIPS also fail * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 * * memcpy doesn't exist on freestanding environment * * If you optimize for binary size, use memcpy instead of this at your own risk * of above portability issue. * * see also http://togetter.com/li/462898 * */ static inline void array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) { mrb_int i; for (i = 0; i < size; i++) { dst[i] = src[i]; } } MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_capa(mrb, size); array_copy(a->ptr, vals, size); a->len = size; return mrb_obj_value(a); } MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) { struct RArray *a; a = ary_new_capa(mrb, 2); a->ptr[0] = car; a->ptr[1] = cdr; a->len = 2; return mrb_obj_value(a); } static void ary_fill_with_nil(mrb_value *ptr, mrb_int size) { mrb_value nil = mrb_nil_value(); while (size--) { *ptr++ = nil; } } static void ary_modify(mrb_state *mrb, struct RArray *a) { if (ARY_SHARED_P(a)) { mrb_shared_array *shared = a->aux.shared; if (shared->refcnt == 1 && a->ptr == shared->ptr) { a->ptr = shared->ptr; a->aux.capa = a->len; mrb_free(mrb, shared); } else { mrb_value *ptr, *p; mrb_int len; p = a->ptr; len = a->len * sizeof(mrb_value); ptr = (mrb_value *)mrb_malloc(mrb, len); if (p) { array_copy(ptr, p, a->len); } a->ptr = ptr; a->aux.capa = a->len; mrb_ary_decref(mrb, shared); } ARY_UNSET_SHARED_FLAG(a); } } MRB_API void mrb_ary_modify(mrb_state *mrb, struct RArray* a) { mrb_write_barrier(mrb, (struct RBasic*)a); ary_modify(mrb, a); } static void ary_make_shared(mrb_state *mrb, struct RArray *a) { if (!ARY_SHARED_P(a)) { mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array)); shared->refcnt = 1; if (a->aux.capa > a->len) { a->ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*a->len+1); } else { shared->ptr = a->ptr; } shared->len = a->len; a->aux.shared = shared; ARY_SET_SHARED_FLAG(a); } } static void ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) { mrb_int capa = a->aux.capa; if (len > ARY_MAX_SIZE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } if (capa == 0) { capa = ARY_DEFAULT_LEN; } while (capa < len) { capa *= 2; } if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */ if (capa > a->aux.capa) { mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); a->aux.capa = capa; a->ptr = expanded_ptr; } } static void ary_shrink_capa(mrb_state *mrb, struct RArray *a) { mrb_int capa = a->aux.capa; if (capa < ARY_DEFAULT_LEN * 2) return; if (capa <= a->len * ARY_SHRINK_RATIO) return; do { capa /= 2; if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; break; } } while (capa > a->len * ARY_SHRINK_RATIO); if (capa > a->len && capa < a->aux.capa) { a->aux.capa = capa; a->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); } } MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) { mrb_int old_len; struct RArray *a = mrb_ary_ptr(ary); ary_modify(mrb, a); old_len = RARRAY_LEN(ary); if (old_len != new_len) { a->len = new_len; if (new_len < old_len) { ary_shrink_capa(mrb, a); } else { ary_expand_capa(mrb, a, new_len); ary_fill_with_nil(a->ptr + old_len, new_len - old_len); } } return ary; } static mrb_value mrb_ary_s_create(mrb_state *mrb, mrb_value self) { mrb_value *vals; mrb_int len; mrb_get_args(mrb, "*", &vals, &len); return mrb_ary_new_from_values(mrb, len, vals); } static void ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen) { mrb_int len = a->len + blen; ary_modify(mrb, a); if (a->aux.capa < len) ary_expand_capa(mrb, a, len); array_copy(a->ptr+a->len, ptr, blen); mrb_write_barrier(mrb, (struct RBasic*)a); a->len = len; } MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); ary_concat(mrb, mrb_ary_ptr(self), a2->ptr, a2->len); } static mrb_value mrb_ary_concat_m(mrb_state *mrb, mrb_value self) { mrb_value *ptr; mrb_int blen; mrb_get_args(mrb, "a", &ptr, &blen); ary_concat(mrb, mrb_ary_ptr(self), ptr, blen); return self; } static mrb_value mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; mrb_int blen; mrb_get_args(mrb, "a", &ptr, &blen); if (ARY_MAX_SIZE - blen < a1->len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } a2 = ary_new_capa(mrb, a1->len + blen); array_copy(a2->ptr, a1->ptr, a1->len); array_copy(a2->ptr + a1->len, ptr, blen); a2->len = a1->len + blen; return mrb_obj_value(a2); } static void ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len) { ary_modify(mrb, a); if (a->aux.capa < len) ary_expand_capa(mrb, a, len); array_copy(a->ptr, argv, len); mrb_write_barrier(mrb, (struct RBasic*)a); a->len = len; } MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); ary_replace(mrb, mrb_ary_ptr(self), a2->ptr, a2->len); } static mrb_value mrb_ary_replace_m(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "A", &other); mrb_ary_replace(mrb, self, other); return self; } static mrb_value mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; mrb_int times; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); if (ARY_MAX_SIZE / times < a1->len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } a2 = ary_new_capa(mrb, a1->len * times); ptr = a2->ptr; while (times--) { array_copy(ptr, a1->ptr, a1->len); ptr += a1->len; a2->len += a1->len; } return mrb_obj_value(a2); } static mrb_value mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); if (a->len > 1) { mrb_value *p1, *p2; ary_modify(mrb, a); p1 = a->ptr; p2 = a->ptr + a->len - 1; while (p1 < p2) { mrb_value tmp = *p1; *p1++ = *p2; *p2-- = tmp; } } return self; } static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len); if (a->len > 0) { mrb_value *p1, *p2, *e; p1 = a->ptr; e = p1 + a->len; p2 = b->ptr + a->len - 1; while (p1 < e) { *p2-- = *p1++; } b->len = a->len; } return mrb_obj_value(b); } MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); ary_modify(mrb, a); if (a->len == a->aux.capa) ary_expand_capa(mrb, a, a->len + 1); a->ptr[a->len++] = elem; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem); } static mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int len; mrb_get_args(mrb, "*", &argv, &len); while (len--) { mrb_ary_push(mrb, self, *argv++); } return self; } MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); if (a->len == 0) return mrb_nil_value(); return a->ptr[--a->len]; } #define ARY_SHIFT_SHARED_MIN 10 MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_value val; if (a->len == 0) return mrb_nil_value(); if (ARY_SHARED_P(a)) { L_SHIFT: val = a->ptr[0]; a->ptr++; a->len--; return val; } if (a->len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else { mrb_value *ptr = a->ptr; mrb_int size = a->len; val = *ptr; while (--size) { *ptr = *(ptr+1); ++ptr; } --a->len; } return val; } /* self = [1,2,3] item = 0 self.unshift item p self #=> [0, 1, 2, 3] */ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); if (ARY_SHARED_P(a) && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->ptr - a->aux.shared->ptr >= 1) /* there's room for unshifted item */ { a->ptr--; a->ptr[0] = item; } else { ary_modify(mrb, a); if (a->aux.capa < a->len + 1) ary_expand_capa(mrb, a, a->len + 1); value_move(a->ptr + 1, a->ptr, a->len); a->ptr[0] = item; } a->len++; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); return self; } static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_value *vals; mrb_int len; mrb_get_args(mrb, "*", &vals, &len); if (ARY_SHARED_P(a) && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ { a->ptr -= len; } else { ary_modify(mrb, a); if (len == 0) return self; if (a->aux.capa < a->len + len) ary_expand_capa(mrb, a, a->len + len); value_move(a->ptr + len, a->ptr, a->len); } array_copy(a->ptr, vals, len); a->len += len; while (len--) { mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]); } return self; } MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) { struct RArray *a = mrb_ary_ptr(ary); /* range check */ if (n < 0) n += a->len; if (n < 0 || a->len <= n) return mrb_nil_value(); return a->ptr[n]; } MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { struct RArray *a = mrb_ary_ptr(ary); ary_modify(mrb, a); /* range check */ if (n < 0) { n += a->len; if (n < 0) { mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - a->len)); } } if (a->len <= n) { if (a->aux.capa <= n) ary_expand_capa(mrb, a, n + 1); ary_fill_with_nil(a->ptr + a->len, n + 1 - a->len); a->len = n + 1; } a->ptr[n] = val; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); mrb_int tail, size; const mrb_value *argv; mrb_int i, argc; ary_modify(mrb, a); /* len check */ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len)); /* range check */ if (head < 0) { head += a->len; if (head < 0) { mrb_raise(mrb, E_INDEX_ERROR, "index is out of array"); } } if (a->len < len || a->len < head + len) { len = a->len - head; } tail = head + len; /* size check */ if (mrb_array_p(rpl)) { argc = RARRAY_LEN(rpl); argv = RARRAY_PTR(rpl); } else { argc = 1; argv = &rpl; } size = head + argc; if (tail < a->len) size += a->len - tail; if (size > a->aux.capa) ary_expand_capa(mrb, a, size); if (head > a->len) { ary_fill_with_nil(a->ptr + a->len, head - a->len); } else if (head < a->len) { value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail); } for (i = 0; i < argc; i++) { *(a->ptr + head + i) = *(argv + i); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, argv[i]); } a->len = size; return ary; } void mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared) { shared->refcnt--; if (shared->refcnt == 0) { mrb_free(mrb, shared->ptr); mrb_free(mrb, shared); } } static mrb_value ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) { struct RArray *b; ary_make_shared(mrb, a); b = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); b->ptr = a->ptr + beg; b->len = len; b->aux.shared = a->aux.shared; b->aux.shared->refcnt++; ARY_SET_SHARED_FLAG(b); return mrb_obj_value(b); } static mrb_int aget_index(mrb_state *mrb, mrb_value index) { if (mrb_fixnum_p(index)) { return mrb_fixnum(index); } else if (mrb_float_p(index)) { return (mrb_int)mrb_float(index); } else { mrb_int i, argc; mrb_value *argv; mrb_get_args(mrb, "i*", &i, &argv, &argc); return i; } } /* * call-seq: * ary[index] -> obj or nil * ary[start, length] -> new_ary or nil * ary[range] -> new_ary or nil * ary.slice(index) -> obj or nil * ary.slice(start, length) -> new_ary or nil * ary.slice(range) -> new_ary or nil * * Element Reference --- Returns the element at +index+, or returns a * subarray starting at the +start+ index and continuing for +length+ * elements, or returns a subarray specified by +range+ of indices. * * Negative indices count backward from the end of the array (-1 is the last * element). For +start+ and +range+ cases the starting index is just before * an element. Additionally, an empty array is returned when the starting * index for an element range is at the end of the array. * * Returns +nil+ if the index (or starting index) are out of range. * * a = [ "a", "b", "c", "d", "e" ] * a[1] => "b" * a[1,2] => ["b", "c"] * a[1..-2] => ["b", "c", "d"] * */ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, len; mrb_value index; if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: if (mrb_range_beg_len(mrb, index, &i, &len, a->len)) { return ary_subseq(mrb, a, i, len); } else { return mrb_nil_value(); } case MRB_TT_FIXNUM: return mrb_ary_ref(mrb, self, mrb_fixnum(index)); default: return mrb_ary_ref(mrb, self, aget_index(mrb, index)); } } i = aget_index(mrb, index); if (i < 0) i += a->len; if (i < 0 || a->len < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); if (a->len == i) return mrb_ary_new(mrb); if (len > a->len - i) len = a->len - i; return ary_subseq(mrb, a, i, len); } /* * call-seq: * ary[index] = obj -> obj * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil * ary[range] = obj or other_ary or nil -> obj or other_ary or nil * * Element Assignment --- Sets the element at +index+, or replaces a subarray * from the +start+ index for +length+ elements, or replaces a subarray * specified by the +range+ of indices. * * If indices are greater than the current capacity of the array, the array * grows automatically. Elements are inserted into the array at +start+ if * +length+ is zero. * * Negative indices will count backward from the end of the array. For * +start+ and +range+ cases the starting index is just before an element. * * An IndexError is raised if a negative index points past the beginning of * the array. * * See also Array#push, and Array#unshift. * * a = Array.new * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] * a[0, 2] = "?" #=> ["?", 2, nil, "4"] * a[0..2] = "A" #=> ["A", "4"] * a[-1] = "Z" #=> ["A", "Z"] * a[1..-1] = nil #=> ["A", nil] * a[1..-1] = [] #=> ["A"] * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] * a[3, 0] = "B" #=> [1, 2, "A", "B"] */ static mrb_value mrb_ary_aset(mrb_state *mrb, mrb_value self) { mrb_value v1, v2, v3; mrb_int i, len; if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { switch (mrb_type(v1)) { /* a[n..m] = v */ case MRB_TT_RANGE: if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) { mrb_ary_splice(mrb, self, i, len, v2); } break; /* a[n] = v */ case MRB_TT_FIXNUM: mrb_ary_set(mrb, self, mrb_fixnum(v1), v2); break; default: mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; } return v2; } /* a[n,m] = v */ mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3); return v3; } static mrb_value mrb_ary_delete_at(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int index; mrb_value val; mrb_value *ptr; mrb_int len; mrb_get_args(mrb, "i", &index); if (index < 0) index += a->len; if (index < 0 || a->len <= index) return mrb_nil_value(); ary_modify(mrb, a); val = a->ptr[index]; ptr = a->ptr + index; len = a->len - index; while (--len) { *ptr = *(ptr+1); ++ptr; } --a->len; ary_shrink_capa(mrb, a); return val; } static mrb_value mrb_ary_first(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int size; if (mrb_get_args(mrb, "|i", &size) == 0) { return (a->len > 0)? a->ptr[0]: mrb_nil_value(); } if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } if (size > a->len) size = a->len; if (ARY_SHARED_P(a)) { return ary_subseq(mrb, a, 0, size); } return mrb_ary_new_from_values(mrb, size, a->ptr); } static mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int size; if (mrb_get_args(mrb, "|i", &size) == 0) return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value(); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } if (size > a->len) size = a->len; if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { return ary_subseq(mrb, a, a->len - size, size); } return mrb_ary_new_from_values(mrb, size, a->ptr + a->len - size); } static mrb_value mrb_ary_index_m(mrb_state *mrb, mrb_value self) { mrb_value obj; mrb_int i; mrb_get_args(mrb, "o", &obj); for (i = 0; i < RARRAY_LEN(self); i++) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); } } return mrb_nil_value(); } static mrb_value mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) { mrb_value obj; mrb_int i; mrb_get_args(mrb, "o", &obj); for (i = RARRAY_LEN(self) - 1; i >= 0; i--) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); } } return mrb_nil_value(); } MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { if (mrb_array_p(v)) { return v; } if (mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) { return mrb_funcall(mrb, v, "to_a", 0); } else { return mrb_ary_new_from_values(mrb, 1, &v); } } static mrb_value mrb_ary_size(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_fixnum_value(a->len); } MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); if (ARY_SHARED_P(a)) { mrb_ary_decref(mrb, a->aux.shared); ARY_UNSET_SHARED_FLAG(a); } else { mrb_free(mrb, a->ptr); } a->len = 0; a->aux.capa = 0; a->ptr = 0; return self; } static mrb_value mrb_ary_empty_p(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_bool_value(a->len == 0); } MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value ary) { return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary"); } MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset) { if (offset < 0) { offset += RARRAY_LEN(ary); } return ary_elt(ary, offset); } static mrb_value join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) { mrb_int i; mrb_value result, val, tmp; /* check recursive */ for (i=0; i 0 && !mrb_nil_p(sep)) { mrb_str_cat_str(mrb, result, sep); } val = RARRAY_PTR(ary)[i]; switch (mrb_type(val)) { case MRB_TT_ARRAY: ary_join: val = join_ary(mrb, val, sep, list); /* fall through */ case MRB_TT_STRING: str_join: mrb_str_cat_str(mrb, result, val); break; default: tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto str_join; } tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); if (!mrb_nil_p(tmp)) { val = tmp; goto ary_join; } val = mrb_obj_as_string(mrb, val); goto str_join; } } mrb_ary_pop(mrb, list); return result; } MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { sep = mrb_obj_as_string(mrb, sep); return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); } /* * call-seq: * ary.join(sep="") -> str * * Returns a string created by converting each element of the array to * a string, separated by sep. * * [ "a", "b", "c" ].join #=> "abc" * [ "a", "b", "c" ].join("-") #=> "a-b-c" */ static mrb_value mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } static mrb_value mrb_ary_eq(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); } if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return mrb_false_value(); return ary2; } static mrb_value mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); if (!mrb_array_p(ary2)) { return mrb_nil_value(); } return ary2; } void mrb_init_array(mrb_state *mrb) { struct RClass *a; mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */ mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */ mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */ mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */ mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */ } mruby-1.2.0+20160315+git4f20d58a/src/backtrace.c000066400000000000000000000227361267140355100202720ustar00rootroot00000000000000/* ** backtrace.c - ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include struct backtrace_location_raw { int i; int lineno; const char *filename; mrb_sym method_id; char sep; struct RClass *klass; }; struct backtrace_location { int i; int lineno; const char *filename; const char *method; char sep; const char *class_name; }; typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location_raw*, void*); typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*); #ifndef MRB_DISABLE_STDIO struct print_backtrace_args { FILE *stream; int tracehead; }; static void print_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { struct print_backtrace_args *args; args = (struct print_backtrace_args*)data; if (args->tracehead) { fprintf(args->stream, "trace:\n"); args->tracehead = FALSE; } fprintf(args->stream, "\t[%d] %s:%d", loc->i, loc->filename, loc->lineno); if (loc->method) { if (loc->class_name) { fprintf(args->stream, ":in %s%c%s", loc->class_name, loc->sep, loc->method); } else { fprintf(args->stream, ":in %s", loc->method); } } fprintf(args->stream, "\n"); } #endif static void get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { mrb_value ary, str; int ai; ai = mrb_gc_arena_save(mrb); ary = mrb_obj_value((struct RArray*)data); str = mrb_str_new_cstr(mrb, loc->filename); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_fixnum_to_str(mrb, mrb_fixnum_value(loc->lineno), 10)); if (loc->method) { mrb_str_cat_lit(mrb, str, ":in "); if (loc->class_name) { mrb_str_cat_cstr(mrb, str, loc->class_name); mrb_str_cat(mrb, str, &loc->sep, 1); } mrb_str_cat_cstr(mrb, str, loc->method); } mrb_ary_push(mrb, ary, str); mrb_gc_arena_restore(mrb, ai); } static void each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data) { int i; if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ for (i = ciidx; i >= 0; i--) { struct backtrace_location_raw loc; mrb_callinfo *ci; mrb_irep *irep; mrb_code *pc; ci = &mrb->c->cibase[i]; if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) continue; irep = ci->proc->body.irep; if (mrb->c->cibase[i].err) { pc = mrb->c->cibase[i].err; } else if (i+1 <= ciidx) { pc = mrb->c->cibase[i+1].pc - 1; } else { pc = pc0; } loc.filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq)); loc.lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq)); if (loc.lineno == -1) continue; if (ci->target_class == ci->proc->target_class) { loc.sep = '.'; } else { loc.sep = '#'; } if (!loc.filename) { loc.filename = "(unknown)"; } loc.method_id = ci->mid; loc.klass = ci->proc->target_class; loc.i = i; func(mrb, &loc, data); } } struct output_backtrace_args { output_stream_func func; void *data; }; static void output_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data) { struct backtrace_location loc; struct output_backtrace_args *args = data; loc.i = loc_raw->i; loc.lineno = loc_raw->lineno; loc.filename = loc_raw->filename; loc.method = mrb_sym2name(mrb, loc_raw->method_id); loc.sep = loc_raw->sep; loc.class_name = mrb_class_name(mrb, loc_raw->klass); args->func(mrb, &loc, args->data); } static void output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data) { struct output_backtrace_args args; args.func = func; args.data = data; each_backtrace(mrb, ciidx, pc0, output_backtrace_i, &args); } static void exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) { mrb_value lastpc; mrb_code *code; lastpc = mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc")); if (mrb_nil_p(lastpc)) { code = NULL; } else { code = (mrb_code*)mrb_cptr(lastpc); } output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))), code, func, stream); } /* mrb_print_backtrace/mrb_get_backtrace: function to retrieve backtrace information from the exception. note that if you call method after the exception, call stack will be overwritten. So invoke these functions just after detecting exceptions. */ #ifndef MRB_DISABLE_STDIO static void print_backtrace(mrb_state *mrb, mrb_value backtrace) { int i, n; FILE *stream = stderr; fprintf(stream, "trace:\n"); n = RARRAY_LEN(backtrace); for (i = 0; i < n; i++) { mrb_value entry = RARRAY_PTR(backtrace)[i]; fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry)); } } static void print_backtrace_saved(mrb_state *mrb) { int i; FILE *stream = stderr; fprintf(stream, "trace:\n"); for (i = 0; i < mrb->backtrace.n; i++) { mrb_backtrace_entry *entry; entry = &(mrb->backtrace.entries[i]); fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno); if (entry->method_id != 0) { const char *method_name; method_name = mrb_sym2name(mrb, entry->method_id); if (entry->klass) { fprintf(stream, ":in %s%c%s", mrb_class_name(mrb, entry->klass), entry->sep, method_name); } else { fprintf(stream, ":in %s", method_name); } } fprintf(stream, "\n"); } } MRB_API void mrb_print_backtrace(mrb_state *mrb) { mrb_value backtrace; if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) { return; } backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); if (!mrb_nil_p(backtrace)) { print_backtrace(mrb, backtrace); } else if (mrb->backtrace.n > 0) { print_backtrace_saved(mrb); } else { struct print_backtrace_args args; args.stream = stderr; args.tracehead = TRUE; exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args); } } #else MRB_API void mrb_print_backtrace(mrb_state *mrb) { } #endif MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value self) { mrb_value ary; ary = mrb_ary_new(mrb); exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary)); return ary; } MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb) { mrb_value ary; mrb_callinfo *ci = mrb->c->ci; mrb_code *pc = ci->pc; mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1); if (ciidx < 0) ciidx = 0; ary = mrb_ary_new(mrb); output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary)); return ary; } void mrb_free_backtrace(mrb_state *mrb) { mrb->backtrace.exc = 0; mrb->backtrace.n = 0; mrb->backtrace.n_allocated = 0; mrb_free(mrb, mrb->backtrace.entries); } static void save_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data) { mrb_backtrace_entry *entry; if (mrb->backtrace.n >= mrb->backtrace.n_allocated) { int new_n_allocated; if (mrb->backtrace.n_allocated == 0) { new_n_allocated = 8; } else { new_n_allocated = mrb->backtrace.n_allocated * 2; } mrb->backtrace.entries = mrb_realloc(mrb, mrb->backtrace.entries, sizeof(mrb_backtrace_entry) * new_n_allocated); mrb->backtrace.n_allocated = new_n_allocated; } entry = &mrb->backtrace.entries[mrb->backtrace.n]; entry->filename = loc_raw->filename; entry->lineno = loc_raw->lineno; entry->klass = loc_raw->klass; entry->sep = loc_raw->sep; entry->method_id = loc_raw->method_id; mrb->backtrace.n++; } void mrb_save_backtrace(mrb_state *mrb) { mrb_value lastpc; mrb_code *code; mrb_int ciidx; mrb->backtrace.n = 0; mrb->backtrace.exc = 0; if (!mrb->exc) return; mrb->backtrace.exc = mrb->exc; lastpc = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc")); if (mrb_nil_p(lastpc)) { code = NULL; } else { code = (mrb_code*)mrb_cptr(lastpc); } ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"))); each_backtrace(mrb, ciidx, code, save_backtrace_i, NULL); } mrb_value mrb_restore_backtrace(mrb_state *mrb) { int i; mrb_value backtrace; backtrace = mrb_ary_new(mrb); for (i = 0; i < mrb->backtrace.n; i++) { int ai; mrb_backtrace_entry *entry; mrb_value mrb_entry; ai = mrb_gc_arena_save(mrb); entry = &(mrb->backtrace.entries[i]); mrb_entry = mrb_str_new_cstr(mrb, entry->filename); mrb_str_cat_lit(mrb, mrb_entry, ":"); mrb_str_concat(mrb, mrb_entry, mrb_fixnum_to_str(mrb, mrb_fixnum_value(entry->lineno), 10)); if (entry->method_id != 0) { mrb_str_cat_lit(mrb, mrb_entry, ":in "); if (entry->klass) { mrb_str_cat_cstr(mrb, mrb_entry, mrb_class_name(mrb, entry->klass)); mrb_str_cat(mrb, mrb_entry, &entry->sep, 1); } mrb_str_cat_cstr(mrb, mrb_entry, mrb_sym2name(mrb, entry->method_id)); } mrb_ary_push(mrb, backtrace, mrb_entry); mrb_gc_arena_restore(mrb, ai); } return backtrace; } mruby-1.2.0+20160315+git4f20d58a/src/class.c000066400000000000000000001663171267140355100174640ustar00rootroot00000000000000/* ** class.c - Class class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal) void mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) { khiter_t k; khash_t(mt) *h = c->mt; if (!h) return; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { struct RProc *m = kh_value(h, k); if (m) { mrb_gc_mark(mrb, (struct RBasic*)m); } } } } size_t mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c) { khash_t(mt) *h = c->mt; if (!h) return 0; return kh_size(h); } void mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) { kh_destroy(mt, mrb, c->mt); } static void name_class(mrb_state *mrb, struct RClass *c, mrb_sym name) { mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__"), mrb_symbol_value(name)); } static void setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { name_class(mrb, c, id); mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c)); if (outer != mrb->object_class) { mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), mrb_obj_value(outer)); } } #define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c)) static void prepare_singleton_class(mrb_state *mrb, struct RBasic *o) { struct RClass *sc, *c; if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; if (!c->super) { sc->super = mrb->class_class; } else { sc->super = c->super->c; } } else if (o->tt == MRB_TT_SCLASS) { c = (struct RClass*)o; while (c->super->tt == MRB_TT_ICLASS) c = c->super; make_metaclass(mrb, c->super); sc->super = c->super->c; } else { sc->super = o->c; } o->c = sc; mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc); mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o); mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o)); } static struct RClass * class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_CLASS); return mrb_class_ptr(c); } static struct RClass * module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_MODULE); return mrb_class_ptr(c); } MRB_API struct RClass* mrb_class_outer_module(mrb_state *mrb, struct RClass *c) { mrb_value outer; outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); if (mrb_nil_p(outer)) return NULL; return mrb_class_ptr(outer); } static void check_if_class_or_module(mrb_state *mrb, mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: return; default: mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj)); } } static struct RClass* define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) { struct RClass *m; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { return module_from_sym(mrb, outer, name); } m = mrb_module_new(mrb); setup_class(mrb, outer, m, name); return m; } MRB_API struct RClass* mrb_define_module_id(mrb_state *mrb, mrb_sym name) { return define_module(mrb, name, mrb->object_class); } MRB_API struct RClass* mrb_define_module(mrb_state *mrb, const char *name) { return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class); } MRB_API struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { check_if_class_or_module(mrb, outer); return define_module(mrb, id, mrb_class_ptr(outer)); } MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c = define_module(mrb, id, outer); setup_class(mrb, outer, c, id); return c; } static struct RClass* find_origin(struct RClass *c) { MRB_CLASS_ORIGIN(c); return c; } static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { struct RClass * c; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); MRB_CLASS_ORIGIN(c); if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", mrb_sym2str(mrb, name), mrb_obj_value(c->super), mrb_obj_value(super)); } return c; } c = mrb_class_new(mrb, super); setup_class(mrb, outer, c, name); return c; } MRB_API struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { if (!super) { mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name)); } return define_class(mrb, name, super, mrb->object_class); } MRB_API struct RClass* mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) { return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super); } static void mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) { if (!super) super = mrb->object_class; mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass)); } MRB_API struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *s; struct RClass *c; if (!mrb_nil_p(super)) { if (mrb_type(super) != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", super); } s = mrb_class_ptr(super); } else { s = 0; } check_if_class_or_module(mrb, outer); c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); return c; } MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name) { mrb_value sym = mrb_check_intern_cstr(mrb, name); if (mrb_nil_p(sym)) { return FALSE; } return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym)); } MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name) { return mrb_module_get_under(mrb, mrb->object_class, name); } /*! * Defines a class under the namespace of \a outer. * \param outer a class which contains the new class. * \param id name of the new class * \param super a class from which the new class will derive. * NULL means \c Object class. * \return the created class * \throw TypeError if the constant name \a name is already taken but * the constant is not a \c Class. * \throw NameError if the class is already defined but the class can not * be reopened because its superclass is not \a super. * \post top-level constant named \a name refers the returned class. * * \note if a class named \a name is already defined and its superclass is * \a super, the function just returns the defined class. */ MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) { mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c; #if 0 if (!super) { mrb_warn(mrb, "no super class for '%S::%S', Object assumed", mrb_obj_value(outer), mrb_sym2str(mrb, id)); } #endif c = define_class(mrb, id, super, outer); setup_class(mrb, outer, c, id); return c; } MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { khash_t(mt) *h; khiter_t k; MRB_CLASS_ORIGIN(c); h = c->mt; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); kh_value(h, k) = p; if (p) { mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); } } MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) { struct RProc *p; int ai = mrb_gc_arena_save(mrb); p = mrb_proc_new_cfunc(mrb, func); p->target_class = c; mrb_define_method_raw(mrb, c, mid, p); mrb_gc_arena_restore(mrb, ai); } MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } /* a function to raise NotImplementedError with current method name */ MRB_API void mrb_notimplement(mrb_state *mrb) { const char *str; mrb_int len; mrb_callinfo *ci = mrb->c->ci; if (ci->mid) { str = mrb_sym2name_len(mrb, ci->mid, &len); mrb_raisef(mrb, E_NOTIMP_ERROR, "%S() function is unimplemented on this machine", mrb_str_new_static(mrb, str, (size_t)len)); } } /* a function to be replacement of unimplemented method */ MRB_API mrb_value mrb_notimplement_m(mrb_state *mrb, mrb_value self) { mrb_notimplement(mrb); /* not reached */ return mrb_nil_value(); } static mrb_value check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m) { mrb_value tmp; tmp = mrb_check_convert_type(mrb, val, t, c, m); if (mrb_nil_p(tmp)) { mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c)); } return tmp; } static mrb_value to_str(mrb_state *mrb, mrb_value val) { return check_type(mrb, val, MRB_TT_STRING, "String", "to_str"); } static mrb_value to_ary(mrb_state *mrb, mrb_value val) { return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); } static mrb_value to_hash(mrb_state *mrb, mrb_value val) { return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash"); } static mrb_sym to_sym(mrb_state *mrb, mrb_value ss) { if (mrb_type(ss) == MRB_TT_SYMBOL) { return mrb_symbol(ss); } else if (mrb_string_p(ss)) { return mrb_intern_str(mrb, to_str(mrb, ss)); } else { mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0); mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj); /* not reached */ return 0; } } /* retrieve arguments from mrb_state. mrb_get_args(mrb, format, ...) returns number of arguments parsed. format specifiers: string mruby type C type note ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: class/module [mrb_value] S: String [mrb_value] when ! follows, the value may be nil A: Array [mrb_value] when ! follows, the value may be nil H: Hash [mrb_value] when ! follows, the value may be nil s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil z: String [char*] NUL terminated string; z! gives NULL for nil a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil f: Float [mrb_float] i: Integer [mrb_int] b: Boolean [mrb_bool] n: Symbol [mrb_sym] d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified &: Block [mrb_value] *: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array. |: optional Next argument of '|' and later are optional. ?: optional given [mrb_bool] true if preceding argument (optional) is given. */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...) { char c; int i = 0; va_list ap; int argc = mrb->c->ci->argc; int arg_i = 0; mrb_bool array_argv; mrb_bool opt = FALSE; mrb_bool given = TRUE; va_start(ap, format); if (argc < 0) { struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); argc = a->len; array_argv = TRUE; } else { array_argv = FALSE; } #define ARGV \ (array_argv ? mrb_ary_ptr(mrb->c->stack[1])->ptr : (mrb->c->stack + 1)) while ((c = *format++)) { switch (c) { case '|': case '*': case '&': case '?': break; default: if (argc <= i) { if (opt) { given = FALSE; } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } } break; } switch (c) { case 'o': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { *p = ARGV[arg_i++]; i++; } } break; case 'C': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { mrb_value ss; ss = ARGV[arg_i++]; switch (mrb_type(ss)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: break; default: mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); break; } *p = ss; i++; } } break; case 'S': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_str(mrb, ARGV[arg_i++]); i++; } } break; case 'A': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_ary(mrb, ARGV[arg_i++]); i++; } } break; case 'H': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_hash(mrb, ARGV[arg_i++]); i++; } } break; case 's': { mrb_value ss; char **ps = 0; mrb_int *pl = 0; ps = va_arg(ap, char**); pl = va_arg(ap, mrb_int*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *ps = NULL; *pl = 0; i++; break; } } if (i < argc) { ss = to_str(mrb, ARGV[arg_i++]); *ps = RSTRING_PTR(ss); *pl = RSTRING_LEN(ss); i++; } } break; case 'z': { mrb_value ss; const char **ps; ps = va_arg(ap, const char**); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *ps = NULL; i++; arg_i++; break; } } if (i < argc) { ss = to_str(mrb, ARGV[arg_i++]); *ps = mrb_string_value_cstr(mrb, &ss); i++; } } break; case 'a': { mrb_value aa; struct RArray *a; mrb_value **pb; mrb_int *pl; pb = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *pb = 0; *pl = 0; i++; arg_i++; break; } } if (i < argc) { aa = to_ary(mrb, ARGV[arg_i++]); a = mrb_ary_ptr(aa); *pb = a->ptr; *pl = a->len; i++; } } break; case 'f': { mrb_float *p; p = va_arg(ap, mrb_float*); if (i < argc) { *p = mrb_to_flo(mrb, ARGV[arg_i]); arg_i++; i++; } } break; case 'i': { mrb_int *p; p = va_arg(ap, mrb_int*); if (i < argc) { switch (mrb_type(ARGV[arg_i])) { case MRB_TT_FIXNUM: *p = mrb_fixnum(ARGV[arg_i]); break; case MRB_TT_FLOAT: { mrb_float f = mrb_float(ARGV[arg_i]); if (!FIXABLE(f)) { mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); } *p = (mrb_int)f; } break; case MRB_TT_STRING: mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer"); break; default: *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i])); break; } arg_i++; i++; } } break; case 'b': { mrb_bool *boolp = va_arg(ap, mrb_bool*); if (i < argc) { mrb_value b = ARGV[arg_i++]; *boolp = mrb_test(b); i++; } } break; case 'n': { mrb_sym *symp; symp = va_arg(ap, mrb_sym*); if (i < argc) { mrb_value ss; ss = ARGV[arg_i++]; *symp = to_sym(mrb, ss); i++; } } break; case 'd': { void** datap; struct mrb_data_type const* type; datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *datap = 0; i++; arg_i++; break; } } if (i < argc) { *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type); ++i; } } break; case '&': { mrb_value *p, *bp; p = va_arg(ap, mrb_value*); if (mrb->c->ci->argc < 0) { bp = mrb->c->stack + 2; } else { bp = mrb->c->stack + mrb->c->ci->argc + 1; } *p = *bp; } break; case '|': opt = TRUE; break; case '?': { mrb_bool *p; p = va_arg(ap, mrb_bool*); *p = given; } break; case '*': { mrb_value **var; mrb_int *pl; var = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { *var = ARGV + arg_i; } i = argc; arg_i += *pl; } else { *pl = 0; *var = NULL; } } break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1)); break; } } #undef ARGV if (!c && argc > i) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } va_end(ap); return i; } static struct RClass* boot_defclass(mrb_state *mrb, struct RClass *super) { struct RClass *c; c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); } else { c->super = mrb->object_class; } c->mt = kh_init(mt, mrb); return c; } static void boot_initmod(mrb_state *mrb, struct RClass *mod) { mod->mt = kh_init(mt, mrb); } static struct RClass* include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); if (m->tt == MRB_TT_ICLASS) { m = m->c; } MRB_CLASS_ORIGIN(m); ic->iv = m->iv; ic->mt = m->mt; ic->super = super; if (m->tt == MRB_TT_ICLASS) { ic->c = m->c; } else { ic->c = m; } return ic; } static int include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { struct RClass *p, *ic; void *klass_mt = find_origin(c)->mt; while (m) { int superclass_seen = 0; if (m->flags & MRB_FLAG_IS_PREPENDED) goto skip; if (klass_mt && klass_mt == m->mt) return -1; p = c->super; while(p) { if (p->tt == MRB_TT_ICLASS) { if (p->mt == m->mt) { if (!superclass_seen) { ins_pos = p; // move insert point } goto skip; } } else if (p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; } p = p->super; } ic = include_class_new(mrb, m, ins_pos->super); ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super); ins_pos = ic; skip: m = m->super; } return 0; } MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { int changed = include_module_at(mrb, c, find_origin(c), m, 1); if (changed < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } } MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { struct RClass *origin; int changed = 0; if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); origin->flags |= MRB_FLAG_IS_ORIGIN; origin->super = c->super; c->super = origin; origin->mt = c->mt; c->mt = kh_init(mt, mrb); mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); c->flags |= MRB_FLAG_IS_PREPENDED; } changed = include_module_at(mrb, c, c, m, 0); if (changed < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } } static mrb_value mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) { mrb_value klass; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "C", &klass); mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); return mod; } static mrb_value mrb_mod_prepend(mrb_state *mrb, mrb_value klass) { mrb_value *argv; mrb_int argc, i; mrb_get_args(mrb, "*", &argv, &argc); for (i=0; i true or false * * Returns true if module is included in * mod or one of mod's ancestors. * * module A * end * class B * include A * end * class C < B * end * B.include?(A) #=> true * C.include?(A) #=> true * A.include?(A) #=> false */ static mrb_value mrb_mod_include_p(mrb_state *mrb, mrb_value mod) { mrb_value mod2; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "C", &mod2); mrb_check_type(mrb, mod2, MRB_TT_MODULE); while (c) { if (c->tt == MRB_TT_ICLASS) { if (c->c == mrb_class_ptr(mod2)) return mrb_true_value(); } c = c->super; } return mrb_false_value(); } static mrb_value mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; } return result; } static mrb_value mrb_mod_extend_object(mrb_state *mrb, mrb_value mod) { mrb_value obj; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "o", &obj); mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod)); return mod; } static mrb_value mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); struct RClass *origin = c; MRB_CLASS_ORIGIN(origin); result = mrb_ary_new(mrb); while (c) { if (c != origin && c->tt == MRB_TT_ICLASS) { if (c->c->tt == MRB_TT_MODULE) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } } c = c->super; } return result; } static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; struct RClass *m = mrb_class_ptr(mod); boot_initmod(mrb, m); // bootstrap a newly initialized module mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int); /* 15.2.2.4.33 */ /* * call-seq: * mod.instance_methods(include_super=true) -> array * * Returns an array containing the names of the public and protected instance * methods in the receiver. For a module, these are the public and protected methods; * for a class, they are the instance (not singleton) methods. With no * argument, or with an argument that is false, the * instance methods in mod are returned, otherwise the methods * in mod and mod's superclasses are returned. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * def method3() end * end * * A.instance_methods #=> [:method1] * B.instance_methods(false) #=> [:method2] * C.instance_methods(false) #=> [:method3] * C.instance_methods(true).length #=> 43 */ static mrb_value mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_class_instance_method_list(mrb, recur, c, 0); } /* implementation of module_eval/class_eval */ mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); static mrb_value mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) { return mod; } MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value v) { struct RBasic *obj; switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_nil_p(v)) return mrb_obj_value(mrb->nil_class); return mrb_obj_value(mrb->false_class); case MRB_TT_TRUE: return mrb_obj_value(mrb->true_class); case MRB_TT_CPTR: return mrb_obj_value(mrb->object_class); case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: case MRB_TT_FLOAT: mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); return mrb_nil_value(); /* not reached */ default: break; } obj = mrb_basic_ptr(v); prepare_singleton_class(mrb, obj); if (mrb->c && mrb->c->ci && mrb->c->ci->target_class) { mrb_obj_iv_set(mrb, (struct RObject*)obj->c, mrb_intern_lit(mrb, "__outer__"), mrb_obj_value(mrb->c->ci->target_class)); } return mrb_obj_value(obj->c); } MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec) { prepare_singleton_class(mrb, (struct RBasic*)o); mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec); } MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec); } MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_class_method(mrb, c, name, func, aspec); mrb_define_method(mrb, c, name, func, aspec); } MRB_API struct RProc* mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { khiter_t k; struct RProc *m; struct RClass *c = *cp; while (c) { khash_t(mt) *h = c->mt; if (h) { k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { m = kh_value(h, k); if (!m) break; *cp = c; return m; } } c = c->super; } return NULL; /* no method */ } MRB_API struct RProc* mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) { struct RProc *m; m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) { inspect = mrb_any_to_s(mrb, mrb_obj_value(c)); } mrb_name_error(mrb, mid, "undefined method '%S' for class %S", mrb_sym2str(mrb, mid), inspect); } return m; } static mrb_value attr_reader(mrb_state *mrb, mrb_value obj) { mrb_value name = mrb_proc_cfunc_env_get(mrb, 0); return mrb_iv_get(mrb, obj, to_sym(mrb, name)); } static mrb_value mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_value *argv; mrb_int argc, i; int ai; mrb_get_args(mrb, "*", &argv, &argc); ai = mrb_gc_arena_save(mrb); for (i=0; i obj * * Creates a new object of class's class, then * invokes that object's initialize method, * passing it args. This is the method that ends * up getting called whenever an object is constructed using * `.new`. * */ MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { mrb_value obj, blk; mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*&", &argv, &argc, &blk); obj = mrb_instance_alloc(mrb, cv); mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk); return obj; } MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv) { mrb_value obj; obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv); return obj; } static mrb_value mrb_class_initialize(mrb_state *mrb, mrb_value c) { mrb_value a, b; mrb_get_args(mrb, "|C&", &a, &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &c, c, mrb_class_ptr(c)); } return c; } static mrb_value mrb_class_new_class(mrb_state *mrb, mrb_value cv) { mrb_int n; mrb_value super, blk; mrb_value new_class; n = mrb_get_args(mrb, "|C&", &super, &blk); if (n == 0) { super = mrb_obj_value(mrb->object_class); } new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); mrb_funcall_with_block(mrb, new_class, mrb_intern_lit(mrb, "initialize"), n, &super, blk); mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); return new_class; } static mrb_value mrb_class_superclass(mrb_state *mrb, mrb_value klass) { struct RClass *c; c = mrb_class_ptr(klass); c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); } static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv) { return mrb_nil_value(); } static mrb_value mrb_bob_not(mrb_state *mrb, mrb_value cv) { return mrb_bool_value(!mrb_test(cv)); } void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) { mrb_sym inspect; mrb_value repr; inspect = mrb_intern_lit(mrb, "inspect"); if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) { /* method missing in inspect; avoid recursion */ repr = mrb_any_to_s(mrb, self); } else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) { repr = mrb_funcall_argv(mrb, self, inspect, 0, 0); if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) { repr = mrb_any_to_s(mrb, self); } } else { repr = mrb_any_to_s(mrb, self); } mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); } /* 15.3.1.3.30 */ /* * call-seq: * obj.method_missing(symbol [, *args] ) -> result * * Invoked by Ruby when obj is sent a message it cannot handle. * symbol is the symbol for the method called, and args * are any arguments that were passed to it. By default, the interpreter * raises an error when this method is called. However, it is possible * to override the method to provide more dynamic behavior. * If it is decided that a particular method should not be handled, then * super should be called, so that ancestors can pick up the * missing method. * The example below creates * a class Roman, which responds to methods with names * consisting of roman numerals, returning the corresponding integer * values. * * class Roman * def romanToInt(str) * # ... * end * def method_missing(methId) * str = methId.id2name * romanToInt(str) * end * end * * r = Roman.new * r.iv #=> 4 * r.xxiii #=> 23 * r.mm #=> 2000 */ static mrb_value mrb_bob_missing(mrb_state *mrb, mrb_value mod) { mrb_sym name; mrb_value *a; mrb_int alen; mrb_get_args(mrb, "n*", &name, &a, &alen); mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); } MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { khiter_t k; while (c) { khash_t(mt) *h = c->mt; if (h) { k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { if (kh_value(h, k)) { return TRUE; /* method exists */ } else { return FALSE; /* undefined method */ } } } c = c->super; } return FALSE; /* no method */ } MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid) { return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid); } MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; const char *name; mrb_sym classpath = mrb_intern_lit(mrb, "__classpath__"); path = mrb_obj_iv_get(mrb, (struct RObject*)c, classpath); if (mrb_nil_p(path)) { struct RClass *outer = mrb_class_outer_module(mrb, c); mrb_sym sym = mrb_class_sym(mrb, c, outer); mrb_int len; if (sym == 0) { return mrb_nil_value(); } else if (outer && outer != mrb->object_class) { mrb_value base = mrb_class_path(mrb, outer); path = mrb_str_buf_new(mrb, 0); if (mrb_nil_p(base)) { mrb_str_cat_lit(mrb, path, "#"); } else { mrb_str_concat(mrb, path, base); } mrb_str_cat_lit(mrb, path, "::"); name = mrb_sym2name_len(mrb, sym, &len); mrb_str_cat(mrb, path, name, len); } else { name = mrb_sym2name_len(mrb, sym, &len); path = mrb_str_new(mrb, name, len); } mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path); } return path; } MRB_API struct RClass * mrb_class_real(struct RClass* cl) { if (cl == 0) return NULL; while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; } return cl; } MRB_API const char* mrb_class_name(mrb_state *mrb, struct RClass* c) { mrb_value path = mrb_class_path(mrb, c); if (mrb_nil_p(path)) { path = mrb_str_new_lit(mrb, "#"); } return RSTRING_PTR(path); } MRB_API const char* mrb_obj_classname(mrb_state *mrb, mrb_value obj) { return mrb_class_name(mrb, mrb_obj_class(mrb, obj)); } /*! * Ensures a class can be derived from super. * * \param super a reference to an object. * \exception TypeError if \a super is not a Class or \a super is a singleton class. */ static void mrb_check_inheritable(mrb_state *mrb, struct RClass *super) { if (super->tt != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super)); } if (super->tt == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class"); } if (super == mrb->class_class) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class"); } } /*! * Creates a new class. * \param super a class from which the new class derives. * \exception TypeError \a super is not inheritable. * \exception TypeError \a super is the Class class. */ MRB_API struct RClass* mrb_class_new(mrb_state *mrb, struct RClass *super) { struct RClass *c; if (super) { mrb_check_inheritable(mrb, super); } c = boot_defclass(mrb, super); if (super) { MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super)); } make_metaclass(mrb, c); return c; } /*! * Creates a new module. */ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); boot_initmod(mrb, m); return m; } /* * call-seq: * obj.class => class * * Returns the class of obj, now preferred over * Object#type, as an object's type in Ruby is only * loosely tied to that object's class. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Fixnum * self.class #=> Object */ MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj) { return mrb_class_real(mrb_class(mrb, obj)); } MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b) { struct RProc *m = mrb_method_search(mrb, c, b); mrb_define_method_raw(mrb, c, a, m); } /*! * Defines an alias of a method. * \param klass the class which the original method belongs to * \param name1 a new name for the method * \param name2 the original name of the method */ MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2) { mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2)); } /* * call-seq: * mod.to_s -> string * * Return a string representing this module or class. For basic * classes and modules, this is the name. For singletons, we * show information on the thing we're attached to as well. */ static mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass) { mrb_value str; if (mrb_type(klass) == MRB_TT_SCLASS) { mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__")); str = mrb_str_new_lit(mrb, "#"); } else { struct RClass *c; mrb_value path; str = mrb_str_buf_new(mrb, 32); c = mrb_class_ptr(klass); path = mrb_class_path(mrb, c); if (mrb_nil_p(path)) { switch (mrb_type(klass)) { case MRB_TT_CLASS: mrb_str_cat_lit(mrb, str, "#"); } else { return path; } } } static mrb_value mrb_mod_alias(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_sym new_name, old_name; mrb_get_args(mrb, "nn", &new_name, &old_name); mrb_alias_method(mrb, c, new_name, old_name); return mrb_nil_value(); } static void undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) { if (!mrb_obj_respond_to(mrb, c, a)) { mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); } else { mrb_define_method_raw(mrb, c, a, NULL); } } MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { undef_method(mrb, c, mrb_intern_cstr(mrb, name)); } MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name) { mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } static mrb_value mrb_mod_undef(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { undef_method(mrb, c, mrb_symbol(*argv)); argv++; } return mrb_nil_value(); } static mrb_value mod_define_method(mrb_state *mrb, mrb_value self) { struct RClass *c = mrb_class_ptr(self); struct RProc *p; mrb_sym mid; mrb_value blk; mrb_get_args(mrb, "n&", &mid, &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; mrb_define_method_raw(mrb, c, mid, p); return mrb_symbol_value(mid); } static void check_cv_name_str(mrb_state *mrb, mrb_value str) { const char *s = RSTRING_PTR(str); mrb_int len = RSTRING_LEN(str); if (len < 3 || !(s[0] == '@' && s[1] == '@')) { mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str); } } static void check_cv_name_sym(mrb_state *mrb, mrb_sym id) { check_cv_name_str(mrb, mrb_sym2str(mrb, id)); } /* 15.2.2.4.16 */ /* * call-seq: * obj.class_variable_defined?(symbol) -> true or false * * Returns true if the given class variable is defined * in obj. * * class Fred * @@foo = 99 * end * Fred.class_variable_defined?(:@@foo) #=> true * Fred.class_variable_defined?(:@@bar) #=> false */ static mrb_value mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_bool_value(mrb_cv_defined(mrb, mod, id)); } /* 15.2.2.4.17 */ /* * call-seq: * mod.class_variable_get(symbol) -> obj * * Returns the value of the given class variable (or throws a * NameError exception). The @@ part of the * variable name should be included for regular class variables * * class Fred * @@foo = 99 * end * Fred.class_variable_get(:@@foo) #=> 99 */ static mrb_value mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_cv_get(mrb, mod, id); } /* 15.2.2.4.18 */ /* * call-seq: * obj.class_variable_set(symbol, obj) -> obj * * Sets the class variable names by symbol to * object. * * class Fred * @@foo = 99 * def foo * @@foo * end * end * Fred.class_variable_set(:@@foo, 101) #=> 101 * Fred.new.foo #=> 101 */ static mrb_value mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod) { mrb_value value; mrb_sym id; mrb_get_args(mrb, "no", &id, &value); check_cv_name_sym(mrb, id); mrb_cv_set(mrb, mod, id, value); return value; } /* 15.2.2.4.39 */ /* * call-seq: * remove_class_variable(sym) -> obj * * Removes the definition of the sym, returning that * constant's value. * * class Dummy * @@var = 99 * puts @@var * p class_variables * remove_class_variable(:@@var) * p class_variables * end * * produces: * * 99 * [:@@var] * [] */ static mrb_value mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) { mrb_value val; mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (!mrb_undef_p(val)) return val; if (mrb_cv_defined(mrb, mod, id)) { mrb_name_error(mrb, id, "cannot remove %S for %S", mrb_sym2str(mrb, id), mod); } mrb_name_error(mrb, id, "class variable %S not defined for %S", mrb_sym2str(mrb, id), mod); /* not reached */ return mrb_nil_value(); } /* 15.2.2.4.34 */ /* * call-seq: * mod.method_defined?(symbol) -> true or false * * Returns +true+ if the named method is defined by * _mod_ (or its included modules and, if _mod_ is a class, * its ancestors). Public and protected methods are matched. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * include A * def method3() end * end * * A.method_defined? :method1 #=> true * C.method_defined? "method1" #=> true * C.method_defined? "method2" #=> true * C.method_defined? "method3" #=> true * C.method_defined? "method4" #=> false */ static mrb_value mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id)); } static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); khash_t(mt) *h = find_origin(c)->mt; khiter_t k; if (h) { k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { kh_del(mt, mrb, h, k); mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); return; } } mrb_name_error(mrb, mid, "method '%S' not defined in %S", mrb_sym2str(mrb, mid), mod); } /* 15.2.2.4.41 */ /* * call-seq: * remove_method(symbol) -> self * * Removes the method identified by _symbol_ from the current * class. For an example, see Module.undef_method. */ static mrb_value mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { remove_method(mrb, mod, mrb_symbol(*argv)); argv++; } return mod; } static void check_const_name_str(mrb_state *mrb, mrb_value str) { if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) { mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str); } } static void check_const_name_sym(mrb_state *mrb, mrb_sym id) { check_const_name_str(mrb, mrb_sym2str(mrb, id)); } static mrb_value const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit) { if (inherit) { return mrb_bool_value(mrb_const_defined(mrb, mod, id)); } return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); } static mrb_value mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_bool inherit = TRUE; mrb_get_args(mrb, "n|b", &id, &inherit); check_const_name_sym(mrb, id); return const_defined(mrb, mod, id, inherit); } static mrb_value mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_const_name_sym(mrb, id); return mrb_const_get(mrb, mod, id); } static mrb_value mrb_mod_const_set(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_value value; mrb_get_args(mrb, "no", &id, &value); check_const_name_sym(mrb, id); mrb_const_set(mrb, mod, id, value); return value; } static mrb_value mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_value val; mrb_get_args(mrb, "n", &id); check_const_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (mrb_undef_p(val)) { mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id)); } return val; } static mrb_value mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { mrb_name_error(mrb, sym, "uninitialized constant %S::%S", mod, mrb_sym2str(mrb, sym)); } else { mrb_name_error(mrb, sym, "uninitialized constant %S", mrb_sym2str(mrb, sym)); } /* not reached */ return mrb_nil_value(); } static mrb_value mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) { mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); return mrb_nil_value(); /* not reached */ } static mrb_value mrb_mod_eqq(mrb_state *mrb, mrb_value mod) { mrb_value obj; mrb_bool eqq; mrb_get_args(mrb, "o", &obj); eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); return mrb_bool_value(eqq); } MRB_API mrb_value mrb_mod_module_function(mrb_state *mrb, mrb_value mod) { mrb_value *argv; mrb_int argc, i; mrb_sym mid; struct RProc *method_rproc; struct RClass *rclass; int ai; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "*", &argv, &argc); if(argc == 0) { /* set MODFUNC SCOPE if implemented */ return mod; } /* set PRIVATE method visibility if implemented */ /* mrb_mod_dummy_visibility(mrb, mod); */ for (i=0; ic, mid, method_rproc); mrb_gc_arena_restore(mrb, ai); } return mod; } void mrb_init_class(mrb_state *mrb) { struct RClass *bob; /* BasicObject */ struct RClass *obj; /* Object */ struct RClass *mod; /* Module */ struct RClass *cls; /* Class */ /* boot class hierarchy */ bob = boot_defclass(mrb, 0); obj = boot_defclass(mrb, bob); mrb->object_class = obj; mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */ cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */ /* fix-up loose ends */ bob->c = obj->c = mod->c = cls->c = cls; make_metaclass(mrb, bob); make_metaclass(mrb, obj); make_metaclass(mrb, mod); make_metaclass(mrb, cls); /* name basic classes */ mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob)); mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob)); mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj)); mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod)); mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls)); /* name each classes */ name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject")); name_class(mrb, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS); mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "method_missing", mrb_bob_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */ mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */ mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */ mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1)); MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE); mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */ mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */ mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */ mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ mrb_define_method(mrb, mod, "prepend", mrb_mod_prepend, MRB_ARGS_ANY()); mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */ mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */ mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */ mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */ mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */ mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */ mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */ mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */ mrb_define_method(mrb, mod, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */ mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY()); mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */ mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */ mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, mod, "inspect", mrb_mod_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, mod, "alias_method", mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */ mrb_define_method(mrb, mod, "ancestors", mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */ mrb_define_method(mrb, mod, "undef_method", mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */ mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */ mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */ mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */ mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */ mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */ mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */ mrb_undef_method(mrb, cls, "append_features"); mrb_undef_method(mrb, cls, "extend_object"); } mruby-1.2.0+20160315+git4f20d58a/src/codedump.c000066400000000000000000000315271267140355100201510ustar00rootroot00000000000000#include #include #include #include #include #include #ifndef MRB_DISABLE_STDIO static int print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre) { size_t i; if (n == 0) return 0; for (i=0; i+1nlocals; i++) { if (irep->lv[i].r == n) { mrb_sym sym = irep->lv[i].name; if (pre) printf(" "); printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym)); return 1; } } return 0; } #define RA 1 #define RB 2 #define RAB 3 static void print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r) { int pre = 0; if (!irep->lv || ((!(r & RA) || GETARG_A(c) >= irep->nlocals) && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) { printf("\n"); return; } printf("\t; "); if (r & RA) { pre = print_r(mrb, irep, GETARG_A(c), 0); } if (r & RB) { print_r(mrb, irep, GETARG_B(c), pre); } printf("\n"); } #endif static void codedump(mrb_state *mrb, mrb_irep *irep) { #ifndef MRB_DISABLE_STDIO int i; int ai; mrb_code c; const char *file = NULL, *next_file; int32_t line; if (!irep) return; printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep, irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen); for (i = 0; i < (int)irep->ilen; i++) { ai = mrb_gc_arena_save(mrb); next_file = mrb_debug_get_filename(irep, i); if (next_file && file != next_file) { printf("file: %s\n", next_file); file = next_file; } line = mrb_debug_get_line(irep, i); if (line < 0) { printf(" "); } else { printf("%5d ", line); } printf("%03d ", i); c = irep->iseq[i]; switch (GET_OPCODE(c)) { case OP_NOP: printf("OP_NOP\n"); break; case OP_MOVE: printf("OP_MOVE\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); print_lv(mrb, irep, c, RAB); break; case OP_LOADL: { mrb_value v = irep->pool[GETARG_Bx(c)]; mrb_value s = mrb_inspect(mrb, v); printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); } print_lv(mrb, irep, c, RA); break; case OP_LOADI: printf("OP_LOADI\tR%d\t%d\t", GETARG_A(c), GETARG_sBx(c)); print_lv(mrb, irep, c, RA); break; case OP_LOADSYM: printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RA); break; case OP_LOADNIL: printf("OP_LOADNIL\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_LOADSELF: printf("OP_LOADSELF\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_LOADT: printf("OP_LOADT\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_LOADF: printf("OP_LOADF\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_GETGLOBAL: printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RA); break; case OP_SETGLOBAL: printf("OP_SETGLOBAL\t:%s\tR%d\t", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_GETCONST: printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RA); break; case OP_SETCONST: printf("OP_SETCONST\t:%s\tR%d\t", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_GETMCNST: printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RAB); break; case OP_SETMCNST: printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1, mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_GETIV: printf("OP_GETIV\tR%d\t%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RA); break; case OP_SETIV: printf("OP_SETIV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_GETUPVAR: printf("OP_GETUPVAR\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RA); break; case OP_SETUPVAR: printf("OP_SETUPVAR\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RA); break; case OP_GETCV: printf("OP_GETCV\tR%d\t%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); print_lv(mrb, irep, c, RA); break; case OP_SETCV: printf("OP_SETCV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_JMP: printf("OP_JMP\t%03d\n", i+GETARG_sBx(c)); break; case OP_JMPIF: printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); break; case OP_JMPNOT: printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); break; case OP_SEND: printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_SENDB: printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_TAILCALL: printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_SUPER: printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c), GETARG_C(c)); break; case OP_ARGARY: printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c), (GETARG_Bx(c)>>10)&0x3f, (GETARG_Bx(c)>>9)&0x1, (GETARG_Bx(c)>>4)&0x1f, (GETARG_Bx(c)>>0)&0xf); print_lv(mrb, irep, c, RA); break; case OP_ENTER: printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", (GETARG_Ax(c)>>18)&0x1f, (GETARG_Ax(c)>>13)&0x1f, (GETARG_Ax(c)>>12)&0x1, (GETARG_Ax(c)>>7)&0x1f, (GETARG_Ax(c)>>2)&0x1f, (GETARG_Ax(c)>>1)&0x1, GETARG_Ax(c) & 0x1); break; case OP_RETURN: printf("OP_RETURN\tR%d", GETARG_A(c)); switch (GETARG_B(c)) { case OP_R_NORMAL: case OP_R_RETURN: printf("\treturn\t"); break; case OP_R_BREAK: printf("\tbreak\t"); break; default: printf("\tbroken\t"); break; } print_lv(mrb, irep, c, RA); break; case OP_BLKPUSH: printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c), (GETARG_Bx(c)>>10)&0x3f, (GETARG_Bx(c)>>9)&0x1, (GETARG_Bx(c)>>4)&0x1f, (GETARG_Bx(c)>>0)&0xf); print_lv(mrb, irep, c, RA); break; case OP_LAMBDA: printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c)); print_lv(mrb, irep, c, RA); break; case OP_RANGE: printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RAB); break; case OP_METHOD: printf("OP_METHOD\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); print_lv(mrb, irep, c, RA); break; case OP_ADD: printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_ADDI: printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_SUB: printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_SUBI: printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_MUL: printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_DIV: printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_LT: printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_LE: printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_GT: printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_GE: printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_EQ: printf("OP_EQ\t\tR%d\t:%s\t%d\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c)); break; case OP_STOP: printf("OP_STOP\n"); break; case OP_ARRAY: printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RAB); break; case OP_ARYCAT: printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); print_lv(mrb, irep, c, RAB); break; case OP_ARYPUSH: printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); print_lv(mrb, irep, c, RAB); break; case OP_AREF: printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RAB); break; case OP_APOST: printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RA); break; case OP_STRING: { mrb_value v = irep->pool[GETARG_Bx(c)]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); } print_lv(mrb, irep, c, RA); break; case OP_STRCAT: printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); print_lv(mrb, irep, c, RAB); break; case OP_HASH: printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); print_lv(mrb, irep, c, RAB); break; case OP_OCLASS: printf("OP_OCLASS\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_CLASS: printf("OP_CLASS\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); print_lv(mrb, irep, c, RA); break; case OP_MODULE: printf("OP_MODULE\tR%d\t:%s", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); print_lv(mrb, irep, c, RA); break; case OP_EXEC: printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1); print_lv(mrb, irep, c, RA); break; case OP_SCLASS: printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); print_lv(mrb, irep, c, RAB); break; case OP_TCLASS: printf("OP_TCLASS\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_ERR: { mrb_value v = irep->pool[GETARG_Bx(c)]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); printf("OP_ERR\t%s\n", RSTRING_PTR(s)); } break; case OP_EPUSH: printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1); break; case OP_ONERR: printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c)); break; case OP_RESCUE: printf("OP_RESCUE\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_RAISE: printf("OP_RAISE\tR%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_POPERR: printf("OP_POPERR\t%d\t\t", GETARG_A(c)); print_lv(mrb, irep, c, RA); break; case OP_EPOP: printf("OP_EPOP\t%d\n", GETARG_A(c)); break; default: printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c), GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; } mrb_gc_arena_restore(mrb, ai); } printf("\n"); #endif } static void codedump_recur(mrb_state *mrb, mrb_irep *irep) { size_t i; codedump(mrb, irep); for (i=0; irlen; i++) { codedump_recur(mrb, irep->reps[i]); } } void mrb_codedump_all(mrb_state *mrb, struct RProc *proc) { codedump_recur(mrb, proc->body.irep); } mruby-1.2.0+20160315+git4f20d58a/src/compar.c000066400000000000000000000003041267140355100176170ustar00rootroot00000000000000/* ** compar.c - Comparable module ** ** See Copyright Notice in mruby.h */ #include void mrb_init_comparable(mrb_state *mrb) { mrb_define_module(mrb, "Comparable"); /* 15.3.3 */ } mruby-1.2.0+20160315+git4f20d58a/src/crc.c000066400000000000000000000014721267140355100171140ustar00rootroot00000000000000/* ** crc.c - calculate CRC ** ** See Copyright Notice in mruby.h */ #include #include #include /* Calculate CRC (CRC-16-CCITT) ** ** 0000_0000_0000_0000_0000_0000_0000_0000 ** ^|------- CRC -------|- work --| ** carry */ #define CRC_16_CCITT 0x11021ul /* x^16+x^12+x^5+1 */ #define CRC_XOR_PATTERN (CRC_16_CCITT << 8) #define CRC_CARRY_BIT (0x01000000) uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc) { size_t ibyte; uint32_t ibit; uint32_t crcwk = crc << 8; for (ibyte = 0; ibyte < nbytes; ibyte++) { crcwk |= *src++; for (ibit = 0; ibit < CHAR_BIT; ibit++) { crcwk <<= 1; if (crcwk & CRC_CARRY_BIT) { crcwk ^= CRC_XOR_PATTERN; } } } return (uint16_t)(crcwk >> 8); } mruby-1.2.0+20160315+git4f20d58a/src/debug.c000066400000000000000000000137331267140355100174360ustar00rootroot00000000000000#include #include #include #include static mrb_irep_debug_info_file * get_file(mrb_irep_debug_info *info, uint32_t pc) { mrb_irep_debug_info_file **ret; int32_t count; if (pc >= info->pc_count) { return NULL; } /* get upper bound */ ret = info->files; count = info->flen; while (count > 0) { int32_t step = count / 2; mrb_irep_debug_info_file **it = ret + step; if (!(pc < (*it)->start_pos)) { ret = it + 1; count -= step + 1; } else { count = step; } } --ret; /* check returning file exists inside debug info */ mrb_assert(info->files <= ret && ret < (info->files + info->flen)); /* check pc is within the range of returning file */ mrb_assert((*ret)->start_pos <= pc && pc < (((ret + 1 - info->files) < info->flen) ? (*(ret+1))->start_pos : info->pc_count)); return *ret; } static mrb_debug_line_type select_line_type(const uint16_t *lines, size_t lines_len) { size_t line_count = 0; int prev_line = -1; size_t i; for (i = 0; i < lines_len; ++i) { if (lines[i] != prev_line) { ++line_count; } } return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count) ? mrb_debug_line_ary : mrb_debug_line_flat_map; } MRB_API char const* mrb_debug_get_filename(mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; if (!irep->debug_info) { return irep->filename; } else if ((f = get_file(irep->debug_info, pc))) { return f->filename; } } return NULL; } MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; if (!irep->debug_info) { return irep->lines? irep->lines[pc] : -1; } else if ((f = get_file(irep->debug_info, pc))) { switch (f->line_type) { case mrb_debug_line_ary: mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count)); return f->lines.ary[pc - f->start_pos]; case mrb_debug_line_flat_map: { /* get upper bound */ mrb_irep_debug_info_line *ret = f->lines.flat_map; uint32_t count = f->line_entry_count; while (count > 0) { int32_t step = count / 2; mrb_irep_debug_info_line *it = ret + step; if (!(pc < it->start_pos)) { ret = it + 1; count -= step + 1; } else { count = step; } } --ret; /* check line entry pointer range */ mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count)); /* check pc range */ mrb_assert(ret->start_pos <= pc && pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count) ? (ret+1)->start_pos : irep->debug_info->pc_count)); return ret->line; } } } } return -1; } MRB_API mrb_irep_debug_info * mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) { static const mrb_irep_debug_info initial = { 0, 0, NULL }; mrb_irep_debug_info *ret; mrb_assert(!irep->debug_info); ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret)); *ret = initial; irep->debug_info = ret; return ret; } MRB_API mrb_irep_debug_info_file * mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep, uint32_t start_pos, uint32_t end_pos) { mrb_irep_debug_info *info; mrb_irep_debug_info_file *ret; uint32_t file_pc_count; size_t fn_len; mrb_int len; uint32_t i; if (!irep->debug_info) { return NULL; } mrb_assert(irep->filename); mrb_assert(irep->lines); info = irep->debug_info; if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) { return NULL; } ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret)); info->files = (mrb_irep_debug_info_file**)( info->files ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1)) : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*))); info->files[info->flen++] = ret; file_pc_count = end_pos - start_pos; ret->start_pos = start_pos; info->pc_count = end_pos; fn_len = strlen(irep->filename); ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len); len = 0; ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len); ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos); ret->lines.ptr = NULL; switch (ret->line_type) { case mrb_debug_line_ary: ret->line_entry_count = file_pc_count; ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count); for (i = 0; i < file_pc_count; ++i) { ret->lines.ary[i] = irep->lines[start_pos + i]; } break; case mrb_debug_line_flat_map: { uint16_t prev_line = 0; mrb_irep_debug_info_line m; ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1); ret->line_entry_count = 0; for (i = 0; i < file_pc_count; ++i) { if (irep->lines[start_pos + i] == prev_line) { continue; } ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc( mrb, ret->lines.flat_map, sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1)); m.start_pos = start_pos + i; m.line = irep->lines[start_pos + i]; ret->lines.flat_map[ret->line_entry_count] = m; /* update */ ++ret->line_entry_count; prev_line = irep->lines[start_pos + i]; } } break; default: mrb_assert(0); break; } return ret; } MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) { uint32_t i; if (!d) { return; } for (i = 0; i < d->flen; ++i) { mrb_assert(d->files[i]); mrb_free(mrb, d->files[i]->lines.ptr); mrb_free(mrb, d->files[i]); } mrb_free(mrb, d->files); mrb_free(mrb, d); } mruby-1.2.0+20160315+git4f20d58a/src/dump.c000066400000000000000000000672751267140355100173270ustar00rootroot00000000000000/* ** dump.c - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #define FLAG_BYTEORDER_NATIVE 2 #define FLAG_BYTEORDER_NONATIVE 0 #ifdef MRB_USE_FLOAT #define MRB_FLOAT_FMT "%.8e" #else #define MRB_FLOAT_FMT "%.16e" #endif static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep); #if UINT32_MAX > SIZE_MAX # error This code cannot be built on your environment. #endif static size_t write_padding(uint8_t *buf) { const size_t align = MRB_DUMP_ALIGNMENT; size_t pad_len = -(intptr_t)buf & (align-1); if (pad_len > 0) { memset(buf, 0, pad_len); } return pad_len; } static size_t get_irep_header_size(mrb_state *mrb) { size_t size = 0; size += sizeof(uint32_t) * 1; size += sizeof(uint16_t) * 3; return size; } static ptrdiff_t write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */ cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ return cur - buf; } static size_t get_iseq_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size += sizeof(uint32_t); /* ilen */ size += sizeof(uint32_t); /* max padding */ size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */ return size; } static ptrdiff_t write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags) { uint8_t *cur = buf; uint32_t iseq_no; cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ cur += write_padding(cur); switch (flags & DUMP_ENDIAN_NAT) { case DUMP_ENDIAN_BIG: if (bigendian_p()) goto native; for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */ } break; case DUMP_ENDIAN_LIL: if (!bigendian_p()) goto native; for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */ } break; native: case DUMP_ENDIAN_NAT: memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code)); cur += irep->ilen * sizeof(mrb_code); break; } return cur - buf; } static size_t get_pool_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size_t pool_no; mrb_value str; size += sizeof(uint32_t); /* plen */ size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (mrb_type(irep->pool[pool_no])) { case MRB_TT_FIXNUM: str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); { mrb_int len = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; case MRB_TT_FLOAT: str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); { mrb_int len = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; case MRB_TT_STRING: { mrb_int len = RSTRING_LEN(irep->pool[pool_no]); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; default: break; } mrb_gc_arena_restore(mrb, ai); } return size; } static ptrdiff_t write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { size_t pool_no; uint8_t *cur = buf; uint16_t len; mrb_value str; const char *char_ptr; cur += uint32_to_bin(irep->plen, cur); /* number of pool */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (mrb_type(irep->pool[pool_no])) { case MRB_TT_FIXNUM: cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */ str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); break; case MRB_TT_FLOAT: cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); break; case MRB_TT_STRING: cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ str = irep->pool[pool_no]; break; default: continue; } char_ptr = RSTRING_PTR(str); { mrb_int tlen = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); len = (uint16_t)tlen; } cur += uint16_to_bin(len, cur); /* data length */ memcpy(cur, char_ptr, (size_t)len); cur += len; mrb_gc_arena_restore(mrb, ai); } return cur - buf; } static size_t get_syms_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; uint32_t sym_no; mrb_int len; size += sizeof(uint32_t); /* slen */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { size += sizeof(uint16_t); /* snl(n) */ if (irep->syms[sym_no] != 0) { mrb_sym2name_len(mrb, irep->syms[sym_no], &len); size += len + 1; /* sn(n) + null char */ } } return size; } static ptrdiff_t write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint32_t sym_no; uint8_t *cur = buf; const char *name; cur += uint32_to_bin(irep->slen, cur); /* number of symbol */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { if (irep->syms[sym_no] != 0) { mrb_int len; name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len); mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ memcpy(cur, name, len); /* symbol name */ cur += (uint16_t)len; *cur++ = '\0'; } else { cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */ } } return cur - buf; } static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size += get_irep_header_size(mrb); size += get_iseq_block_size(mrb, irep); size += get_pool_block_size(mrb, irep); size += get_syms_block_size(mrb, irep); return size; } static size_t get_irep_record_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size_t irep_no; size = get_irep_record_size_1(mrb, irep); for (irep_no = 0; irep_no < irep->rlen; irep_no++) { size += get_irep_record_size(mrb, irep->reps[irep_no]); } return size; } static int write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) { uint32_t i; uint8_t *src = bin; if (irep == NULL) { return MRB_DUMP_INVALID_IREP; } *irep_record_size = get_irep_record_size_1(mrb, irep); if (*irep_record_size == 0) { return MRB_DUMP_GENERAL_FAILURE; } bin += write_irep_header(mrb, irep, bin); bin += write_iseq_block(mrb, irep, bin, flags); bin += write_pool_block(mrb, irep, bin); bin += write_syms_block(mrb, irep, bin); for (i = 0; i < irep->rlen; i++) { int result; size_t rsize; result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } bin += rsize; } *irep_record_size = bin - src; return MRB_DUMP_OK; } static uint32_t write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); memcpy(bin, &footer, sizeof(struct rite_binary_footer)); return sizeof(struct rite_binary_footer); } static int write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version)); return MRB_DUMP_OK; } static int write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) { int result; size_t rsize = 0; uint8_t *cur = bin; if (mrb == NULL || bin == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } cur += sizeof(struct rite_section_irep_header); result = write_irep_record(mrb, irep, cur, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } *len_p = cur - bin + rsize; write_section_irep_header(mrb, *len_p, bin); return MRB_DUMP_OK; } static int write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin; memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident)); uint32_to_bin((uint32_t)section_size, header->section_size); return MRB_DUMP_OK; } static size_t get_lineno_record_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size += sizeof(uint32_t); /* record size */ size += sizeof(uint16_t); /* filename size */ if (irep->filename) { size += strlen(irep->filename); /* filename */ } size += sizeof(uint32_t); /* niseq */ if (irep->lines) { size += sizeof(uint16_t) * irep->ilen; /* lineno */ } return size; } static size_t write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) { uint8_t *cur = bin; size_t iseq_no; size_t filename_len; ptrdiff_t diff; cur += sizeof(uint32_t); /* record size */ if (irep->filename) { filename_len = strlen(irep->filename); } else { filename_len = 0; } mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */ if (filename_len) { memcpy(cur, irep->filename, filename_len); cur += filename_len; /* filename */ } if (irep->lines) { mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX); cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */ for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */ } } else { cur += uint32_to_bin(0, cur); /* niseq */ } diff = cur - bin; mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)diff, bin); /* record size */ mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); return (size_t)diff; } static size_t write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) { size_t i; size_t rlen, size = 0; rlen = write_lineno_record_1(mrb, irep, bin); bin += rlen; size += rlen; for (i=0; irlen; i++) { rlen = write_lineno_record(mrb, irep, bin); bin += rlen; size += rlen; } return size; } static int write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) { size_t section_size = 0; size_t rlen = 0; /* size of irep record */ uint8_t *cur = bin; if (mrb == NULL || bin == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } cur += sizeof(struct rite_section_lineno_header); section_size += sizeof(struct rite_section_lineno_header); rlen = write_lineno_record(mrb, irep, cur); section_size += rlen; write_section_lineno_header(mrb, section_size, bin); return MRB_DUMP_OK; } static size_t get_debug_record_size(mrb_state *mrb, mrb_irep *irep) { size_t ret = 0; uint16_t f_idx; size_t i; ret += sizeof(uint32_t); /* record size */ ret += sizeof(uint16_t); /* file count */ for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx]; ret += sizeof(uint32_t); /* position */ ret += sizeof(uint16_t); /* filename index */ /* lines */ ret += sizeof(uint32_t); /* entry count */ ret += sizeof(uint8_t); /* line type */ switch (file->line_type) { case mrb_debug_line_ary: ret += sizeof(uint16_t) * (size_t)(file->line_entry_count); break; case mrb_debug_line_flat_map: ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); break; default: mrb_assert(0); break; } } for (i=0; irlen; i++) { ret += get_debug_record_size(mrb, irep->reps[i]); } return ret; } static int find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s) { int i; for (i = 0; i < ary_len; ++i) { if (ary[i] == s) { return i; } } return -1; } static size_t get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp) { mrb_sym *filenames = *fp; size_t i, size = 0; mrb_irep_debug_info *di = irep->debug_info; mrb_assert(lp); for (i = 0; i < di->flen; ++i) { mrb_irep_debug_info_file *file; mrb_int filename_len; file = di->files[i]; if (find_filename_index(filenames, *lp, file->filename_sym) == -1) { /* register filename */ *lp += 1; *fp = filenames = (mrb_sym *)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp)); filenames[*lp - 1] = file->filename_sym; /* filename */ mrb_sym2name_len(mrb, file->filename_sym, &filename_len); size += sizeof(uint16_t) + (size_t)filename_len; } } for (i=0; irlen; i++) { size += get_filename_table_size(mrb, irep->reps[i], fp, lp); } return size; } static size_t write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { uint8_t *cur; uint16_t f_idx; ptrdiff_t ret; cur = bin + sizeof(uint32_t); /* skip record size */ cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */ for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { int filename_idx; const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx]; /* position */ cur += uint32_to_bin(file->start_pos, cur); /* filename index */ filename_idx = find_filename_index(filenames, filenames_len, file->filename_sym); mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)filename_idx, cur); /* lines */ cur += uint32_to_bin(file->line_entry_count, cur); cur += uint8_to_bin(file->line_type, cur); switch (file->line_type) { case mrb_debug_line_ary: { uint32_t l; for (l = 0; l < file->line_entry_count; ++l) { cur += uint16_to_bin(file->lines.ary[l], cur); } } break; case mrb_debug_line_flat_map: { uint32_t line; for (line = 0; line < file->line_entry_count; ++line) { cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur); cur += uint16_to_bin(file->lines.flat_map[line].line, cur); } } break; default: mrb_assert(0); break; } } ret = cur - bin; mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); uint32_to_bin(ret, bin); mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX); return (size_t)ret; } static size_t write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { size_t size, len; size_t irep_no; size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len); bin += len; for (irep_no = 0; irep_no < irep->rlen; irep_no++) { len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len); bin += len; size += len; } mrb_assert(size == get_debug_record_size(mrb, irep)); return size; } static int write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) { size_t section_size = 0; const uint8_t *bin = cur; struct rite_section_debug_header *header; size_t dlen; uint16_t i; char const *sym; mrb_int sym_len; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } header = (struct rite_section_debug_header *)bin; cur += sizeof(struct rite_section_debug_header); section_size += sizeof(struct rite_section_debug_header); /* filename table */ cur += uint16_to_bin(filenames_len, cur); section_size += sizeof(uint16_t); for (i = 0; i < filenames_len; ++i) { sym = mrb_sym2name_len(mrb, filenames[i], &sym_len); mrb_assert(sym); cur += uint16_to_bin(sym_len, cur); memcpy(cur, sym, sym_len); cur += sym_len; section_size += sizeof(uint16_t) + sym_len; } /* debug records */ dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); mrb_assert(section_size <= INT32_MAX); uint32_to_bin(section_size, header->section_size); return MRB_DUMP_OK; } static void create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len) { size_t i; if (*syms == NULL) { *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1); } for (i = 0; i + 1 < irep->nlocals; ++i) { mrb_sym const name = irep->lv[i].name; if (name == 0) continue; if (find_filename_index(*syms, *syms_len, name) != -1) continue; ++(*syms_len); *syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len)); (*syms)[*syms_len - 1] = name; } for (i = 0; i < irep->rlen; ++i) { create_lv_sym_table(mrb, irep->reps[i], syms, syms_len); } } static int write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; uint32_t i; const char *str; mrb_int str_len; cur += uint32_to_bin(syms_len, cur); for (i = 0; i < syms_len; ++i) { str = mrb_sym2name_len(mrb, syms[i], &str_len); cur += uint16_to_bin(str_len, cur); memcpy(cur, str, str_len); cur += str_len; } *start = cur; return MRB_DUMP_OK; } static int write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; size_t i; for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name == 0) { cur += uint16_to_bin(RITE_LV_NULL_MARK, cur); cur += uint16_to_bin(0, cur); } else { int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name); mrb_assert(sym_idx != -1); /* local variable name must be in syms */ cur += uint16_to_bin(sym_idx, cur); cur += uint16_to_bin(irep->lv[i].r, cur); } } for (i = 0; i < irep->rlen; ++i) { write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len); } *start = cur; return MRB_DUMP_OK; } static size_t get_lv_record_size(mrb_state *mrb, mrb_irep *irep) { size_t ret = 0, i; ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1); for (i = 0; i < irep->rlen; ++i) { ret += get_lv_record_size(mrb, irep->reps[i]); } return ret; } static size_t get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) { size_t ret = 0, i; ret += sizeof(uint32_t); /* syms_len */ ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */ for (i = 0; i < syms_len; ++i) { mrb_int str_len; mrb_sym2name_len(mrb, syms[i], &str_len); ret += str_len; } ret += get_lv_record_size(mrb, irep); return ret; } static int write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len) { uint8_t *cur = start; struct rite_section_lv_header *header; ptrdiff_t diff; int result = MRB_DUMP_OK; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } header = (struct rite_section_lv_header*)cur; cur += sizeof(struct rite_section_lv_header); result = write_lv_sym_table(mrb, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { goto lv_section_exit; } result = write_lv_record(mrb, irep, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { goto lv_section_exit; } memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); diff = cur - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); uint32_to_bin(diff, header->section_size); lv_section_exit: return result; } static int write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags) { struct rite_binary_header *header = (struct rite_binary_header *)bin; uint16_t crc; uint32_t offset; switch (flags & DUMP_ENDIAN_NAT) { endian_big: case DUMP_ENDIAN_BIG: memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); break; endian_little: case DUMP_ENDIAN_LIL: memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)); break; case DUMP_ENDIAN_NAT: if (bigendian_p()) goto endian_big; goto endian_little; break; } memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)); memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); mrb_assert(binary_size <= UINT32_MAX); uint32_to_bin((uint32_t)binary_size, header->binary_size); offset = (&(header->binary_crc[0]) - bin) + sizeof(uint16_t); crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0); uint16_to_bin(crc, header->binary_crc); return MRB_DUMP_OK; } static mrb_bool is_debug_info_defined(mrb_irep *irep) { size_t i; if (!irep->debug_info) return FALSE; for (i=0; irlen; i++) { if (!is_debug_info_defined(irep->reps[i])) return FALSE; } return TRUE; } static mrb_bool is_lv_defined(mrb_irep *irep) { size_t i; if (irep->lv) { return TRUE; } for (i = 0; i < irep->rlen; ++i) { if (is_lv_defined(irep->reps[i])) { return TRUE; } } return FALSE; } static uint8_t dump_flags(uint8_t flags, uint8_t native) { if (native == FLAG_BYTEORDER_NATIVE) { if ((flags & DUMP_ENDIAN_NAT) == 0) { return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT; } return flags; } if ((flags & DUMP_ENDIAN_NAT) == 0) { return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG; } return flags; } static int dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { int result = MRB_DUMP_GENERAL_FAILURE; size_t malloc_size; size_t section_irep_size; size_t section_lineno_size = 0, section_lv_size = 0; uint8_t *cur = NULL; mrb_bool const debug_info_defined = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep); mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; mrb_sym *filenames = NULL; uint16_t filenames_len = 0; if (mrb == NULL) { *bin = NULL; return MRB_DUMP_GENERAL_FAILURE; } section_irep_size = sizeof(struct rite_section_irep_header); section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) + 1); /* filename table size */ section_lineno_size += sizeof(uint16_t); section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len); section_lineno_size += get_debug_record_size(mrb, irep); } else { section_lineno_size += sizeof(struct rite_section_lineno_header); section_lineno_size += get_lineno_record_size(mrb, irep); } } if (lv_defined) { section_lv_size += sizeof(struct rite_section_lv_header); create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len); section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len); } malloc_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size); cur += sizeof(struct rite_binary_header); result = write_section_irep(mrb, irep, cur, §ion_irep_size, flags); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_irep_size; *bin_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); /* write DEBUG section */ if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); } else { result = write_section_lineno(mrb, irep, cur); } if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lineno_size; } if (lv_defined) { result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lv_size; } write_footer(mrb, cur); write_rite_binary_header(mrb, *bin_size, *bin, flags); error_exit: if (result != MRB_DUMP_OK) { mrb_free(mrb, *bin); *bin = NULL; } mrb_free(mrb, lv_syms); mrb_free(mrb, filenames); return result; } int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size); } #ifndef MRB_DISABLE_STDIO int mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) { uint8_t *bin = NULL; size_t bin_size = 0; int result; if (fp == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size); if (result == MRB_DUMP_OK) { if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { result = MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } static mrb_bool dump_bigendian_p(uint8_t flags) { switch (flags & DUMP_ENDIAN_NAT) { case DUMP_ENDIAN_BIG: return TRUE; case DUMP_ENDIAN_LIL: return FALSE; default: case DUMP_ENDIAN_NAT: return bigendian_p(); } } int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) { uint8_t *bin = NULL; size_t bin_size = 0, bin_idx = 0; int result; if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE); result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { if (!dump_bigendian_p(flags)) { if (fprintf(fp, "/* dumped in little endian order.\n" " use `mrbc -E` option for big endian CPU. */\n") < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } else { if (fprintf(fp, "/* dumped in big endian order.\n" " use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fprintf(fp, "#include \n") < 0) { /* for uint8_t under at least Darwin */ mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } if (fprintf(fp, "const uint8_t\n" "#if defined __GNUC__\n" "__attribute__((aligned(%u)))\n" "#elif defined _MSC_VER\n" "__declspec(align(%u))\n" "#endif\n" "%s[] = {", (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } while (bin_idx < bin_size) { if (bin_idx % 16 == 0) { if (fputs("\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fputs("\n};\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } #endif /* MRB_DISABLE_STDIO */ mruby-1.2.0+20160315+git4f20d58a/src/enum.c000066400000000000000000000003031267140355100173010ustar00rootroot00000000000000/* ** enum.c - Enumerable module ** ** See Copyright Notice in mruby.h */ #include void mrb_init_enumerable(mrb_state *mrb) { mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */ } mruby-1.2.0+20160315+git4f20d58a/src/error.c000066400000000000000000000322351267140355100174770ustar00rootroot00000000000000/* ** error.c - Exception class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len) { mrb_value arg = mrb_str_new(mrb, ptr, len); return mrb_obj_new(mrb, c, 1, &arg); } MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) { str = mrb_str_to_str(mrb, str); return mrb_obj_new(mrb, c, 1, &str); } /* * call-seq: * Exception.new(msg = nil) -> exception * * Construct a new Exception object, optionally passing in * a message. */ static mrb_value exc_initialize(mrb_state *mrb, mrb_value exc) { mrb_value mesg; if (mrb_get_args(mrb, "|o", &mesg) == 1) { mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); } return exc; } /* * Document-method: exception * * call-seq: * exc.exception(string) -> an_exception or exc * * With no argument, or if the argument is the same as the receiver, * return the receiver. Otherwise, create a new * exception object of the same class as the receiver, but with a * message equal to string.to_str. * */ static mrb_value exc_exception(mrb_state *mrb, mrb_value self) { mrb_value exc; mrb_value a; int argc; argc = mrb_get_args(mrb, "|o", &a); if (argc == 0) return self; if (mrb_obj_equal(mrb, self, a)) return self; exc = mrb_obj_clone(mrb, self); mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a); return exc; } /* * call-seq: * exception.to_s -> string * * Returns exception's message (or the name of the exception if * no message is set). */ static mrb_value exc_to_s(mrb_state *mrb, mrb_value exc) { mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); struct RObject *p; if (!mrb_string_p(mesg)) { return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); } p = mrb_obj_ptr(mesg); if (!p->c) { p->c = mrb->string_class; } return mesg; } /* * call-seq: * exception.message -> string * * Returns the result of invoking exception.to_s. * Normally this returns the exception's message or name. By * supplying a to_str method, exceptions are agreeing to * be used where Strings are expected. */ static mrb_value exc_message(mrb_state *mrb, mrb_value exc) { return mrb_funcall(mrb, exc, "to_s", 0); } /* * call-seq: * exception.inspect -> string * * Returns this exception's file name, line number, * message and class name. * If file name or line number is not set, * returns message and class name. */ static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, mesg, file, line; mrb_bool append_mesg; mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file")); line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line")); append_mesg = !mrb_nil_p(mesg); if (append_mesg) { mesg = mrb_obj_as_string(mrb, mesg); append_mesg = RSTRING_LEN(mesg) > 0; } if (!mrb_nil_p(file) && !mrb_nil_p(line)) { str = mrb_str_dup(mrb, file); mrb_str_cat_lit(mrb, str, ":"); mrb_str_append(mrb, str, line); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { mrb_str_cat_str(mrb, str, mesg); mrb_str_cat_lit(mrb, str, " ("); } mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); if (append_mesg) { mrb_str_cat_lit(mrb, str, ")"); } } else { const char *cname = mrb_obj_classname(mrb, exc); str = mrb_str_new_cstr(mrb, cname); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { mrb_str_cat_str(mrb, str, mesg); } else { mrb_str_cat_cstr(mrb, str, cname); } } return str; } void mrb_save_backtrace(mrb_state *mrb); mrb_value mrb_restore_backtrace(mrb_state *mrb); static mrb_value exc_get_backtrace(mrb_state *mrb, mrb_value exc) { mrb_sym attr_name; mrb_value backtrace; attr_name = mrb_intern_lit(mrb, "backtrace"); backtrace = mrb_iv_get(mrb, exc, attr_name); if (mrb_nil_p(backtrace)) { if (mrb_obj_ptr(exc) == mrb->backtrace.exc && mrb->backtrace.n > 0) { backtrace = mrb_restore_backtrace(mrb); mrb->backtrace.n = 0; mrb->backtrace.exc = 0; } else { backtrace = mrb_exc_backtrace(mrb, exc); } mrb_iv_set(mrb, exc, attr_name, backtrace); } return backtrace; } static mrb_value exc_set_backtrace(mrb_state *mrb, mrb_value exc) { mrb_value backtrace; mrb_get_args(mrb, "o", &backtrace); mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); return backtrace; } static void exc_debug_info(mrb_state *mrb, struct RObject *exc) { mrb_callinfo *ci = mrb->c->ci; mrb_code *pc = ci->pc; mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value((mrb_int)(ci - mrb->c->cibase))); while (ci >= mrb->c->cibase) { mrb_code *err = ci->err; if (!err && pc) err = pc - 1; if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { mrb_irep *irep = ci->proc->body.irep; int32_t const line = mrb_debug_get_line(irep, (uint32_t)(err - irep->iseq)); char const* file = mrb_debug_get_filename(irep, (uint32_t)(err - irep->iseq)); if (line != -1 && file) { mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file)); mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line)); return; } } pc = ci->pc; ci--; } } static void set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt) { mrb_funcall(mrb, info, "set_backtrace", 1, bt); } static mrb_bool have_backtrace(mrb_state *mrb, struct RObject *exc) { return !mrb_nil_p(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "backtrace"))); } void mrb_exc_set(mrb_state *mrb, mrb_value exc) { if (!mrb->gc.out_of_memory && mrb->backtrace.n > 0) { mrb_value target_exc = mrb_nil_value(); int ai; ai = mrb_gc_arena_save(mrb); if ((mrb->exc && !have_backtrace(mrb, mrb->exc))) { target_exc = mrb_obj_value(mrb->exc); } else if (!mrb_nil_p(exc) && mrb->backtrace.exc) { target_exc = mrb_obj_value(mrb->backtrace.exc); mrb_gc_protect(mrb, target_exc); } if (!mrb_nil_p(target_exc)) { mrb_value backtrace; backtrace = mrb_restore_backtrace(mrb); set_backtrace(mrb, target_exc, backtrace); } mrb_gc_arena_restore(mrb, ai); } mrb->backtrace.n = 0; if (mrb_nil_p(exc)) { mrb->exc = 0; } else { mrb->exc = mrb_obj_ptr(exc); } } MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { mrb_exc_set(mrb, exc); if (!mrb->gc.out_of_memory) { exc_debug_info(mrb, mrb->exc); mrb_save_backtrace(mrb); } if (!mrb->jmp) { mrb_p(mrb, exc); abort(); } MRB_THROW(mrb->jmp); } MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { mrb_value mesg; mesg = mrb_str_new_cstr(mrb, msg); mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); } MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap) { const char *p = format; const char *b = p; ptrdiff_t size; mrb_value ary = mrb_ary_new_capa(mrb, 4); while (*p) { const char c = *p++; if (c == '%') { if (*p == 'S') { size = p - b - 1; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); mrb_ary_push(mrb, ary, va_arg(ap, mrb_value)); b = p + 1; } } else if (c == '\\') { if (*p) { size = p - b - 1; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1)); b = ++p; } else { break; } } } if (b == format) { return mrb_str_new_cstr(mrb, format); } else { size = p - b; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0)); } } MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...) { va_list ap; mrb_value str; va_start(ap, format); str = mrb_vformat(mrb, format, ap); va_end(ap); return str; } MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) { va_list args; mrb_value mesg; va_start(args, fmt); mesg = mrb_vformat(mrb, fmt, args); va_end(args); mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); } MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) { mrb_value exc; mrb_value argv[2]; va_list args; va_start(args, fmt); argv[0] = mrb_vformat(mrb, fmt, args); va_end(args); argv[1] = mrb_symbol_value(id); exc = mrb_obj_new(mrb, E_NAME_ERROR, 2, argv); mrb_exc_raise(mrb, exc); } MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { #ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; va_start(ap, fmt); str = mrb_vformat(mrb, fmt, ap); fputs("warning: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); va_end(ap); #endif } MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...) { #ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; va_start(ap, fmt); str = mrb_vformat(mrb, fmt, ap); fputs("bug: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); va_end(ap); #endif exit(EXIT_FAILURE); } static mrb_value make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr) { mrb_value mesg; int n; mesg = mrb_nil_value(); switch (argc) { case 0: break; case 1: if (mrb_nil_p(argv[0])) break; if (isstr) { mesg = mrb_check_string_type(mrb, argv[0]); if (!mrb_nil_p(mesg)) { mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mesg); break; } } n = 0; goto exception_call; case 2: case 3: n = 1; exception_call: { mrb_sym exc = mrb_intern_lit(mrb, "exception"); if (mrb_respond_to(mrb, argv[0], exc)) { mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1); } else { /* undef */ mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected"); } } break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc)); break; } if (argc > 0) { if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class)) mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); if (argc > 2) set_backtrace(mrb, mesg, argv[2]); } return mesg; } MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv) { return make_exception(mrb, argc, argv, TRUE); } MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg) { struct RClass *sce; mrb_int no; no = (mrb_int)errno; if (mrb_class_defined(mrb, "SystemCallError")) { sce = mrb_class_get(mrb, "SystemCallError"); if (mesg != NULL) { mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg)); } else { mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 1, mrb_fixnum_value(no)); } } else { mrb_raise(mrb, E_RUNTIME_ERROR, mesg); } } MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; va_list ap; va_start(ap, fmt); exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args); va_end(ap); mrb_exc_raise(mrb, exc); } void mrb_init_exception(mrb_state *mrb) { struct RClass *exception, *runtime_error, *script_error; mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "backtrace", exc_get_backtrace, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1)); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "Out of memory")); script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ mrb_define_class(mrb, "SystemStackError", exception); } mruby-1.2.0+20160315+git4f20d58a/src/error.h000066400000000000000000000001621267140355100174760ustar00rootroot00000000000000/* this header file is to be removed soon. added for compatibility purpose (1.0.0) */ #include mruby-1.2.0+20160315+git4f20d58a/src/etc.c000066400000000000000000000116661267140355100171260ustar00rootroot00000000000000/* ** etc.c - ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include MRB_API struct RData* mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type) { struct RData *data; data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass); data->data = ptr; data->type = type; return data; } MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_type(obj) != MRB_TT_DATA) { mrb_check_type(mrb, obj, MRB_TT_DATA); } if (DATA_TYPE(obj) != type) { const mrb_data_type *t2 = DATA_TYPE(obj); if (t2) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name)); } else { struct RClass *c = mrb_class(mrb, obj); mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)", mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name)); } } } MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_type(obj) != MRB_TT_DATA) { return NULL; } if (DATA_TYPE(obj) != type) { return NULL; } return DATA_PTR(obj); } MRB_API void* mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { mrb_data_check_type(mrb, obj, type); return DATA_PTR(obj); } MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { mrb_sym id; switch (mrb_type(name)) { default: name = mrb_check_string_type(mrb, name); if (mrb_nil_p(name)) { name = mrb_inspect(mrb, name); mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name); } /* fall through */ case MRB_TT_STRING: name = mrb_str_intern(mrb, name); /* fall through */ case MRB_TT_SYMBOL: id = mrb_symbol(name); } return id; } MRB_API mrb_int mrb_float_id(mrb_float f) { const char *p = (const char*)&f; int len = sizeof(f); mrb_int id = 0; while (len--) { id = id*65599 + *p; p++; } id = id + (id>>5); return id; } MRB_API mrb_int mrb_obj_id(mrb_value obj) { mrb_int tt = mrb_type(obj); #define MakeID2(p,t) (mrb_int)(((intptr_t)(p))^(t)) #define MakeID(p) MakeID2(p,tt) switch (tt) { case MRB_TT_FREE: case MRB_TT_UNDEF: return MakeID(0); /* not define */ case MRB_TT_FALSE: if (mrb_nil_p(obj)) return MakeID(1); return MakeID(0); case MRB_TT_TRUE: return MakeID(1); case MRB_TT_SYMBOL: return MakeID(mrb_symbol(obj)); case MRB_TT_FIXNUM: return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT); case MRB_TT_FLOAT: return MakeID(mrb_float_id(mrb_float(obj))); case MRB_TT_STRING: case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_ICLASS: case MRB_TT_SCLASS: case MRB_TT_PROC: case MRB_TT_ARRAY: case MRB_TT_HASH: case MRB_TT_RANGE: case MRB_TT_EXCEPTION: case MRB_TT_FILE: case MRB_TT_DATA: default: return MakeID(mrb_ptr(obj)); } } #ifdef MRB_WORD_BOXING MRB_API mrb_value mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) { mrb_value v; v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); v.value.fp->f = f; return v; } MRB_API mrb_value mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f) { struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat)); nf->tt = MRB_TT_FLOAT; nf->c = mrb->float_class; nf->f = f; return mrb_obj_value(nf); } MRB_API mrb_value mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) { mrb_value v; v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class); v.value.vp->p = p; return v; } #endif /* MRB_WORD_BOXING */ MRB_API mrb_bool mrb_regexp_p(mrb_state *mrb, mrb_value v) { if (mrb->flags & MRB_STATE_NO_REGEXP) { return FALSE; } if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) { mrb->flags |= MRB_STATE_REGEXP; return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); } else { mrb->flags |= MRB_STATE_REGEXP; mrb->flags |= MRB_STATE_NO_REGEXP; } return FALSE; } #if defined _MSC_VER && _MSC_VER < 1900 #ifndef va_copy static void mrb_msvc_va_copy(va_list *dest, va_list src) { *dest = src; } #define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src) #endif MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg) { int cnt; va_list argcp; va_copy(argcp, arg); if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) { cnt = _vscprintf(format, arg); } va_end(argcp); return cnt; } MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...) { va_list arg; int ret; va_start(arg, format); ret = mrb_msvc_vsnprintf(s, n, format, arg); va_end(arg); return ret; } #endif /* defined _MSC_VER && _MSC_VER < 1900 */ mruby-1.2.0+20160315+git4f20d58a/src/ext/000077500000000000000000000000001267140355100167755ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/src/ext/.gitkeep000066400000000000000000000000001267140355100204140ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/src/fmt_fp.c000066400000000000000000000215201267140355100176140ustar00rootroot00000000000000/* Most code in this file originates from musl (src/stdio/vfprintf.c) which, just like mruby itself, is licensed under the MIT license. Copyright (c) 2005-2014 Rich Felker, et al. 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. */ #include #include #include #include #include #include #include #include struct fmt_args { mrb_state *mrb; mrb_value str; }; #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MIN(a,b) ((a)<(b) ? (a) : (b)) /* Convenient bit representation for modifier flags, which all fall * within 31 codepoints of the space character. */ #define ALT_FORM (1U<<('#'-' ')) #define ZERO_PAD (1U<<('0'-' ')) #define LEFT_ADJ (1U<<('-'-' ')) #define PAD_POS (1U<<(' '-' ')) #define MARK_POS (1U<<('+'-' ')) static void out(struct fmt_args *f, const char *s, size_t l) { mrb_str_cat(f->mrb, f->str, s, l); } #define PAD_SIZE 256 static void pad(struct fmt_args *f, char c, int w, int l, int fl) { char pad[PAD_SIZE]; if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; l = w - l; memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l); for (; l >= PAD_SIZE; l -= PAD_SIZE) out(f, pad, PAD_SIZE); out(f, pad, l); } static const char xdigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static char* fmt_u(uint32_t x, char *s) { for (; x; x /= 10) *--s = '0' + x % 10; return s; } /* Do not override this check. The floating point printing code below * depends on the float.h constants being right. If they are wrong, it * may overflow the stack. */ #if LDBL_MANT_DIG == 53 typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; #endif static int fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t) { uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion uint32_t *a, *d, *r, *z; uint32_t i; int e2=0, e, j, l; char buf[9+LDBL_MANT_DIG/4], *s; const char *prefix="-0X+0X 0X-0x+0x 0x"; int pl; char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; pl=1; if (signbit(y)) { y=-y; } else if (fl & MARK_POS) { prefix+=3; } else if (fl & PAD_POS) { prefix+=6; } else prefix++, pl=0; if (!isfinite(y)) { const char *ss = (t&32)?"inf":"INF"; if (y!=y) ss=(t&32)?"nan":"NAN"; pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); out(f, prefix, pl); out(f, ss, 3); pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); return MAX(w, 3+pl); } y = frexp((double)y, &e2) * 2; if (y) e2--; if ((t|32)=='a') { long double round = 8.0; int re; if (t&32) prefix += 9; pl += 2; if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; else re=LDBL_MANT_DIG/4-1-p; if (re) { while (re--) round*=16; if (*prefix=='-') { y=-y; y-=round; y+=round; y=-y; } else { y+=round; y-=round; } } estr=fmt_u(e2<0 ? -e2 : e2, ebuf); if (estr==ebuf) *--estr='0'; *--estr = (e2<0 ? '-' : '+'); *--estr = t+('p'-'a'); s=buf; do { int x=(int)y; *s++=xdigits[x]|(t&32); y=16*(y-x); if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; } while (y); if (p && s-buf-2 < p) l = (p+2) + (ebuf-estr); else l = (s-buf) + (ebuf-estr); pad(f, ' ', w, pl+l, fl); out(f, prefix, pl); pad(f, '0', w, pl+l, fl^ZERO_PAD); out(f, buf, s-buf); pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); out(f, estr, ebuf-estr); pad(f, ' ', w, pl+l, fl^LEFT_ADJ); return MAX(w, pl+l); } if (p<0) p=6; if (y) y *= 268435456.0, e2-=28; if (e2<0) a=r=z=big; else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; do { *z = (uint32_t)y; y = 1000000000*(y-*z++); } while (y); while (e2>0) { uint32_t carry=0; int sh=MIN(29,e2); for (d=z-1; d>=a; d--) { uint64_t x = ((uint64_t)*d<a && !z[-1]) z--; e2-=sh; } while (e2<0) { uint32_t carry=0, *b; int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9; for (d=a; d>sh) + carry; carry = (1000000000>>sh) * rm; } if (!*a) a++; if (carry) *z++ = carry; /* Avoid (slow!) computation past requested precision */ b = (t|32)=='f' ? r : a; if (z-b > need) z = b+need; e2+=sh; } if (a=i; i*=10, e++); else e=0; /* Perform rounding: j is precision after the radix (possibly neg) */ j = p - ((t|32)!='f')*e - ((t|32)=='g' && p); if (j < 9*(z-r-1)) { uint32_t x; /* We avoid C's broken division of negative numbers */ d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); j += 9*LDBL_MAX_EXP; j %= 9; for (i=10, j++; j<9; i*=10, j++); x = *d % i; /* Are there any significant digits past j? */ if (x || d+1!=z) { long double round = 2/LDBL_EPSILON; long double small; if (*d/i & 1) round += 2; if (x 999999999) { *d--=0; if (d=i; i*=10, e++); } } if (z>d+1) z=d+1; } for (; z>a && !z[-1]; z--); if ((t|32)=='g') { if (!p) p++; if (p>e && e>=-4) { t--; p-=e+1; } else { t-=2; p--; } if (!(fl&ALT_FORM)) { /* Count trailing zeros in last place */ if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); else j=9; if ((t|32)=='f') p = MIN(p,MAX(0,9*(z-r-1)-j)); else p = MIN(p,MAX(0,9*(z-r-1)+e-j)); } } l = 1 + p + (p || (fl&ALT_FORM)); if ((t|32)=='f') { if (e>0) l+=e; } else { estr=fmt_u(e<0 ? -e : e, ebuf); while(ebuf-estr<2) *--estr='0'; *--estr = (e<0 ? '-' : '+'); *--estr = t; l += ebuf-estr; } pad(f, ' ', w, pl+l, fl); out(f, prefix, pl); pad(f, '0', w, pl+l, fl^ZERO_PAD); if ((t|32)=='f') { if (a>r) a=r; for (d=a; d<=r; d++) { char *ss = fmt_u(*d, buf+9); if (d!=a) while (ss>buf) *--ss='0'; else if (ss==buf+9) *--ss='0'; out(f, ss, buf+9-ss); } if (p || (fl&ALT_FORM)) out(f, ".", 1); for (; d0; d++, p-=9) { char *ss = fmt_u(*d, buf+9); while (ss>buf) *--ss='0'; out(f, ss, MIN(9,p)); } pad(f, '0', p+9, 9, 0); } else { if (z<=a) z=a+1; for (d=a; d=0; d++) { char *ss = fmt_u(*d, buf+9); if (ss==buf+9) *--ss='0'; if (d!=a) while (ss>buf) *--ss='0'; else { out(f, ss++, 1); if (p>0||(fl&ALT_FORM)) out(f, ".", 1); } out(f, ss, MIN(buf+9-ss, p)); p -= buf+9-ss; } pad(f, '0', p+18, 18, 0); out(f, estr, ebuf-estr); } pad(f, ' ', w, pl+l, fl^LEFT_ADJ); return MAX(w, pl+l); } static int fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) { int p; if (*fmt != '%') { return -1; } ++fmt; if (*fmt == '.') { ++fmt; for (p = 0; ISDIGIT(*fmt); ++fmt) { p = 10 * p + (*fmt - '0'); } } else { p = -1; } switch (*fmt) { case 'e': case 'f': case 'g': case 'a': case 'E': case 'F': case 'G': case 'A': return fmt_fp(f, flo, 0, p, 0, *fmt); default: return -1; } } mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { struct fmt_args f; f.mrb = mrb; f.str = mrb_str_buf_new(mrb, 24); if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); } return f.str; } mruby-1.2.0+20160315+git4f20d58a/src/gc.c000066400000000000000000001165121267140355100167400ustar00rootroot00000000000000/* ** gc.c - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* = Tri-color Incremental Garbage Collection mruby's GC is Tri-color Incremental GC with Mark & Sweep. Algorithm details are omitted. Instead, the implementation part is described below. == Object's Color Each object can be painted in three colors: * White - Unmarked. * Gray - Marked, But the child objects are unmarked. * Black - Marked, the child objects are also marked. == Two White Types There're two white color types in a flip-flop fashion: White-A and White-B, which respectively represent the Current White color (the newly allocated objects in the current GC cycle) and the Sweep Target White color (the dead objects to be swept). A and B will be switched just at the beginning of the next GC cycle. At that time, all the dead objects have been swept, while the newly created objects in the current GC cycle which finally remains White are now regarded as dead objects. Instead of traversing all the White-A objects and painting them as White-B, just switch the meaning of White-A and White-B as this will be much cheaper. As a result, the objects we sweep in the current GC cycle are always left from the previous GC cycle. This allows us to sweep objects incrementally, without the disturbance of the newly created objects. == Execution Timing GC Execution Time and Each step interval are decided by live objects count. List of Adjustment API: * gc_interval_ratio_set * gc_step_ratio_set For details, see the comments for each function. == Write Barrier mruby implementer and C extension library writer must insert a write barrier when updating a reference from a field of an object. When updating a reference from a field of object A to object B, two different types of write barrier are available: * mrb_field_write_barrier - target B object for a mark. * mrb_write_barrier - target A object for a mark. == Generational Mode mruby's GC offers an Generational Mode while re-using the tri-color GC infrastructure. It will treat the Black objects as Old objects after each sweep phase, instead of painting them White. The key ideas are still the same as traditional generational GC: * Minor GC - just traverse the Young objects (Gray objects) in the mark phase, then only sweep the newly created objects, and leave the Old objects live. * Major GC - same as a full regular GC cycle. The difference from "traditional" generational GC is, that the major GC in mruby is triggered incrementally in a tri-color manner. For details, see the comments for each function. */ struct free_obj { MRB_OBJECT_HEADER; struct RBasic *next; }; typedef struct { union { struct free_obj free; struct RBasic basic; struct RObject object; struct RClass klass; struct RString string; struct RArray array; struct RHash hash; struct RRange range; struct RData data; struct RProc proc; struct RException exc; #ifdef MRB_WORD_BOXING struct RFloat floatv; struct RCptr cptr; #endif } as; } RVALUE; #ifdef GC_PROFILE #include #include static double program_invoke_time = 0; static double gc_time = 0; static double gc_total_time = 0; static double gettimeofday_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec * 1e-6; } #define GC_INVOKE_TIME_REPORT(with) do {\ fprintf(stderr, "%s\n", with);\ fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\ fprintf(stderr, "is_generational: %d\n", is_generational(gc));\ fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\ } while(0) #define GC_TIME_START do {\ gc_time = gettimeofday_time();\ } while(0) #define GC_TIME_STOP_AND_REPORT do {\ gc_time = gettimeofday_time() - gc_time;\ gc_total_time += gc_time;\ fprintf(stderr, "gc_state: %d\n", gc->state);\ fprintf(stderr, "live: %zu\n", gc->live);\ fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\ fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\ fprintf(stderr, "gc_time: %30.20f\n", gc_time);\ fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\ } while(0) #else #define GC_INVOKE_TIME_REPORT(s) #define GC_TIME_START #define GC_TIME_STOP_AND_REPORT #endif #ifdef GC_DEBUG #define DEBUG(x) (x) #else #define DEBUG(x) #endif #ifndef MRB_HEAP_PAGE_SIZE #define MRB_HEAP_PAGE_SIZE 1024 #endif #define GC_STEP_SIZE 1024 /* white: 011, black: 100, gray: 000 */ #define GC_GRAY 0 #define GC_WHITE_A 1 #define GC_WHITE_B (1 << 1) #define GC_BLACK (1 << 2) #define GC_WHITES (GC_WHITE_A | GC_WHITE_B) #define GC_COLOR_MASK 7 #define paint_gray(o) ((o)->color = GC_GRAY) #define paint_black(o) ((o)->color = GC_BLACK) #define paint_white(o) ((o)->color = GC_WHITES) #define paint_partial_white(s, o) ((o)->color = (s)->current_white_part) #define is_gray(o) ((o)->color == GC_GRAY) #define is_white(o) ((o)->color & GC_WHITES) #define is_black(o) ((o)->color & GC_BLACK) #define flip_white_part(s) ((s)->current_white_part = other_white_part(s)) #define other_white_part(s) ((s)->current_white_part ^ GC_WHITES) #define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE) #define objects(p) ((RVALUE *)p->objects) MRB_API void* mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); if (!p2 && len > 0 && mrb->gc.heaps) { mrb_full_gc(mrb); p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); } return p2; } MRB_API void* mrb_realloc(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = mrb_realloc_simple(mrb, p, len); if (!p2 && len) { if (mrb->gc.out_of_memory) { mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); /* mrb_panic(mrb); */ } else { mrb->gc.out_of_memory = TRUE; mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } } else { mrb->gc.out_of_memory = FALSE; } return p2; } MRB_API void* mrb_malloc(mrb_state *mrb, size_t len) { return mrb_realloc(mrb, 0, len); } MRB_API void* mrb_malloc_simple(mrb_state *mrb, size_t len) { return mrb_realloc_simple(mrb, 0, len); } MRB_API void* mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) { void *p; if (nelem > 0 && len > 0 && nelem <= SIZE_MAX / len) { size_t size; size = nelem * len; p = mrb_malloc(mrb, size); memset(p, 0, size); } else { p = NULL; } return p; } MRB_API void mrb_free(mrb_state *mrb, void *p) { (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); } MRB_API mrb_bool mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) { return is_dead(&mrb->gc, object); } static void link_heap_page(mrb_gc *gc, mrb_heap_page *page) { page->next = gc->heaps; if (gc->heaps) gc->heaps->prev = page; gc->heaps = page; } static void unlink_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->prev) page->prev->next = page->next; if (page->next) page->next->prev = page->prev; if (gc->heaps == page) gc->heaps = page->next; page->prev = NULL; page->next = NULL; } static void link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { page->free_next = gc->free_heaps; if (gc->free_heaps) { gc->free_heaps->free_prev = page; } gc->free_heaps = page; } static void unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->free_prev) page->free_prev->free_next = page->free_next; if (page->free_next) page->free_next->free_prev = page->free_prev; if (gc->free_heaps == page) gc->free_heaps = page->free_next; page->free_prev = NULL; page->free_next = NULL; } static void add_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE)); RVALUE *p, *e; struct RBasic *prev = NULL; for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt = MRB_TT_FREE; p->as.free.next = prev; prev = &p->as.basic; } page->freelist = prev; link_heap_page(gc, page); link_free_heap_page(gc, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 #define DEFAULT_MAJOR_GC_INC_RATIO 200 #define is_generational(gc) ((gc)->generational) #define is_major_gc(gc) (is_generational(gc) && (gc)->full) #define is_minor_gc(gc) (is_generational(gc) && !(gc)->full) void mrb_gc_init(mrb_state *mrb, mrb_gc *gc) { #ifndef MRB_GC_FIXED_ARENA gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); gc->arena_capa = MRB_GC_ARENA_SIZE; #endif gc->current_white_part = GC_WHITE_A; gc->heaps = NULL; gc->free_heaps = NULL; add_heap(mrb, gc); gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; gc->step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL gc->generational = TRUE; gc->full = TRUE; #endif #ifdef GC_PROFILE program_invoke_time = gettimeofday_time(); #endif } static void obj_free(mrb_state *mrb, struct RBasic *obj); void free_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = gc->heaps; mrb_heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic); } mrb_free(mrb, tmp); } } void mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) { free_heap(mrb, gc); #ifndef MRB_GC_FIXED_ARENA mrb_free(mrb, gc->arena); #endif } static void gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p) { #ifdef MRB_GC_FIXED_ARENA if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { /* arena overflow error */ gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error"); } #else if (gc->arena_idx >= gc->arena_capa) { /* extend arena */ gc->arena_capa = (int)(gc->arena_capa * 1.5); gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa); } #endif gc->arena[gc->arena_idx++] = p; } /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { if (mrb_immediate_p(obj)) return; gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj)); } #define GC_ROOT_NAME "_gc_root_" /* mrb_gc_register() keeps the object from GC. Register your object when it's exported to C world, without reference from Ruby world, e.g. callback arguments. Don't forget to remove the obejct using mrb_gc_unregister, otherwise your object will leak. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj) { mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); mrb_value table = mrb_gv_get(mrb, root); if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) { table = mrb_ary_new(mrb); mrb_gv_set(mrb, root, table); } mrb_ary_push(mrb, table, obj); } /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj) { mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); mrb_value table = mrb_gv_get(mrb, root); struct RArray *a; mrb_int i, j; if (mrb_nil_p(table)) return; if (mrb_type(table) != MRB_TT_ARRAY) { mrb_gv_set(mrb, root, mrb_nil_value()); return; } a = mrb_ary_ptr(table); mrb_ary_modify(mrb, a); for (i=j=0; ilen; i++) { if (!mrb_obj_eq(mrb, a->ptr[i], obj)) { a->ptr[j++] = a->ptr[i]; } } a->len = j; } MRB_API struct RBasic* mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) { struct RBasic *p; static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } }; mrb_gc *gc = &mrb->gc; #ifdef MRB_GC_STRESS mrb_full_gc(mrb); #endif if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } if (gc->free_heaps == NULL) { add_heap(mrb, gc); } p = gc->free_heaps->freelist; gc->free_heaps->freelist = ((struct free_obj*)p)->next; if (gc->free_heaps->freelist == NULL) { unlink_free_heap_page(gc, gc->free_heaps); } gc->live++; gc_protect(mrb, gc, p); *(RVALUE *)p = RVALUE_zero; p->tt = ttype; p->c = cls; paint_partial_white(gc, p); return p; } static inline void add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { abort(); } #endif paint_gray(obj); obj->gcnext = gc->gray_list; gc->gray_list = obj; } static void mark_context_stack(mrb_state *mrb, struct mrb_context *c) { size_t i; size_t e; e = c->stack - c->stbase; if (c->ci) e += c->ci->nregs; if (c->stbase + e > c->stend) e = c->stend - c->stbase; for (i=0; istbase[i]; if (!mrb_immediate_p(v)) { if (mrb_basic_ptr(v)->tt == MRB_TT_FREE) { c->stbase[i] = mrb_nil_value(); } else { mrb_gc_mark(mrb, mrb_basic_ptr(v)); } } } } static void mark_context(mrb_state *mrb, struct mrb_context *c) { int i, e = 0; mrb_callinfo *ci; /* mark stack */ mark_context_stack(mrb, c); /* mark VM stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { if (ci->eidx > e) { e = ci->eidx; } mrb_gc_mark(mrb, (struct RBasic*)ci->env); mrb_gc_mark(mrb, (struct RBasic*)ci->proc); mrb_gc_mark(mrb, (struct RBasic*)ci->target_class); } } /* mark ensure stack */ for (i=0; iensure[i]); } /* mark fibers */ if (c->prev && c->prev->fib) { mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib); } } static void gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); gc->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: { struct RClass *c = (struct RClass*)obj; if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) mrb_gc_mark_mt(mrb, c); mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); } break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: { struct RClass *c = (struct RClass*)obj; mrb_gc_mark_mt(mrb, c); mrb_gc_mark(mrb, (struct RBasic*)c->super); } /* fall through */ case MRB_TT_OBJECT: case MRB_TT_DATA: case MRB_TT_EXCEPTION: mrb_gc_mark_iv(mrb, (struct RObject*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; mrb_gc_mark(mrb, (struct RBasic*)p->env); mrb_gc_mark(mrb, (struct RBasic*)p->target_class); } break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; mrb_int i, len; len = MRB_ENV_STACK_LEN(e); for (i=0; istack[i]); } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c) mark_context(mrb, c); } break; case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; size_t i, e; for (i=0,e=a->len; iptr[i]); } } break; case MRB_TT_HASH: mrb_gc_mark_iv(mrb, (struct RObject*)obj); mrb_gc_mark_hash(mrb, (struct RHash*)obj); break; case MRB_TT_STRING: break; case MRB_TT_RANGE: { struct RRange *r = (struct RRange*)obj; if (r->edges) { mrb_gc_mark_value(mrb, r->edges->beg); mrb_gc_mark_value(mrb, r->edges->end); } } break; default: break; } } MRB_API void mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) { if (obj == 0) return; if (!is_white(obj)) return; mrb_assert((obj)->tt != MRB_TT_FREE); add_gray_list(mrb, &mrb->gc, obj); } static void obj_free(mrb_state *mrb, struct RBasic *obj) { DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { /* immediate - no mark */ case MRB_TT_TRUE: case MRB_TT_FIXNUM: case MRB_TT_SYMBOL: /* cannot happen */ return; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING break; #else return; #endif case MRB_TT_OBJECT: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); if ((struct RObject*)obj == mrb->backtrace.exc) mrb->backtrace.exc = 0; break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_ICLASS: if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN)) mrb_gc_free_mt(mrb, (struct RClass*)obj); break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; if (!MRB_ENV_STACK_SHARED_P(e)) { mrb_free(mrb, e->stack); e->stack = NULL; } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c && c != mrb->root_c) { mrb_callinfo *ci = c->ci; mrb_callinfo *ce = c->cibase; while (ce <= ci) { struct REnv *e = ci->env; if (e && !is_dead(&mrb->gc, e) && MRB_ENV_STACK_SHARED_P(e)) { mrb_env_unshare(mrb, e); } ci--; } mrb_free_context(mrb, c); } } break; case MRB_TT_ARRAY: if (ARY_SHARED_P(obj)) mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared); else mrb_free(mrb, ((struct RArray*)obj)->ptr); break; case MRB_TT_HASH: mrb_gc_free_iv(mrb, (struct RObject*)obj); mrb_gc_free_hash(mrb, (struct RHash*)obj); break; case MRB_TT_STRING: mrb_gc_free_str(mrb, (struct RString*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { mrb_irep_decref(mrb, p->body.irep); } } break; case MRB_TT_RANGE: mrb_free(mrb, ((struct RRange*)obj)->edges); break; case MRB_TT_DATA: { struct RData *d = (struct RData*)obj; if (d->type && d->type->dfree) { d->type->dfree(mrb, d->data); } mrb_gc_free_iv(mrb, (struct RObject*)obj); } break; default: break; } obj->tt = MRB_TT_FREE; } static void root_scan_phase(mrb_state *mrb, mrb_gc *gc) { size_t i, e; if (!is_minor_gc(gc)) { gc->gray_list = NULL; gc->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ for (i=0,e=gc->arena_idx; iarena[i]); } /* mark class hierarchy */ mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class); /* mark top_self */ mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); /* mark pre-allocated exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); mark_context(mrb, mrb->root_c); if (mrb->root_c->fib) { mrb_gc_mark(mrb, (struct RBasic*)mrb->root_c->fib); } if (mrb->root_c != mrb->c) { mark_context(mrb, mrb->c); } } static size_t gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; gc_mark_children(mrb, gc, obj); switch (obj->tt) { case MRB_TT_ICLASS: children++; break; case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: { struct RClass *c = (struct RClass*)obj; children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); children += mrb_gc_mark_mt_size(mrb, c); children++; } break; case MRB_TT_OBJECT: case MRB_TT_DATA: case MRB_TT_EXCEPTION: children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); break; case MRB_TT_ENV: children += (int)obj->flags; break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; size_t i; mrb_callinfo *ci; if (!c) break; /* mark stack */ i = c->stack - c->stbase; if (c->ci) i += c->ci->nregs; if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; /* mark ensure stack */ children += (c->ci) ? c->ci->eidx : 0; /* mark closure */ if (c->cibase) { for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) ; } children += i; } break; case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; children += a->len; } break; case MRB_TT_HASH: children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); children += mrb_gc_mark_hash_size(mrb, (struct RHash*)obj); break; case MRB_TT_PROC: case MRB_TT_RANGE: children+=2; break; default: break; } return children; } static void gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { while (gc->gray_list) { if (is_gray(gc->gray_list)) gc_mark_children(mrb, gc, gc->gray_list); else gc->gray_list = gc->gray_list->gcnext; } } static size_t incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { size_t tried_marks = 0; while (gc->gray_list && tried_marks < limit) { tried_marks += gc_gray_mark(mrb, gc, gc->gray_list); } return tried_marks; } static void final_marking_phase(mrb_state *mrb, mrb_gc *gc) { mark_context_stack(mrb, mrb->root_c); gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); gc->gray_list = gc->atomic_gray_list; gc->atomic_gray_list = NULL; gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); } static void prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc) { gc->state = MRB_GC_STATE_SWEEP; gc->sweeps = gc->heaps; gc->live_after_mark = gc->live; } static size_t incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { mrb_heap_page *page = gc->sweeps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; mrb_bool full = (page->freelist == NULL); if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = FALSE; } while (pas.basic)) { if (p->as.basic.tt != MRB_TT_FREE) { obj_free(mrb, &p->as.basic); p->as.free.next = page->freelist; page->freelist = (struct RBasic*)p; freed++; } } else { if (!is_generational(gc)) paint_partial_white(gc, &p->as.basic); /* next gc target */ dead_slot = 0; } p++; } /* free dead slot */ if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) { mrb_heap_page *next = page->next; unlink_heap_page(gc, page); unlink_free_heap_page(gc, page); mrb_free(mrb, page); page = next; } else { if (full && freed > 0) { link_free_heap_page(gc, page); } if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else page->old = FALSE; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; gc->live -= freed; gc->live_after_mark -= freed; } gc->sweeps = page; return tried_sweep; } static size_t incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) { switch (gc->state) { case MRB_GC_STATE_ROOT: root_scan_phase(mrb, gc); gc->state = MRB_GC_STATE_MARK; flip_white_part(gc); return 0; case MRB_GC_STATE_MARK: if (gc->gray_list) { return incremental_marking_phase(mrb, gc, limit); } else { final_marking_phase(mrb, gc); prepare_incremental_sweep(mrb, gc); return 0; } case MRB_GC_STATE_SWEEP: { size_t tried_sweep = 0; tried_sweep = incremental_sweep_phase(mrb, gc, limit); if (tried_sweep == 0) gc->state = MRB_GC_STATE_ROOT; return tried_sweep; } default: /* unknown state */ mrb_assert(0); return 0; } } static void incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state) { do { incremental_gc(mrb, gc, SIZE_MAX); } while (gc->state != to_state); } static void incremental_gc_step(mrb_state *mrb, mrb_gc *gc) { size_t limit = 0, result = 0; limit = (GC_STEP_SIZE/100) * gc->step_ratio; while (result < limit) { result += incremental_gc(mrb, gc, limit); if (gc->state == MRB_GC_STATE_ROOT) break; } gc->threshold = gc->live + GC_STEP_SIZE; } static void clear_all_old(mrb_state *mrb, mrb_gc *gc) { mrb_bool origin_mode = gc->generational; mrb_assert(is_generational(gc)); if (is_major_gc(gc)) { /* finish the half baked GC */ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ gc->generational = FALSE; prepare_incremental_sweep(mrb, gc); incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->generational = origin_mode; /* The gray objects have already been painted as white */ gc->atomic_gray_list = gc->gray_list = NULL; } MRB_API void mrb_incremental_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (gc->disabled) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; if (is_minor_gc(gc)) { incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } else { incremental_gc_step(mrb, gc); } if (gc->state == MRB_GC_STATE_ROOT) { mrb_assert(gc->live >= gc->live_after_mark); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (gc->threshold < GC_STEP_SIZE) { gc->threshold = GC_STEP_SIZE; } if (is_major_gc(gc)) { gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; gc->full = FALSE; } else if (is_minor_gc(gc)) { if (gc->live > gc->majorgc_old_threshold) { clear_all_old(mrb, gc); gc->full = TRUE; } } } GC_TIME_STOP_AND_REPORT; } /* Perform a full gc cycle */ MRB_API void mrb_full_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (gc->disabled) return; GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; if (is_generational(gc)) { /* clear all the old objects back to young */ clear_all_old(mrb, gc); gc->full = TRUE; } else if (gc->state != MRB_GC_STATE_ROOT) { /* finish half baked GC cycle */ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (is_generational(gc)) { gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; gc->full = FALSE; } GC_TIME_STOP_AND_REPORT; } MRB_API void mrb_garbage_collect(mrb_state *mrb) { mrb_full_gc(mrb); } MRB_API int mrb_gc_arena_save(mrb_state *mrb) { return mrb->gc.arena_idx; } MRB_API void mrb_gc_arena_restore(mrb_state *mrb, int idx) { mrb_gc *gc = &mrb->gc; #ifndef MRB_GC_FIXED_ARENA int capa = gc->arena_capa; if (idx < capa / 2) { capa = (int)(capa * 0.66); if (capa < MRB_GC_ARENA_SIZE) { capa = MRB_GC_ARENA_SIZE; } if (capa != gc->arena_capa) { gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); gc->arena_capa = capa; } } #endif gc->arena_idx = idx; } /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). */ MRB_API void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { mrb_gc *gc = &mrb->gc; if (!is_black(obj)) return; if (!is_white(value)) return; mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj))); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) { add_gray_list(mrb, gc, value); } else { mrb_assert(gc->state == MRB_GC_STATE_SWEEP); paint_partial_white(gc, obj); /* for never write barriers */ } } /* * Write barrier * Paint obj(Black) to obj(Gray). * * The object that is painted gray will be traversed atomically in final * mark phase. So you use this write barrier if it's frequency written spot. * e.g. Set element on Array. */ MRB_API void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { mrb_gc *gc = &mrb->gc; if (!is_black(obj)) return; mrb_assert(!is_dead(gc, obj)); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); paint_gray(obj); obj->gcnext = gc->atomic_gray_list; gc->atomic_gray_list = obj; } /* * call-seq: * GC.start -> nil * * Initiates full garbage collection. * */ static mrb_value gc_start(mrb_state *mrb, mrb_value obj) { mrb_full_gc(mrb); return mrb_nil_value(); } /* * call-seq: * GC.enable -> true or false * * Enables garbage collection, returning true if garbage * collection was previously disabled. * * GC.disable #=> false * GC.enable #=> true * GC.enable #=> false * */ static mrb_value gc_enable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = FALSE; return mrb_bool_value(old); } /* * call-seq: * GC.disable -> true or false * * Disables garbage collection, returning true if garbage * collection was already disabled. * * GC.disable #=> false * GC.disable #=> true * */ static mrb_value gc_disable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = TRUE; return mrb_bool_value(old); } /* * call-seq: * GC.interval_ratio -> fixnum * * Returns ratio of GC interval. Default value is 200(%). * */ static mrb_value gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(mrb->gc.interval_ratio); } /* * call-seq: * GC.interval_ratio = fixnum -> nil * * Updates ratio of GC interval. Default value is 200(%). * GC start as soon as after end all step of GC if you set 100(%). * */ static mrb_value gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.interval_ratio = ratio; return mrb_nil_value(); } /* * call-seq: * GC.step_ratio -> fixnum * * Returns step span ratio of Incremental GC. Default value is 200(%). * */ static mrb_value gc_step_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(mrb->gc.step_ratio); } /* * call-seq: * GC.step_ratio = fixnum -> nil * * Updates step span ratio of Incremental GC. Default value is 200(%). * 1 step of incrementalGC becomes long if a rate is big. * */ static mrb_value gc_step_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.step_ratio = ratio; return mrb_nil_value(); } static void change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { if (is_generational(gc) && !enable) { clear_all_old(mrb, gc); mrb_assert(gc->state == MRB_GC_STATE_ROOT); gc->full = FALSE; } else if (!is_generational(gc) && enable) { incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; gc->full = FALSE; } gc->generational = enable; } /* * call-seq: * GC.generational_mode -> true or false * * Returns generational or normal gc mode. * */ static mrb_value gc_generational_mode_get(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb->gc.generational); } /* * call-seq: * GC.generational_mode = true or false -> true or false * * Changes to generational or normal gc mode. * */ static mrb_value gc_generational_mode_set(mrb_state *mrb, mrb_value self) { mrb_bool enable; mrb_get_args(mrb, "b", &enable); if (mrb->gc.generational != enable) change_gen_gc_mode(mrb, &mrb->gc, enable); return mrb_bool_value(enable); } static void gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { mrb_heap_page* page = gc->heaps; while (page != NULL) { RVALUE *p, *pend; p = objects(page); pend = p + MRB_HEAP_PAGE_SIZE; for (;p < pend; p++) { (*callback)(mrb, &p->as.basic, data); } page = page->next; } } void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) { gc_each_objects(mrb, &mrb->gc, callback, data); } #ifdef GC_TEST #ifdef GC_DEBUG static mrb_value gc_test(mrb_state *, mrb_value); #endif #endif void mrb_init_gc(mrb_state *mrb) { struct RClass *gc; gc = mrb_define_module(mrb, "GC"); mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "enable", gc_enable, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "disable", gc_disable, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "interval_ratio", gc_interval_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "interval_ratio=", gc_interval_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "step_ratio", gc_step_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE()); #ifdef GC_TEST #ifdef GC_DEBUG mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE()); #endif #endif } #ifdef GC_TEST #ifdef GC_DEBUG void test_mrb_field_write_barrier(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj, *value; mrb_gc *gc = &mrb->gc; puts("test_mrb_field_write_barrier"); gc->generational = FALSE; obj = mrb_basic_ptr(mrb_ary_new(mrb)); value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value")); paint_black(obj); paint_partial_white(gc, value); puts(" in MRB_GC_STATE_MARK"); gc->state = MRB_GC_STATE_MARK; mrb_field_write_barrier(mrb, obj, value); mrb_assert(is_gray(value)); puts(" in MRB_GC_STATE_SWEEP"); paint_partial_white(gc, value); gc->state = MRB_GC_STATE_SWEEP; mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & gc->current_white_part); mrb_assert(value->color & gc->current_white_part); puts(" fail with black"); gc->state = MRB_GC_STATE_MARK; paint_white(obj); paint_partial_white(gc, value); mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & gc->current_white_part); puts(" fail with gray"); gc->state = MRB_GC_STATE_MARK; paint_black(obj); paint_gray(value); mrb_field_write_barrier(mrb, obj, value); mrb_assert(is_gray(value)); { puts("test_mrb_field_write_barrier_value"); obj = mrb_basic_ptr(mrb_ary_new(mrb)); mrb_value value = mrb_str_new_lit(mrb, "value"); paint_black(obj); paint_partial_white(gc, mrb_basic_ptr(value)); gc->state = MRB_GC_STATE_MARK; mrb_field_write_barrier_value(mrb, obj, value); mrb_assert(is_gray(mrb_basic_ptr(value))); } mrb_close(mrb); } void test_mrb_write_barrier(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj; mrb_gc *gc = &mrb->gc; puts("test_mrb_write_barrier"); obj = mrb_basic_ptr(mrb_ary_new(mrb)); paint_black(obj); puts(" in MRB_GC_STATE_MARK"); gc->state = MRB_GC_STATE_MARK; mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); mrb_assert(gc->atomic_gray_list == obj); puts(" fail with gray"); paint_gray(obj); mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); mrb_close(mrb); } void test_add_gray_list(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj1, *obj2; mrb_gc *gc = &mrb->gc; puts("test_add_gray_list"); change_gen_gc_mode(mrb, gc, FALSE); mrb_assert(gc->gray_list == NULL); obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); add_gray_list(mrb, gc, obj1); mrb_assert(gc->gray_list == obj1); mrb_assert(is_gray(obj1)); obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); add_gray_list(mrb, gc, obj2); mrb_assert(gc->gray_list == obj2); mrb_assert(gc->gray_list->gcnext == obj1); mrb_assert(is_gray(obj2)); mrb_close(mrb); } void test_gc_gray_mark(void) { mrb_state *mrb = mrb_open(); mrb_value obj_v, value_v; struct RBasic *obj; size_t gray_num = 0; mrb_gc *gc = &mrb->gc; puts("test_gc_gray_mark"); puts(" in MRB_TT_CLASS"); obj = (struct RBasic*)mrb->object_class; paint_gray(obj); gray_num = gc_gray_mark(mrb, gc, obj); mrb_assert(is_black(obj)); mrb_assert(gray_num > 1); puts(" in MRB_TT_ARRAY"); obj_v = mrb_ary_new(mrb); value_v = mrb_str_new_lit(mrb, "test"); paint_gray(mrb_basic_ptr(obj_v)); paint_partial_white(gc, mrb_basic_ptr(value_v)); mrb_ary_push(mrb, obj_v, value_v); gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v)); mrb_assert(is_black(mrb_basic_ptr(obj_v))); mrb_assert(is_gray(mrb_basic_ptr(value_v))); mrb_assert(gray_num == 1); mrb_close(mrb); } void test_incremental_gc(void) { mrb_state *mrb = mrb_open(); size_t max = ~0, live = 0, total = 0, freed = 0; RVALUE *free; mrb_heap_page *page; mrb_gc *gc = &mrb->gc; puts("test_incremental_gc"); change_gen_gc_mode(mrb, gc, FALSE); puts(" in mrb_full_gc"); mrb_full_gc(mrb); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in MRB_GC_STATE_ROOT"); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_MARK); puts(" in MRB_GC_STATE_MARK"); incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); mrb_assert(gc->state == MRB_GC_STATE_SWEEP); puts(" in MRB_GC_STATE_SWEEP"); page = gc->heaps; while (page) { RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; while (pas.basic)) { live++; } if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) { printf("%p\n", &p->as.basic); } p++; } page = page->next; total += MRB_HEAP_PAGE_SIZE; } mrb_assert(gc->gray_list == NULL); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_SWEEP); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_ROOT); free = (RVALUE*)gc->heaps->freelist; while (free) { freed++; free = (RVALUE*)free->as.free.next; } mrb_assert(gc->live == live); mrb_assert(gc->live == total-freed); puts("test_incremental_gc(gen)"); incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); change_gen_gc_mode(mrb, gc, TRUE); mrb_assert(gc->full == FALSE); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in minor"); mrb_assert(is_minor_gc(gc)); mrb_assert(gc->majorgc_old_threshold > 0); gc->majorgc_old_threshold = 0; mrb_incremental_gc(mrb); mrb_assert(gc->full == TRUE); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in major"); mrb_assert(is_major_gc(gc)); do { mrb_incremental_gc(mrb); } while (gc->state != MRB_GC_STATE_ROOT); mrb_assert(gc->full == FALSE); mrb_close(mrb); } void test_incremental_sweep_phase(void) { mrb_state *mrb = mrb_open(); mrb_gc *gc = &mrb->gc; puts("test_incremental_sweep_phase"); add_heap(mrb, gc); gc->sweeps = gc->heaps; mrb_assert(gc->heaps->next->next == NULL); mrb_assert(gc->free_heaps->next->next == NULL); incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3); mrb_assert(gc->heaps->next == NULL); mrb_assert(gc->heaps == gc->free_heaps); mrb_close(mrb); } static mrb_value gc_test(mrb_state *mrb, mrb_value self) { test_mrb_field_write_barrier(); test_mrb_write_barrier(); test_add_gray_list(); test_gc_gray_mark(); test_incremental_gc(); test_incremental_sweep_phase(); return mrb_nil_value(); } #endif /* GC_DEBUG */ #endif /* GC_TEST */ mruby-1.2.0+20160315+git4f20d58a/src/hash.c000066400000000000000000000535421267140355100172750ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include /* a function to get hash value of a float number */ mrb_int mrb_float_id(mrb_float f); static inline khint_t mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key) { enum mrb_vtype t = mrb_type(key); mrb_value hv; const char *p; mrb_int i, len; khint_t h; switch (t) { case MRB_TT_STRING: p = RSTRING_PTR(key); len = RSTRING_LEN(key); h = 0; for (i=0; iht; if (!h) return; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { mrb_value key = kh_key(h, k); mrb_value val = kh_value(h, k).v; mrb_gc_mark_value(mrb, key); mrb_gc_mark_value(mrb, val); } } } size_t mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash) { if (!hash->ht) return 0; return kh_size(hash->ht)*2; } void mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) { if (hash->ht) kh_destroy(ht, mrb, hash->ht); } MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, int capa) { struct RHash *h; h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); h->ht = kh_init(ht, mrb); if (capa > 0) { kh_resize(ht, mrb, h->ht, capa); } h->iv = 0; return mrb_obj_value(h); } MRB_API mrb_value mrb_hash_new(mrb_state *mrb) { return mrb_hash_new_capa(mrb, 0); } MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; if (h) { k = kh_get(ht, mrb, h, key); if (k != kh_end(h)) return kh_value(h, k).v; } /* not found */ if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); } return RHASH_IFNONE(hash); } return mrb_nil_value(); } MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; if (h) { k = kh_get(ht, mrb, h, key); if (k != kh_end(h)) return kh_value(h, k).v; } /* not found */ return def; } MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) { khash_t(ht) *h; khiter_t k; int r; mrb_hash_modify(mrb, hash); h = RHASH_TBL(hash); if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb); k = kh_put2(ht, mrb, h, key, &r); kh_value(h, k).v = val; if (r != 0) { /* expand */ int ai = mrb_gc_arena_save(mrb); key = kh_key(h, k) = KEY(key); mrb_gc_arena_restore(mrb, ai); kh_value(h, k).n = kh_size(h)-1; } mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); return; } static mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash) { struct RHash* ret; khash_t(ht) *h, *ret_h; khiter_t k, ret_k; h = RHASH_TBL(hash); ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); ret->ht = kh_init(ht, mrb); if (kh_size(h) > 0) { ret_h = ret->ht; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { int ai = mrb_gc_arena_save(mrb); ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k))); mrb_gc_arena_restore(mrb, ai); kh_val(ret_h, ret_k) = kh_val(h, k); } } } return mrb_obj_value(ret); } MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash) { return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); } MRB_API khash_t(ht)* mrb_hash_tbl(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (!h) { return RHASH_TBL(hash) = kh_init(ht, mrb); } return h; } static void mrb_hash_modify(mrb_state *mrb, mrb_value hash) { mrb_hash_tbl(mrb, hash); } /* 15.2.13.4.16 */ /* * call-seq: * Hash.new -> new_hash * Hash.new(obj) -> new_hash * Hash.new {|hash, key| block } -> new_hash * * Returns a new, empty hash. If this hash is subsequently accessed by * a key that doesn't correspond to a hash entry, the value returned * depends on the style of new used to create the hash. In * the first form, the access returns nil. If * obj is specified, this single object will be used for * all default values. If a block is specified, it will be * called with the hash object and the key, and should return the * default value. It is the block's responsibility to store the value * in the hash if required. * * h = Hash.new("Go Fish") * h["a"] = 100 * h["b"] = 200 * h["a"] #=> 100 * h["c"] #=> "Go Fish" * # The following alters the single default object * h["c"].upcase! #=> "GO FISH" * h["d"] #=> "GO FISH" * h.keys #=> ["a", "b"] * * # While this creates a new default object each time * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } * h["c"] #=> "Go Fish: c" * h["c"].upcase! #=> "GO FISH: C" * h["d"] #=> "Go Fish: d" * h.keys #=> ["c", "d"] * */ static mrb_value mrb_hash_init(mrb_state *mrb, mrb_value hash) { mrb_value block, ifnone; mrb_bool ifnone_p; ifnone = mrb_nil_value(); mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p); mrb_hash_modify(mrb, hash); if (!mrb_nil_p(block)) { if (ifnone_p) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; ifnone = block; } if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); } return hash; } /* 15.2.13.4.2 */ /* * call-seq: * hsh[key] -> value * * Element Reference---Retrieves the value object corresponding * to the key object. If not found, returns the default value (see * Hash::new for details). * * h = { "a" => 100, "b" => 200 } * h["a"] #=> 100 * h["c"] #=> nil * */ static mrb_value mrb_hash_aget(mrb_state *mrb, mrb_value self) { mrb_value key; mrb_get_args(mrb, "o", &key); return mrb_hash_get(mrb, self, key); } /* 15.2.13.4.5 */ /* * call-seq: * hsh.default(key=nil) -> obj * * Returns the default value, the value that would be returned by * hsh[key] if key did not exist in hsh. * See also Hash::new and Hash#default=. * * h = Hash.new #=> {} * h.default #=> nil * h.default(2) #=> nil * * h = Hash.new("cat") #=> {} * h.default #=> "cat" * h.default(2) #=> "cat" * * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {} * h.default #=> nil * h.default(2) #=> 20 */ static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash) { mrb_value key; mrb_bool given; mrb_get_args(mrb, "|o?", &key, &given); if (MRB_RHASH_PROCDEFAULT_P(hash)) { if (!given) return mrb_nil_value(); return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); } else { return RHASH_IFNONE(hash); } } /* 15.2.13.4.6 */ /* * call-seq: * hsh.default = obj -> obj * * Sets the default value, the value returned for a key that does not * exist in the hash. It is not possible to set the default to a * Proc that will be executed on each key lookup. * * h = { "a" => 100, "b" => 200 } * h.default = "Go fish" * h["a"] #=> 100 * h["z"] #=> "Go fish" * # This doesn't do what you might hope... * h.default = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> # * h["cat"] #=> # */ static mrb_value mrb_hash_set_default(mrb_state *mrb, mrb_value hash) { mrb_value ifnone; mrb_get_args(mrb, "o", &ifnone); mrb_hash_modify(mrb, hash); mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; } return ifnone; } /* 15.2.13.4.7 */ /* * call-seq: * hsh.default_proc -> anObject * * If Hash::new was invoked with a block, return that * block, otherwise return nil. * * h = Hash.new {|h,k| h[k] = k*k } #=> {} * p = h.default_proc #=> # * a = [] #=> [] * p.call(a, 2) * a #=> [nil, nil, 4] */ static mrb_value mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return RHASH_PROCDEFAULT(hash); } return mrb_nil_value(); } /* * call-seq: * hsh.default_proc = proc_obj -> proc_obj * * Sets the default proc to be executed on each key lookup. * * h.default_proc = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> 4 * h["cat"] #=> "catcat" */ static mrb_value mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) { mrb_value ifnone; mrb_get_args(mrb, "o", &ifnone); mrb_hash_modify(mrb, hash); mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; RHASH(hash)->flags |= MRB_HASH_DEFAULT; } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; } return ifnone; } MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; mrb_value delVal; mrb_int n; if (h) { k = kh_get(ht, mrb, h, key); if (k != kh_end(h)) { delVal = kh_value(h, k).v; n = kh_value(h, k).n; kh_del(ht, mrb, h, k); for (k = kh_begin(h); k != kh_end(h); k++) { if (!kh_exist(h, k)) continue; if (kh_value(h, k).n > n) kh_value(h, k).n--; } return delVal; } } /* not found */ return mrb_nil_value(); } /* 15.2.13.4.8 */ /* * call-seq: * hsh.delete(key) -> value * hsh.delete(key) {| key | block } -> value * * Deletes and returns a key-value pair from hsh whose key is * equal to key. If the key is not found, returns the * default value. If the optional code block is given and the * key is not found, pass in the key and return the result of * block. * * h = { "a" => 100, "b" => 200 } * h.delete("a") #=> 100 * h.delete("z") #=> nil * h.delete("z") { |el| "#{el} not found" } #=> "z not found" * */ static mrb_value mrb_hash_delete(mrb_state *mrb, mrb_value self) { mrb_value key; mrb_get_args(mrb, "o", &key); return mrb_hash_delete_key(mrb, self, key); } /* 15.2.13.4.24 */ /* * call-seq: * hsh.shift -> anArray or obj * * Removes a key-value pair from hsh and returns it as the * two-item array [ key, value ], or * the hash's default value if the hash is empty. * * h = { 1 => "a", 2 => "b", 3 => "c" } * h.shift #=> [1, "a"] * h #=> {2=>"b", 3=>"c"} */ static mrb_value mrb_hash_shift(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; mrb_value delKey, delVal; mrb_hash_modify(mrb, hash); if (h && kh_size(h) > 0) { for (k = kh_begin(h); k != kh_end(h); k++) { if (!kh_exist(h, k)) continue; delKey = kh_key(h, k); mrb_gc_protect(mrb, delKey); delVal = mrb_hash_delete_key(mrb, hash, delKey); mrb_gc_protect(mrb, delVal); return mrb_assoc_new(mrb, delKey, delVal); } } if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } /* 15.2.13.4.4 */ /* * call-seq: * hsh.clear -> hsh * * Removes all key-value pairs from `hsh`. * * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} * h.clear #=> {} * */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (h) kh_clear(ht, mrb, h); return hash; } /* 15.2.13.4.3 */ /* 15.2.13.4.26 */ /* * call-seq: * hsh[key] = value -> value * hsh.store(key, value) -> value * * Element Assignment---Associates the value given by * value with the key given by key. * key should not have its value changed while it is in * use as a key (a String passed as a key will be * duplicated and frozen). * * h = { "a" => 100, "b" => 200 } * h["a"] = 9 * h["c"] = 4 * h #=> {"a"=>9, "b"=>200, "c"=>4} * */ static mrb_value mrb_hash_aset(mrb_state *mrb, mrb_value self) { mrb_value key, val; mrb_get_args(mrb, "oo", &key, &val); mrb_hash_set(mrb, self, key, val); return val; } /* 15.2.13.4.20 */ /* 15.2.13.4.25 */ /* * call-seq: * hsh.length -> fixnum * hsh.size -> fixnum * * Returns the number of key-value pairs in the hash. * * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 } * h.length #=> 4 * h.delete("a") #=> 200 * h.length #=> 3 */ static mrb_value mrb_hash_size_m(mrb_state *mrb, mrb_value self) { khash_t(ht) *h = RHASH_TBL(self); if (!h) return mrb_fixnum_value(0); return mrb_fixnum_value(kh_size(h)); } /* 15.2.13.4.12 */ /* * call-seq: * hsh.empty? -> true or false * * Returns true if hsh contains no key-value pairs. * * {}.empty? #=> true * */ MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { khash_t(ht) *h = RHASH_TBL(self); if (h) return mrb_bool_value(kh_size(h) == 0); return mrb_true_value(); } /* 15.2.13.4.29 (x)*/ /* * call-seq: * hsh.to_hash => hsh * * Returns +self+. */ static mrb_value mrb_hash_to_hash(mrb_state *mrb, mrb_value hash) { return hash; } /* 15.2.13.4.19 */ /* * call-seq: * hsh.keys -> array * * Returns a new array populated with the keys from this hash. See also * Hash#values. * * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 } * h.keys #=> ["a", "b", "c", "d"] * */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; mrb_value ary; mrb_value *p; if (!h || kh_size(h) == 0) return mrb_ary_new(mrb); ary = mrb_ary_new_capa(mrb, kh_size(h)); mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value()); p = mrb_ary_ptr(ary)->ptr; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { mrb_value kv = kh_key(h, k); mrb_hash_value hv = kh_value(h, k); p[hv.n] = kv; } } return ary; } /* 15.2.13.4.28 */ /* * call-seq: * hsh.values -> array * * Returns a new array populated with the values from hsh. See * also Hash#keys. * * h = { "a" => 100, "b" => 200, "c" => 300 } * h.values #=> [100, 200, 300] * */ static mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; mrb_value ary; if (!h) return mrb_ary_new(mrb); ary = mrb_ary_new_capa(mrb, kh_size(h)); for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { mrb_hash_value hv = kh_value(h, k); mrb_ary_set(mrb, ary, hv.n, hv.v); } } return ary; } /* 15.2.13.4.13 */ /* 15.2.13.4.15 */ /* 15.2.13.4.18 */ /* 15.2.13.4.21 */ /* * call-seq: * hsh.has_key?(key) -> true or false * hsh.include?(key) -> true or false * hsh.key?(key) -> true or false * hsh.member?(key) -> true or false * * Returns true if the given key is present in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_key?("a") #=> true * h.has_key?("z") #=> false * */ static mrb_value mrb_hash_has_key(mrb_state *mrb, mrb_value hash) { mrb_value key; khash_t(ht) *h; khiter_t k; mrb_get_args(mrb, "o", &key); h = RHASH_TBL(hash); if (h) { k = kh_get(ht, mrb, h, key); return mrb_bool_value(k != kh_end(h)); } return mrb_false_value(); } /* 15.2.13.4.14 */ /* 15.2.13.4.27 */ /* * call-seq: * hsh.has_value?(value) -> true or false * hsh.value?(value) -> true or false * * Returns true if the given value is present for some key * in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_value?(100) #=> true * h.has_value?(999) #=> false */ static mrb_value mrb_hash_has_value(mrb_state *mrb, mrb_value hash) { mrb_value val; khash_t(ht) *h; khiter_t k; mrb_get_args(mrb, "o", &val); h = RHASH_TBL(hash); if (h) { for (k = kh_begin(h); k != kh_end(h); k++) { if (!kh_exist(h, k)) continue; if (mrb_equal(mrb, kh_value(h, k).v, val)) { return mrb_true_value(); } } } return mrb_false_value(); } void mrb_init_hash(mrb_state *mrb) { struct RClass *h; mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */ mrb_define_method(mrb, h, "[]=", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.3 */ mrb_define_method(mrb, h, "clear", mrb_hash_clear, MRB_ARGS_NONE()); /* 15.2.13.4.4 */ mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_ANY()); /* 15.2.13.4.5 */ mrb_define_method(mrb, h, "default=", mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6 */ mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */ mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */ mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)); /* 15.2.13.4.16 */ mrb_define_method(mrb, h, "key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */ mrb_define_method(mrb, h, "keys", mrb_hash_keys, MRB_ARGS_NONE()); /* 15.2.13.4.19 */ mrb_define_method(mrb, h, "length", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.20 */ mrb_define_method(mrb, h, "member?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */ mrb_define_method(mrb, h, "shift", mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */ mrb_define_method(mrb, h, "dup", mrb_hash_dup, MRB_ARGS_NONE()); mrb_define_method(mrb, h, "size", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.25 */ mrb_define_method(mrb, h, "store", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */ mrb_define_method(mrb, h, "value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */ mrb_define_method(mrb, h, "values", mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */ mrb_define_method(mrb, h, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/ } mruby-1.2.0+20160315+git4f20d58a/src/init.c000066400000000000000000000024341267140355100173070ustar00rootroot00000000000000/* ** init.c - initialize mruby core ** ** See Copyright Notice in mruby.h */ #include void mrb_init_symtbl(mrb_state*); void mrb_init_class(mrb_state*); void mrb_init_object(mrb_state*); void mrb_init_kernel(mrb_state*); void mrb_init_comparable(mrb_state*); void mrb_init_enumerable(mrb_state*); void mrb_init_symbol(mrb_state*); void mrb_init_exception(mrb_state*); void mrb_init_proc(mrb_state*); void mrb_init_string(mrb_state*); void mrb_init_array(mrb_state*); void mrb_init_hash(mrb_state*); void mrb_init_numeric(mrb_state*); void mrb_init_range(mrb_state*); void mrb_init_gc(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_version(mrb_state*); void mrb_init_mrblib(mrb_state*); #define DONE mrb_gc_arena_restore(mrb, 0); void mrb_init_core(mrb_state *mrb) { mrb_init_symtbl(mrb); DONE; mrb_init_class(mrb); DONE; mrb_init_object(mrb); DONE; mrb_init_kernel(mrb); DONE; mrb_init_comparable(mrb); DONE; mrb_init_enumerable(mrb); DONE; mrb_init_symbol(mrb); DONE; mrb_init_exception(mrb); DONE; mrb_init_proc(mrb); DONE; mrb_init_string(mrb); DONE; mrb_init_array(mrb); DONE; mrb_init_hash(mrb); DONE; mrb_init_numeric(mrb); DONE; mrb_init_range(mrb); DONE; mrb_init_gc(mrb); DONE; mrb_init_version(mrb); DONE; mrb_init_mrblib(mrb); DONE; } mruby-1.2.0+20160315+git4f20d58a/src/kernel.c000066400000000000000000001050051267140355100176220ustar00rootroot00000000000000/* ** kernel.c - Kernel module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include typedef enum { NOEX_PUBLIC = 0x00, NOEX_NOSUPER = 0x01, NOEX_PRIVATE = 0x02, NOEX_PROTECTED = 0x04, NOEX_MASK = 0x06, NOEX_BASIC = 0x08, NOEX_UNDEF = NOEX_NOSUPER, NOEX_MODFUNC = 0x12, NOEX_SUPER = 0x20, NOEX_VCALL = 0x40, NOEX_RESPONDS = 0x80 } mrb_method_flag_t; static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s")); if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s)) return TRUE; return FALSE; } /* 15.3.1.3.17 */ /* * call-seq: * obj.inspect -> string * * Returns a string containing a human-readable representation of * obj. If not overridden and no instance variables, uses the * to_s method to generate the string. * obj. If not overridden, uses the to_s method to * generate the string. * * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" * Time.new.inspect #=> "2008-03-08 19:43:39 +0900" */ MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value obj) { if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) { return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj)); } return mrb_any_to_s(mrb, obj); } /* 15.3.1.3.1 */ /* 15.3.1.3.10 */ /* 15.3.1.3.11 */ /* * call-seq: * obj == other -> true or false * obj.equal?(other) -> true or false * obj.eql?(other) -> true or false * * Equality---At the Object level, == returns * true only if obj and other are the * same object. Typically, this method is overridden in descendant * classes to provide class-specific meaning. * * Unlike ==, the equal? method should never be * overridden by subclasses: it is used to determine object identity * (that is, a.equal?(b) iff a is the same * object as b). * * The eql? method returns true if * obj and anObject have the same value. Used by * Hash to test members for equality. For objects of * class Object, eql? is synonymous with * ==. Subclasses normally continue this tradition, but * there are exceptions. Numeric types, for example, * perform type conversion across ==, but not across * eql?, so: * * 1 == 1.0 #=> true * 1.eql? 1.0 #=> false */ static mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); } static mrb_value mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(!mrb_equal(mrb, self, arg)); } /* 15.3.1.3.2 */ /* * call-seq: * obj === other -> true or false * * Case Equality---For class Object, effectively the same * as calling #==, but typically overridden by descendants * to provide meaningful semantics in case statements. */ static mrb_value mrb_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_equal(mrb, self, arg)); } /* 15.3.1.3.3 */ /* 15.3.1.3.33 */ /* * Document-method: __id__ * Document-method: object_id * * call-seq: * obj.__id__ -> fixnum * obj.object_id -> fixnum * * Returns an integer identifier for obj. The same number will * be returned on all calls to id for a given object, and * no two active objects will share an id. * Object#object_id is a different concept from the * :name notation, which returns the symbol id of * name. Replaces the deprecated Object#id. */ static mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } /* 15.3.1.2.2 */ /* 15.3.1.2.5 */ /* 15.3.1.3.6 */ /* 15.3.1.3.25 */ /* * call-seq: * block_given? -> true or false * iterator? -> true or false * * Returns true if yield would execute a * block in the current context. The iterator? form * is mildly deprecated. * * def try * if block_given? * yield * else * "no block" * end * end * try #=> "no block" * try { "hello" } #=> "hello" * try do "hello" end #=> "hello" */ static mrb_value mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = mrb->c->ci; mrb_value *bp; mrb_bool given_p; bp = ci->stackent + 1; ci--; if (ci <= mrb->c->cibase) { given_p = FALSE; } else { /* block_given? called within block; check upper scope */ if (ci->proc->env) { struct REnv *e = ci->proc->env; mrb_value *sp; while (e->c) { e = (struct REnv*)e->c; } sp = e->stack; if (sp) { /* top-level does not have block slot (alway false) */ if (sp == mrb->c->stbase) return mrb_false_value(); ci = mrb->c->cibase + e->cioff; bp = ci[1].stackent + 1; } } if (ci->argc > 0) { bp += ci->argc; } given_p = !mrb_nil_p(*bp); } return mrb_bool_value(given_p); } /* 15.3.1.3.7 */ /* * call-seq: * obj.class -> class * * Returns the class of obj. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Fixnum * self.class #=> Object */ static mrb_value mrb_obj_class_m(mrb_state *mrb, mrb_value self) { return mrb_obj_value(mrb_obj_class(mrb, self)); } static struct RClass* mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) { struct RClass *klass = mrb_basic_ptr(obj)->c; if (klass->tt != MRB_TT_SCLASS) return klass; else { /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) { clone->c = clone; } else { clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj); } if (klass->mt) { clone->mt = kh_copy(mt, mrb, klass->mt); } else { clone->mt = kh_init(mt, mrb); } clone->tt = MRB_TT_SCLASS; return clone; } } static void copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) { struct RClass *dc = mrb_class_ptr(dst); struct RClass *sc = mrb_class_ptr(src); /* if the origin is not the same as the class, then the origin and the current class need to be copied */ if (sc->flags & MRB_FLAG_IS_PREPENDED) { struct RClass *c0 = sc->super; struct RClass *c1 = dc; /* copy prepended iclasses */ while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) { c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); c1 = c1->super; c0 = c0->super; } c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); c1->super->flags |= MRB_FLAG_IS_ORIGIN; } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; } static void init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_MODULE: copy_class(mrb, dest, obj); /* fall through */ case MRB_TT_OBJECT: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: case MRB_TT_EXCEPTION: mrb_iv_copy(mrb, dest, obj); break; default: break; } mrb_funcall(mrb, dest, "initialize_copy", 1, obj); } /* 15.3.1.3.8 */ /* * call-seq: * obj.clone -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. Copies * the frozen state of obj. See also the discussion * under Object#dup. * * class Klass * attr_accessor :str * end * s1 = Klass.new #=> # * s1.str = "Hello" #=> "Hello" * s2 = s1.clone #=> # * s2.str[1,4] = "i" #=> "i" * s1.inspect #=> "#" * s2.inspect #=> "#" * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. * * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone. */ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self) { struct RObject *p; mrb_value clone; if (mrb_immediate_p(self)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self); } if (mrb_type(self) == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); } p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self)); p->c = mrb_singleton_class_clone(mrb, self); clone = mrb_obj_value(p); init_copy(mrb, clone, self); return clone; } /* 15.3.1.3.9 */ /* * call-seq: * obj.dup -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. * dup copies the frozen state of obj. See also * the discussion under Object#clone. In general, * clone and dup may have different semantics * in descendant classes. While clone is used to duplicate * an object, including its internal state, dup typically * uses the class of the descendant object to create the new instance. * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj) { struct RBasic *p; mrb_value dup; if (mrb_immediate_p(obj)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj); } if (mrb_type(obj) == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); } p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); dup = mrb_obj_value(p); init_copy(mrb, dup, obj); return dup; } static mrb_value mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj) { mrb_int i; if (argc == 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 1)"); } for (i = 0; i < argc; i++) { mrb_check_type(mrb, argv[i], MRB_TT_MODULE); } while (argc--) { mrb_funcall(mrb, argv[argc], "extend_object", 1, obj); mrb_funcall(mrb, argv[argc], "extended", 1, obj); } return obj; } /* 15.3.1.3.13 */ /* * call-seq: * obj.extend(module, ...) -> obj * * Adds to _obj_ the instance methods from each module given as a * parameter. * * module Mod * def hello * "Hello from Mod.\n" * end * end * * class Klass * def hello * "Hello from Klass.\n" * end * end * * k = Klass.new * k.hello #=> "Hello from Klass.\n" * k.extend(Mod) #=> # * k.hello #=> "Hello from Mod.\n" */ static mrb_value mrb_obj_extend_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); return mrb_obj_extend(mrb, argc, argv, self); } /* 15.3.1.3.15 */ /* * call-seq: * obj.hash -> fixnum * * Generates a Fixnum hash value for this object. This * function must have the property that a.eql?(b) implies * a.hash == b.hash. The hash value is used by class * Hash. Any hash value that exceeds the capacity of a * Fixnum will be truncated before being used. */ MRB_API mrb_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } /* 15.3.1.3.16 */ static mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self) { mrb_value orig; mrb_get_args(mrb, "o", &orig); if (mrb_obj_equal(mrb, self, orig)) return self; if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); } return self; } /* implementation of instance_eval */ mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) { if (mrb_obj_class(mrb, obj) == c) return TRUE; return FALSE; } /* 15.3.1.3.19 */ /* * call-seq: * obj.instance_of?(class) -> true or false * * Returns true if obj is an instance of the given * class. See also Object#kind_of?. */ static mrb_value obj_is_instance_of(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "C", &arg); return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg))); } /* 15.3.1.3.20 */ /* * call-seq: * obj.instance_variable_defined?(symbol) -> true or false * * Returns true if the given instance variable is * defined in obj. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_defined?(:@a) #=> true * fred.instance_variable_defined?("@b") #=> true * fred.instance_variable_defined?("@c") #=> false */ static mrb_value mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); mrb_iv_check(mrb, sym); return mrb_bool_value(mrb_iv_defined(mrb, self, sym)); } /* 15.3.1.3.21 */ /* * call-seq: * obj.instance_variable_get(symbol) -> obj * * Returns the value of the given instance variable, or nil if the * instance variable is not set. The @ part of the * variable name should be included for regular instance * variables. Throws a NameError exception if the * supplied symbol is not valid as an instance variable name. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_get(:@a) #=> "cat" * fred.instance_variable_get("@b") #=> 99 */ static mrb_value mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_get_args(mrb, "n", &iv_name); mrb_iv_check(mrb, iv_name); return mrb_iv_get(mrb, self, iv_name); } /* 15.3.1.3.22 */ /* * call-seq: * obj.instance_variable_set(symbol, obj) -> obj * * Sets the instance variable names by symbol to * object, thereby frustrating the efforts of the class's * author to attempt to provide proper encapsulation. The variable * did not have to exist prior to this call. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_set(:@a, 'dog') #=> "dog" * fred.instance_variable_set(:@c, 'cat') #=> "cat" * fred.inspect #=> "#" */ static mrb_value mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_value val; mrb_get_args(mrb, "no", &iv_name, &val); mrb_iv_check(mrb, iv_name); mrb_iv_set(mrb, self, iv_name, val); return val; } /* 15.3.1.3.24 */ /* 15.3.1.3.26 */ /* * call-seq: * obj.is_a?(class) -> true or false * obj.kind_of?(class) -> true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "C", &arg); return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg))); } KHASH_DECLARE(st, mrb_sym, char, FALSE) KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal) static void method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set) { khint_t i; khash_t(mt) *h = klass->mt; if (!h) return; for (i=0;iflags & MRB_FLAG_IS_PREPENDED)) { MRB_CLASS_ORIGIN(klass); prepended = TRUE; } oldklass = 0; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set); if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { if (!recur) break; } oldklass = klass; klass = klass->super; } ary = mrb_ary_new(mrb); for (i=0;itt == MRB_TT_SCLASS)) { method_entry_loop(mrb, klass, set); klass = klass->super; } if (recur) { while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) { method_entry_loop(mrb, klass, set); klass = klass->super; } } ary = mrb_ary_new(mrb); for (i=0;i array * * Returns a list of the names of methods publicly accessible in * obj. This will include all the methods accessible in * obj's ancestors. * * class Klass * def kMethod() * end * end * k = Klass.new * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?, * # :class, :instance_variable_set, * # :methods, :extend, :__send__, :instance_eval] * k.methods.length #=> 42 */ static mrb_value mrb_obj_methods_m(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */ } /* 15.3.1.3.32 */ /* * call_seq: * nil.nil? -> true * .nil? -> false * * Only the object nil responds true to nil?. */ static mrb_value mrb_false(mrb_state *mrb, mrb_value self) { return mrb_false_value(); } /* 15.3.1.3.36 */ /* * call-seq: * obj.private_methods(all=true) -> array * * Returns the list of private methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_private_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */ } /* 15.3.1.3.37 */ /* * call-seq: * obj.protected_methods(all=true) -> array * * Returns the list of protected methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_protected_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */ } /* 15.3.1.3.38 */ /* * call-seq: * obj.public_methods(all=true) -> array * * Returns the list of public methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_public_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */ } /* 15.3.1.2.12 */ /* 15.3.1.3.40 */ /* * call-seq: * raise * raise(string) * raise(exception [, string]) * * With no arguments, raises a RuntimeError * With a single +String+ argument, raises a * +RuntimeError+ with the string as a message. Otherwise, * the first parameter should be the name of an +Exception+ * class (or an object that returns an +Exception+ object when sent * an +exception+ message). The optional second parameter sets the * message associated with the exception, and the third parameter is an * array of callback information. Exceptions are caught by the * +rescue+ clause of begin...end blocks. * * raise "Failed to create socket" * raise ArgumentError, "No parameters", caller */ MRB_API mrb_value mrb_f_raise(mrb_state *mrb, mrb_value self) { mrb_value a[2], exc; int argc; argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]); switch (argc) { case 0: mrb_raise(mrb, E_RUNTIME_ERROR, ""); break; case 1: a[1] = mrb_check_string_type(mrb, a[0]); if (!mrb_nil_p(a[1])) { argc = 2; a[0] = mrb_obj_value(E_RUNTIME_ERROR); } /* fall through */ default: exc = mrb_make_exception(mrb, argc, a); mrb_obj_iv_set(mrb, mrb_obj_ptr(exc), mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, mrb->c->ci->pc)); mrb_exc_raise(mrb, exc); break; } return mrb_nil_value(); /* not reached */ } /* 15.3.1.3.41 */ /* * call-seq: * obj.remove_instance_variable(symbol) -> obj * * Removes the named instance variable from obj, returning that * variable's value. * * class Dummy * attr_reader :var * def initialize * @var = 99 * end * def remove * remove_instance_variable(:@var) * end * end * d = Dummy.new * d.var #=> 99 * d.remove #=> 99 * d.var #=> nil */ static mrb_value mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_value val; mrb_get_args(mrb, "n", &sym); mrb_iv_check(mrb, sym); val = mrb_iv_remove(mrb, self, sym); if (mrb_undef_p(val)) { mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym)); } return val; } static inline mrb_bool basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub) { return mrb_respond_to(mrb, obj, id); } /* 15.3.1.3.43 */ /* * call-seq: * obj.respond_to?(symbol, include_private=false) -> true or false * * Returns +true+ if _obj_ responds to the given * method. Private methods are included in the search only if the * optional second parameter evaluates to +true+. * * If the method is not implemented, * as Process.fork on Windows, File.lchmod on GNU/Linux, etc., * false is returned. * * If the method is not defined, respond_to_missing? * method is called and the result is returned. */ static mrb_value obj_respond_to(mrb_state *mrb, mrb_value self) { mrb_value mid; mrb_sym id, rtm_id; mrb_bool priv = FALSE, respond_to_p = TRUE; mrb_get_args(mrb, "o|b", &mid, &priv); if (mrb_symbol_p(mid)) { id = mrb_symbol(mid); } else { mrb_value tmp; if (!mrb_string_p(mid)) { tmp = mrb_check_string_type(mrb, mid); if (mrb_nil_p(tmp)) { tmp = mrb_inspect(mrb, mid); mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); } } tmp = mrb_check_intern_str(mrb, mid); if (mrb_nil_p(tmp)) { respond_to_p = FALSE; } else { id = mrb_symbol(tmp); } } if (respond_to_p) { respond_to_p = basic_obj_respond_to(mrb, self, id, !priv); } if (!respond_to_p) { rtm_id = mrb_intern_lit(mrb, "respond_to_missing?"); if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) { mrb_value args[2]; args[0] = mid; args[1] = mrb_bool_value(priv); return mrb_funcall_argv(mrb, self, rtm_id, 2, args); } } return mrb_bool_value(respond_to_p); } /* 15.3.1.3.45 */ /* * call-seq: * obj.singleton_methods(all=true) -> array * * Returns an array of the names of singleton methods for obj. * If the optional all parameter is true, the list will include * methods in modules included in obj. * Only public and protected singleton methods are returned. * * module Other * def three() end * end * * class Single * def Single.four() end * end * * a = Single.new * * def a.one() * end * * class << a * include Other * def two() * end * end * * Single.singleton_methods #=> [:four] * a.singleton_methods(false) #=> [:two, :one] * a.singleton_methods #=> [:two, :one, :three] */ static mrb_value mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_singleton_methods(mrb, recur, self); } static mrb_value mod_define_singleton_method(mrb_state *mrb, mrb_value self) { struct RProc *p; mrb_sym mid; mrb_value blk = mrb_nil_value(); mrb_get_args(mrb, "n&", &mid, &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, p); return mrb_symbol_value(mid); } static mrb_value mrb_obj_ceqq(mrb_state *mrb, mrb_value self) { mrb_value v; mrb_int i, len; mrb_sym eqq = mrb_intern_lit(mrb, "==="); mrb_value ary = mrb_ary_splat(mrb, self); mrb_get_args(mrb, "o", &v); len = RARRAY_LEN(ary); for (i=0; ic->ci[-1].proc; if (MRB_PROC_CFUNC_P(proc)) { return mrb_ary_new(mrb); } irep = proc->body.irep; if (!irep->lv) { return mrb_ary_new(mrb); } ret = mrb_ary_new_capa(mrb, irep->nlocals - 1); for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name) { mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name)); } } if (proc->env) { struct REnv *e = proc->env; while (e) { if (!MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) { irep = mrb->c->cibase[e->cioff].proc->body.irep; if (irep->lv) { for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name) { mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name)); } } } } e = (struct REnv*)e->c; } } return ret; } void mrb_init_kernel(mrb_state *mrb) { struct RClass *krn; mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */ mrb_define_class_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */ mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */ ; /* 15.3.1.2.11 */ mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE()); mrb_define_method(mrb, krn, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ mrb_define_method(mrb, krn, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */ mrb_define_method(mrb, krn, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */ mrb_define_method(mrb, krn, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */ mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */ mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */ mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */ mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */ mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */ mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */ mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */ mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */ mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */ mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */ mrb_define_method(mrb, krn, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */ mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */ mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */ mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */ mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */ mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */ mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */ mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */ mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */ mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */ mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */ mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */ mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */ mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */ mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */ mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */ mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */ mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */ mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY()); mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */ mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */ mrb_include_module(mrb, mrb->object_class, mrb->kernel_module); mrb_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone")); } mruby-1.2.0+20160315+git4f20d58a/src/load.c000066400000000000000000000444301267140355100172650ustar00rootroot00000000000000/* ** load.c - mruby binary loader ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #if SIZE_MAX < UINT32_MAX # error size_t must be at least 32 bits wide #endif #define FLAG_BYTEORDER_BIG 2 #define FLAG_BYTEORDER_LIL 4 #define FLAG_BYTEORDER_NATIVE 8 #define FLAG_SRC_MALLOC 1 #define FLAG_SRC_STATIC 0 #define SIZE_ERROR_MUL(nmemb, size) ((nmemb) > SIZE_MAX / (size)) static size_t skip_padding(const uint8_t *buf) { const size_t align = MRB_DUMP_ALIGNMENT; return -(intptr_t)buf & (align-1); } static size_t offset_crc_body(void) { struct rite_binary_header header; return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc); } static mrb_irep* read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) { size_t i; const uint8_t *src = bin; ptrdiff_t diff; uint16_t tt, pool_data_len, snl; size_t plen; int ai = mrb_gc_arena_save(mrb); mrb_irep *irep = mrb_add_irep(mrb); /* skip record size */ src += sizeof(uint32_t); /* number of local variable */ irep->nlocals = bin_to_uint16(src); src += sizeof(uint16_t); /* number of register variable */ irep->nregs = bin_to_uint16(src); src += sizeof(uint16_t); /* number of child irep */ irep->rlen = (size_t)bin_to_uint16(src); src += sizeof(uint16_t); /* Binary Data Section */ /* ISEQ BLOCK */ irep->ilen = (size_t)bin_to_uint32(src); src += sizeof(uint32_t); src += skip_padding(src); if (irep->ilen > 0) { if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { return NULL; } if ((flags & FLAG_SRC_MALLOC) == 0 && (flags & FLAG_BYTEORDER_NATIVE)) { irep->iseq = (mrb_code*)src; src += sizeof(uint32_t) * irep->ilen; irep->flags |= MRB_ISEQ_NO_FREE; } else { irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen); if (flags & FLAG_BYTEORDER_NATIVE) { memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen); src += sizeof(uint32_t) * irep->ilen; } else if (flags & FLAG_BYTEORDER_BIG) { for (i = 0; i < irep->ilen; i++) { irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */ src += sizeof(uint32_t); } } else { for (i = 0; i < irep->ilen; i++) { irep->iseq[i] = (mrb_code)bin_to_uint32l(src); /* iseq */ src += sizeof(uint32_t); } } } } /* POOL BLOCK */ plen = (size_t)bin_to_uint32(src); /* number of pool */ src += sizeof(uint32_t); if (plen > 0) { if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { return NULL; } irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); for (i = 0; i < plen; i++) { mrb_value s; tt = *src++; /* pool TT */ pool_data_len = bin_to_uint16(src); /* pool data length */ src += sizeof(uint16_t); if (flags & FLAG_SRC_MALLOC) { s = mrb_str_new(mrb, (char *)src, pool_data_len); } else { s = mrb_str_new_static(mrb, (char *)src, pool_data_len); } src += pool_data_len; switch (tt) { /* pool data */ case IREP_TT_FIXNUM: irep->pool[i] = mrb_str_to_inum(mrb, s, 10, FALSE); break; case IREP_TT_FLOAT: irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE)); break; case IREP_TT_STRING: irep->pool[i] = mrb_str_pool(mrb, s); break; default: /* should not happen */ irep->pool[i] = mrb_nil_value(); break; } irep->plen++; mrb_gc_arena_restore(mrb, ai); } } /* SYMS BLOCK */ irep->slen = (size_t)bin_to_uint32(src); /* syms length */ src += sizeof(uint32_t); if (irep->slen > 0) { if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { return NULL; } irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); for (i = 0; i < irep->slen; i++) { snl = bin_to_uint16(src); /* symbol name length */ src += sizeof(uint16_t); if (snl == MRB_DUMP_NULL_SYM_LEN) { irep->syms[i] = 0; continue; } if (flags & FLAG_SRC_MALLOC) { irep->syms[i] = mrb_intern(mrb, (char *)src, snl); } else { irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl); } src += snl + 1; mrb_gc_arena_restore(mrb, ai); } } irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*irep->rlen); diff = src - bin; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *len = (size_t)diff; return irep; } static mrb_irep* read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) { mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags); size_t i; if (irep == NULL) { return NULL; } bin += *len; for (i=0; irlen; i++) { size_t rlen; irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags); if (irep->reps[i] == NULL) { return NULL; } bin += rlen; *len += rlen; } return irep; } static mrb_irep* read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) { size_t len; bin += sizeof(struct rite_section_irep_header); return read_irep_record(mrb, bin, &len, flags); } static int read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len) { size_t i, fname_len, niseq; char *fname; uint16_t *lines; *len = 0; bin += sizeof(uint32_t); /* record size */ *len += sizeof(uint32_t); fname_len = bin_to_uint16(bin); bin += sizeof(uint16_t); *len += sizeof(uint16_t); fname = (char *)mrb_malloc(mrb, fname_len + 1); memcpy(fname, bin, fname_len); fname[fname_len] = '\0'; bin += fname_len; *len += fname_len; niseq = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); /* niseq */ *len += sizeof(uint32_t); if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) { return MRB_DUMP_GENERAL_FAILURE; } lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t)); for (i = 0; i < niseq; i++) { lines[i] = bin_to_uint16(bin); bin += sizeof(uint16_t); /* niseq */ *len += sizeof(uint16_t); } irep->filename = fname; irep->lines = lines; return MRB_DUMP_OK; } static int read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp) { int result = read_lineno_record_1(mrb, bin, irep, lenp); size_t i; if (result != MRB_DUMP_OK) return result; for (i = 0; i < irep->rlen; i++) { size_t len; result = read_lineno_record(mrb, bin, irep->reps[i], &len); if (result != MRB_DUMP_OK) break; bin += len; *lenp += len; } return result; } static int read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep) { size_t len; len = 0; bin += sizeof(struct rite_section_lineno_header); /* Read Binary Data Section */ return read_lineno_record(mrb, bin, irep, &len); } static int read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) { const uint8_t *bin = start; ptrdiff_t diff; size_t record_size, i; uint16_t f_idx; if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; } irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info)); irep->debug_info->pc_count = irep->ilen; record_size = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); irep->debug_info->flen = bin_to_uint16(bin); irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_malloc(mrb, sizeof(mrb_irep_debug_info*) * irep->debug_info->flen); bin += sizeof(uint16_t); for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { mrb_irep_debug_info_file *file; uint16_t filename_idx; mrb_int len; file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file)); irep->debug_info->files[f_idx] = file; file->start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); /* filename */ filename_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); mrb_assert(filename_idx < filenames_len); file->filename_sym = filenames[filename_idx]; len = 0; file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len); file->line_entry_count = bin_to_uint32(bin); bin += sizeof(uint32_t); file->line_type = (mrb_debug_line_type)bin_to_uint8(bin); bin += sizeof(uint8_t); switch (file->line_type) { case mrb_debug_line_ary: { uint32_t l; file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count)); for (l = 0; l < file->line_entry_count; ++l) { file->lines.ary[l] = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; case mrb_debug_line_flat_map: { uint32_t l; file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc( mrb, sizeof(mrb_irep_debug_info_line) * (size_t)(file->line_entry_count)); for (l = 0; l < file->line_entry_count; ++l) { file->lines.flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); file->lines.flat_map[l].line = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; default: return MRB_DUMP_GENERAL_FAILURE; } } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if (record_size != (size_t)diff) { return MRB_DUMP_GENERAL_FAILURE; } for (i = 0; i < irep->rlen; i++) { size_t len; int ret; ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; struct rite_section_debug_header *header; uint16_t i; size_t len = 0; int result; uint16_t filenames_len; mrb_sym *filenames; bin = start; header = (struct rite_section_debug_header *)bin; bin += sizeof(struct rite_section_debug_header); filenames_len = bin_to_uint16(bin); bin += sizeof(uint16_t); filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len); for (i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); if (flags & FLAG_SRC_MALLOC) { filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len); } else { filenames[i] = mrb_intern_static(mrb, (const char *)bin, (size_t)f_len); } bin += f_len; } result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len); if (result != MRB_DUMP_OK) goto debug_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } debug_exit: mrb_free(mrb, filenames); return result; } static int read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len) { const uint8_t *bin = start; size_t i; ptrdiff_t diff; irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1)); for (i = 0; i + 1< irep->nlocals; ++i) { uint16_t const sym_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); if (sym_idx == RITE_LV_NULL_MARK) { irep->lv[i].name = 0; irep->lv[i].r = 0; } else { if (sym_idx >= syms_len) { return MRB_DUMP_GENERAL_FAILURE; } irep->lv[i].name = syms[sym_idx]; irep->lv[i].r = bin_to_uint16(bin); } bin += sizeof(uint16_t); } for (i = 0; i < irep->rlen; ++i) { size_t len; int ret; ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; struct rite_section_lv_header const *header; uint32_t i; size_t len = 0; int result; uint32_t syms_len; mrb_sym *syms; mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static; bin = start; header = (struct rite_section_lv_header const*)bin; bin += sizeof(struct rite_section_lv_header); syms_len = bin_to_uint32(bin); bin += sizeof(uint32_t); syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len); for (i = 0; i < syms_len; ++i) { uint16_t const str_len = bin_to_uint16(bin); bin += sizeof(uint16_t); syms[i] = intern_func(mrb, (const char*)bin, str_len); bin += str_len; } result = read_lv_record(mrb, bin, irep, &len, syms, syms_len); if (result != MRB_DUMP_OK) goto lv_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } lv_exit: mrb_free(mrb, syms); return result; } static int read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags) { const struct rite_binary_header *header = (const struct rite_binary_header *)bin; if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_NATIVE; else *flags |= FLAG_BYTEORDER_BIG; } else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_LIL; else *flags |= FLAG_BYTEORDER_NATIVE; } else { return MRB_DUMP_INVALID_FILE_HEADER; } if (crc) { *crc = bin_to_uint16(header->binary_crc); } *bin_size = (size_t)bin_to_uint32(header->binary_size); return MRB_DUMP_OK; } MRB_API mrb_irep* read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) { int result; mrb_irep *irep = NULL; const struct rite_section_header *section_header; uint16_t crc; size_t bin_size = 0; size_t n; if ((mrb == NULL) || (bin == NULL)) { return NULL; } result = read_binary_header(bin, &bin_size, &crc, &flags); if (result != MRB_DUMP_OK) { return NULL; } n = offset_crc_body(); if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) { return NULL; } bin += sizeof(struct rite_binary_header); do { section_header = (const struct rite_section_header *)bin; if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { irep = read_section_irep(mrb, bin, flags); if (!irep) return NULL; } else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_lineno(mrb, bin, irep); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_debug(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; result = read_section_lv(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } bin += bin_to_uint32(section_header->section_size); } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); return irep; } MRB_API mrb_irep* mrb_read_irep(mrb_state *mrb, const uint8_t *bin) { #ifdef MRB_USE_ETEXT_EDATA uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; #else uint8_t flags = FLAG_SRC_STATIC; #endif return read_irep(mrb, bin, flags); } void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void irep_error(mrb_state *mrb) { mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) { mrb_irep *irep = mrb_read_irep(mrb, bin); struct RProc *proc; if (!irep) { irep_error(mrb); return mrb_nil_value(); } proc = mrb_proc_new(mrb, irep); mrb_irep_decref(mrb, irep); if (c && c->no_exec) return mrb_obj_value(proc); return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); } MRB_API mrb_value mrb_load_irep(mrb_state *mrb, const uint8_t *bin) { return mrb_load_irep_cxt(mrb, bin, NULL); } #ifndef MRB_DISABLE_STDIO MRB_API mrb_irep* mrb_read_irep_file(mrb_state *mrb, FILE* fp) { mrb_irep *irep = NULL; uint8_t *buf; const size_t header_size = sizeof(struct rite_binary_header); size_t buf_size = 0; uint8_t flags = 0; int result; if ((mrb == NULL) || (fp == NULL)) { return NULL; } buf = (uint8_t*)mrb_malloc(mrb, header_size); if (fread(buf, header_size, 1, fp) == 0) { goto irep_exit; } result = read_binary_header(buf, &buf_size, NULL, &flags); if (result != MRB_DUMP_OK || buf_size <= header_size) { goto irep_exit; } buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size); if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { goto irep_exit; } irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); irep_exit: mrb_free(mrb, buf); return irep; } void mrb_codedump_all(mrb_state*, struct RProc*); MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) { mrb_irep *irep = mrb_read_irep_file(mrb, fp); mrb_value val; struct RProc *proc; if (!irep) { irep_error(mrb); return mrb_nil_value(); } proc = mrb_proc_new(mrb, irep); mrb_irep_decref(mrb, irep); if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); val = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); return val; } MRB_API mrb_value mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } #endif /* MRB_DISABLE_STDIO */ mruby-1.2.0+20160315+git4f20d58a/src/mruby_core.rake000066400000000000000000000012411267140355100212050ustar00rootroot00000000000000MRuby.each_target do current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd) relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) current_build_dir = "#{build_dir}/#{relative_from_root}" objs = Dir.glob("#{current_dir}/*.c").map { |f| next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/ objfile(f.pathmap("#{current_build_dir}/%n")) }.compact if cxx_abi_enabled? objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } end self.libmruby << objs file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| archiver.run t.name, t.prerequisites end end mruby-1.2.0+20160315+git4f20d58a/src/numeric.c000066400000000000000000000677411267140355100200220ustar00rootroot00000000000000/* ** numeric.c - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #ifdef MRB_USE_FLOAT #define floor(f) floorf(f) #define ceil(f) ceilf(f) #define fmod(x,y) fmodf(x,y) #define MRB_FLO_TO_STR_FMT "%.7g" #else #define MRB_FLO_TO_STR_FMT "%.14g" #endif MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value val) { switch (mrb_type(val)) { case MRB_TT_FIXNUM: return (mrb_float)mrb_fixnum(val); case MRB_TT_FLOAT: break; default: mrb_raise(mrb, E_TYPE_ERROR, "non float value"); } return mrb_float(val); } /* * call-seq: * * num ** other -> num * * Raises num the other power. * * 2.0**3 #=> 8.0 */ static mrb_value num_pow(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_float d, yv; mrb_get_args(mrb, "o", &y); yv = mrb_to_flo(mrb, y); d = pow(mrb_to_flo(mrb, x), yv); if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 && (d < 0 || (d > 0 && (mrb_int)d > 0))) return mrb_fixnum_value((mrb_int)d); return mrb_float_value(mrb, d); } /* 15.2.8.3.4 */ /* 15.2.9.3.4 */ /* * call-seq: * num / other -> num * * Performs division: the class of the resulting object depends on * the class of num and on the magnitude of the * result. */ mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y)); } /* 15.2.9.3.19(x) */ /* * call-seq: * num.quo(numeric) -> real * * Returns most exact division. */ static mrb_value num_div(mrb_state *mrb, mrb_value x) { mrb_float y; mrb_get_args(mrb, "f", &y); return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y); } /******************************************************************** * * Document-class: Float * * Float objects represent inexact real numbers using * the native architecture's double-precision floating point * representation. */ /* 15.2.9.3.16(x) */ /* * call-seq: * flt.to_s -> string * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return * "NaN", "Infinity", and * "-Infinity". */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { if (isnan(mrb_float(flt))) { return mrb_str_new_lit(mrb, "NaN"); } return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT); } /* 15.2.9.3.2 */ /* * call-seq: * float - other -> float * * Returns a new float which is the difference of float * and other. */ static mrb_value flo_minus(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y)); } /* 15.2.9.3.3 */ /* * call-seq: * float * other -> float * * Returns a new float which is the product of float * and other. */ static mrb_value flo_mul(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y)); } static void flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp) { mrb_float div; mrb_float mod; if (y == 0.0) { div = INFINITY; mod = NAN; } else { mod = fmod(x, y); if (isinf(x) && isfinite(y)) div = x; else div = (x - mod) / y; if (y*mod < 0) { mod += y; div -= 1.0; } } if (modp) *modp = mod; if (divp) *divp = div; } /* 15.2.9.3.5 */ /* * call-seq: * flt % other -> float * flt.modulo(other) -> float * * Return the modulo after division of flt by other. * * 6543.21.modulo(137) #=> 104.21 * 6543.21.modulo(137.24) #=> 92.9299999999996 */ static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_float mod; mrb_get_args(mrb, "o", &y); flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } /* 15.2.8.3.16 */ /* * call-seq: * num.eql?(numeric) -> true or false * * Returns true if num and numeric are the * same type and have equal values. * * 1 == 1.0 #=> true * 1.eql?(1.0) #=> false * (1.0).eql?(1.0) #=> true */ static mrb_value fix_eql(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (!mrb_fixnum_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); } static mrb_value flo_eql(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (!mrb_float_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); } /* 15.2.9.3.7 */ /* * call-seq: * flt == obj -> true or false * * Returns true only if obj has the same value * as flt. Contrast this with Float#eql?, which * requires obj to be a Float. * * 1.0 == 1 #=> true * */ static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); case MRB_TT_FLOAT: return mrb_bool_value(mrb_float(x) == mrb_float(y)); default: return mrb_false_value(); } } /* 15.2.8.3.18 */ /* * call-seq: * flt.hash -> integer * * Returns a hash code for this float. */ static mrb_value flo_hash(mrb_state *mrb, mrb_value num) { mrb_float d; char *c; size_t i; int hash; d = (mrb_float)mrb_fixnum(num); /* normalize -0.0 to 0.0 */ if (d == 0) d = 0.0; c = (char*)&d; for (hash=0, i=0; i self * * As flt is already a float, returns +self+. */ static mrb_value flo_to_f(mrb_state *mrb, mrb_value num) { return num; } /* 15.2.9.3.11 */ /* * call-seq: * flt.infinite? -> nil, -1, +1 * * Returns nil, -1, or +1 depending on whether flt * is finite, -infinity, or +infinity. * * (0.0).infinite? #=> nil * (-1.0/0.0).infinite? #=> -1 * (+1.0/0.0).infinite? #=> 1 */ static mrb_value flo_infinite_p(mrb_state *mrb, mrb_value num) { mrb_float value = mrb_float(num); if (isinf(value)) { return mrb_fixnum_value(value < 0 ? -1 : 1); } return mrb_nil_value(); } /* 15.2.9.3.9 */ /* * call-seq: * flt.finite? -> true or false * * Returns true if flt is a valid IEEE floating * point number (it is not infinite, and nan? is * false). * */ static mrb_value flo_finite_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isfinite(mrb_float(num))); } /* 15.2.9.3.10 */ /* * call-seq: * flt.floor -> integer * * Returns the largest integer less than or equal to flt. * * 1.2.floor #=> 1 * 2.0.floor #=> 2 * (-1.2).floor #=> -2 * (-2.0).floor #=> -2 */ static mrb_value flo_floor(mrb_state *mrb, mrb_value num) { mrb_float f = floor(mrb_float(num)); if (!FIXABLE(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } /* 15.2.9.3.8 */ /* * call-seq: * flt.ceil -> integer * * Returns the smallest Integer greater than or equal to * flt. * * 1.2.ceil #=> 2 * 2.0.ceil #=> 2 * (-1.2).ceil #=> -1 * (-2.0).ceil #=> -2 */ static mrb_value flo_ceil(mrb_state *mrb, mrb_value num) { mrb_float f = ceil(mrb_float(num)); if (!FIXABLE(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } /* 15.2.9.3.12 */ /* * call-seq: * flt.round([ndigits]) -> integer or float * * Rounds flt to a given precision in decimal digits (default 0 digits). * Precision may be negative. Returns a floating point number when ndigits * is more than zero. * * 1.4.round #=> 1 * 1.5.round #=> 2 * 1.6.round #=> 2 * (-1.5).round #=> -2 * * 1.234567.round(2) #=> 1.23 * 1.234567.round(3) #=> 1.235 * 1.234567.round(4) #=> 1.2346 * 1.234567.round(5) #=> 1.23457 * * 34567.89.round(-5) #=> 0 * 34567.89.round(-4) #=> 30000 * 34567.89.round(-3) #=> 35000 * 34567.89.round(-2) #=> 34600 * 34567.89.round(-1) #=> 34570 * 34567.89.round(0) #=> 34568 * 34567.89.round(1) #=> 34567.9 * 34567.89.round(2) #=> 34567.89 * 34567.89.round(3) #=> 34567.89 * */ static mrb_value flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); if (isinf(number)) { if (0 < ndigits) return num; else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity"); } if (isnan(number)) { if (0 < ndigits) return num; else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } f = 1.0; i = ndigits >= 0 ? ndigits : -ndigits; while (--i >= 0) f = f*10.0; if (isinf(f)) { if (ndigits < 0) number = 0; } else { double d; if (ndigits < 0) number /= f; else number *= f; /* home-made inline implementation of round(3) */ if (number > 0.0) { d = floor(number); number = d + (number - d >= 0.5); } else if (number < 0.0) { d = ceil(number); number = d - (d - number >= 0.5); } if (ndigits < 0) number *= f; else number /= f; } if (ndigits > 0) { if (!isfinite(number)) return num; return mrb_float_value(mrb, number); } return mrb_fixnum_value((mrb_int)number); } /* 15.2.9.3.14 */ /* 15.2.9.3.15 */ /* * call-seq: * flt.to_i -> integer * flt.to_int -> integer * flt.truncate -> integer * * Returns flt truncated to an Integer. */ static mrb_value flo_truncate(mrb_state *mrb, mrb_value num) { mrb_float f = mrb_float(num); if (f > 0.0) f = floor(f); if (f < 0.0) f = ceil(f); if (!FIXABLE(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } static mrb_value flo_nan_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isnan(mrb_float(num))); } /* * Document-class: Integer * * Integer is the basis for the two concrete classes that * hold whole numbers, Bignum and Fixnum. * */ /* * call-seq: * int.to_i -> integer * int.to_int -> integer * * As int is already an Integer, all these * methods simply return the receiver. */ static mrb_value int_to_i(mrb_state *mrb, mrb_value num) { return num; } /*tests if N*N would overflow*/ #define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) #define FIT_SQRT_INT(n) (((n)=-SQRT_INT_MAX)) mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_float c; mrb_int b; if (a == 0) return x; b = mrb_fixnum(y); if (FIT_SQRT_INT(a) && FIT_SQRT_INT(b)) return mrb_fixnum_value(a*b); c = a * b; if ((a != 0 && c/a != b) || !FIXABLE(c)) { return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b); } return mrb_fixnum_value((mrb_int)c); } return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); } /* 15.2.8.3.3 */ /* * call-seq: * fix * numeric -> numeric_result * * Performs multiplication: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_mul(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_fixnum_mul(mrb, x, y); } static void fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp) { mrb_int div, mod; /* TODO: add mrb_assert(y != 0) to make sure */ if (y < 0) { if (x < 0) div = -x / -y; else div = - (x / -y); } else { if (x < 0) div = - (-x / y); else div = x / y; } mod = x - div*y; if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { mod += y; div -= 1; } if (divp) *divp = div; if (modp) *modp = mod; } /* 15.2.8.3.5 */ /* * call-seq: * fix % other -> real * fix.modulo(other) -> real * * Returns fix modulo other. * See numeric.divmod for more information. */ static mrb_value fix_mod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_int a; mrb_get_args(mrb, "o", &y); a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, mod; if ((b=mrb_fixnum(y)) == 0) { return mrb_float_value(mrb, NAN); } fixdivmod(mrb, a, b, 0, &mod); return mrb_fixnum_value(mod); } else { mrb_float mod; flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } } /* * call-seq: * fix.divmod(numeric) -> array * * See Numeric#divmod. */ static mrb_value fix_divmod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (mrb_fixnum_p(y)) { mrb_int div, mod; if (mrb_fixnum(y) == 0) { return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY), mrb_float_value(mrb, NAN)); } fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); } else { mrb_float div, mod; mrb_value a, b; flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod); a = mrb_float_value(mrb, (mrb_int)div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } } static mrb_value flo_divmod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_float div, mod; mrb_value a, b; mrb_get_args(mrb, "o", &y); flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod); a = mrb_float_value(mrb, (mrb_int)div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } /* 15.2.8.3.7 */ /* * call-seq: * fix == other -> true or false * * Return true if fix equals other * numerically. * * 1 == 2 #=> false * 1 == 1.0 #=> true */ static mrb_value fix_equal(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); case MRB_TT_FLOAT: return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y)); default: return mrb_false_value(); } } /* 15.2.8.3.8 */ /* * call-seq: * ~fix -> integer * * One's complement: returns a number where each bit is flipped. * ex.0---00001 (1)-> 1---11110 (-2) * ex.0---00010 (2)-> 1---11101 (-3) * ex.0---00100 (4)-> 1---11011 (-5) */ static mrb_value fix_rev(mrb_state *mrb, mrb_value num) { mrb_int val = mrb_fixnum(num); return mrb_fixnum_value(~val); } static mrb_value bit_coerce(mrb_state *mrb, mrb_value x) { while (!mrb_fixnum_p(x)) { if (mrb_float_p(x)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert Float into Integer"); } x = mrb_to_int(mrb, x); } return x; } /* 15.2.8.3.9 */ /* * call-seq: * fix & integer -> integer_result * * Bitwise AND. */ static mrb_value fix_and(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); return mrb_fixnum_value(mrb_fixnum(x) & mrb_fixnum(y)); } /* 15.2.8.3.10 */ /* * call-seq: * fix | integer -> integer_result * * Bitwise OR. */ static mrb_value fix_or(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); return mrb_fixnum_value(mrb_fixnum(x) | mrb_fixnum(y)); } /* 15.2.8.3.11 */ /* * call-seq: * fix ^ integer -> integer_result * * Bitwise EXCLUSIVE OR. */ static mrb_value fix_xor(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); return mrb_fixnum_value(mrb_fixnum(x) ^ mrb_fixnum(y)); } #define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { mrb_assert(width > 0); if (val > 0) { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val > (MRB_INT_MAX >> width))) { goto bit_overflow; } } else { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val < (MRB_INT_MIN >> width))) { goto bit_overflow; } } return mrb_fixnum_value(val << width); bit_overflow: { mrb_float f = (mrb_float)val; while (width--) { f *= 2; } return mrb_float_value(mrb, f); } } static mrb_value rshift(mrb_int val, mrb_int width) { mrb_assert(width > 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); } return mrb_fixnum_value(0); } return mrb_fixnum_value(val >> width); } static inline void fix_shift_get_width(mrb_state *mrb, mrb_int *width) { mrb_value y; mrb_get_args(mrb, "o", &y); *width = mrb_fixnum(bit_coerce(mrb, y)); } /* 15.2.8.3.12 */ /* * call-seq: * fix << count -> integer or float * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ static mrb_value fix_lshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; fix_shift_get_width(mrb, &width); if (width == 0) { return x; } val = mrb_fixnum(x); if (width < 0) { return rshift(val, -width); } return lshift(mrb, val, width); } /* 15.2.8.3.13 */ /* * call-seq: * fix >> count -> integer or float * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ static mrb_value fix_rshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; fix_shift_get_width(mrb, &width); if (width == 0) { return x; } val = mrb_fixnum(x); if (width < 0) { return lshift(mrb, val, -width); } return rshift(val, width); } /* 15.2.8.3.23 */ /* * call-seq: * fix.to_f -> float * * Converts fix to a Float. * */ static mrb_value fix_to_f(mrb_state *mrb, mrb_value num) { return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num)); } /* * Document-class: FloatDomainError * * Raised when attempting to convert special float values * (in particular infinite or NaN) * to numerical classes which don't support them. * * Float::INFINITY.to_r * * raises the exception: * * FloatDomainError: Infinity */ /* ------------------------------------------------------------------------*/ MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) { mrb_int z; if (!mrb_float_p(x)) { mrb_raise(mrb, E_TYPE_ERROR, "non float value"); z = 0; /* not reached. just suppress warnings. */ } else { mrb_float d = mrb_float(x); if (isinf(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity"); } if (isnan(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } if (FIXABLE(d)) { z = (mrb_int)d; } else { mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); } } return mrb_fixnum_value(z); } mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, c; if (a == 0) return y; b = mrb_fixnum(y); if (mrb_int_add_overflow(a, b, &c)) { return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b); } return mrb_fixnum_value(c); } return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y)); } /* 15.2.8.3.1 */ /* * call-seq: * fix + numeric -> numeric_result * * Performs addition: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_plus(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "o", &other); return mrb_fixnum_plus(mrb, self, other); } mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, c; b = mrb_fixnum(y); if (mrb_int_sub_overflow(a, b, &c)) { return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b); } return mrb_fixnum_value(c); } return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y)); } /* 15.2.8.3.2 */ /* 15.2.8.3.16 */ /* * call-seq: * fix - numeric -> numeric_result * * Performs subtraction: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_minus(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "o", &other); return mrb_fixnum_minus(mrb, self, other); } MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base) { char buf[MRB_INT_BIT+1]; char *b = buf + sizeof buf; mrb_int val = mrb_fixnum(x); if (base < 2 || 36 < base) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); } if (val == 0) { *--b = '0'; } else if (val < 0) { do { *--b = mrb_digitmap[-(val % base)]; } while (val /= base); *--b = '-'; } else { do { *--b = mrb_digitmap[(int)(val % base)]; } while (val /= base); } return mrb_str_new(mrb, b, buf + sizeof(buf) - b); } /* 15.2.8.3.25 */ /* * call-seq: * fix.to_s(base=10) -> string * * Returns a string containing the representation of fix radix * base (between 2 and 36). * * 12345.to_s #=> "12345" * 12345.to_s(2) #=> "11000000111001" * 12345.to_s(8) #=> "30071" * 12345.to_s(10) #=> "12345" * 12345.to_s(16) #=> "3039" * 12345.to_s(36) #=> "9ix" * */ static mrb_value fix_to_s(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); return mrb_fixnum_to_str(mrb, self, base); } /* 15.2.9.3.6 */ /* * call-seq: * self.f <=> other.f => -1, 0, +1 * < => -1 * = => 0 * > => +1 * Comparison---Returns -1, 0, or +1 depending on whether fix is * less than, equal to, or greater than numeric. This is the * basis for the tests in Comparable. */ static mrb_value num_cmp(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_float x, y; mrb_get_args(mrb, "o", &other); x = mrb_to_flo(mrb, self); switch (mrb_type(other)) { case MRB_TT_FIXNUM: y = (mrb_float)mrb_fixnum(other); break; case MRB_TT_FLOAT: y = mrb_float(other); break; default: return mrb_nil_value(); } if (x > y) return mrb_fixnum_value(1); else { if (x < y) return mrb_fixnum_value(-1); return mrb_fixnum_value(0); } } /* 15.2.9.3.1 */ /* * call-seq: * float + other -> float * * Returns a new float which is the sum of float * and other. */ static mrb_value flo_plus(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y)); } /* ------------------------------------------------------------------------*/ void mrb_init_numeric(mrb_state *mrb) { struct RClass *numeric, *integer, *fixnum, *fl; /* Numeric Class */ numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */ mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */ mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ /* Integer Class */ integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ mrb_undef_class_method(mrb, integer, "new"); mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); /* Fixnum Class */ mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer); mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */ mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ /* Float Class */ mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ mrb_undef_class_method(mrb, fl, "new"); mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE()); mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); #ifdef INFINITY mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY)); #endif #ifdef NAN mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN)); #endif } mruby-1.2.0+20160315+git4f20d58a/src/object.c000066400000000000000000000355731267140355100176240ustar00rootroot00000000000000/* ** object.c - Object, NilClass, TrueClass, FalseClass class ** ** See Copyright Notice in mruby.h */ #include #include #include #include MRB_API mrb_bool mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) { if (mrb_type(v1) != mrb_type(v2)) return FALSE; switch (mrb_type(v1)) { case MRB_TT_TRUE: return TRUE; case MRB_TT_FALSE: case MRB_TT_FIXNUM: return (mrb_fixnum(v1) == mrb_fixnum(v2)); case MRB_TT_SYMBOL: return (mrb_symbol(v1) == mrb_symbol(v2)); case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); default: return (mrb_ptr(v1) == mrb_ptr(v2)); } } MRB_API mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2) { /* temporary definition */ return mrb_obj_eq(mrb, v1, v2); } MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { mrb_value result; if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; result = mrb_funcall(mrb, obj1, "==", 1, obj2); if (mrb_test(result)) return TRUE; return FALSE; } /* * Document-class: NilClass * * The class of the singleton object nil. */ /* 15.2.4.3.4 */ /* * call_seq: * nil.nil? -> true * * Only the object nil responds true to nil?. */ static mrb_value mrb_true(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* 15.2.4.3.5 */ /* * call-seq: * nil.to_s -> "" * * Always returns the empty string. */ static mrb_value nil_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new(mrb, 0, 0); } static mrb_value nil_inspect(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "nil"); } /*********************************************************************** * Document-class: TrueClass * * The global value true is the only instance of class * TrueClass and represents a logically true value in * boolean expressions. The class provides operators allowing * true to be used in logical expressions. */ /* 15.2.5.3.1 */ /* * call-seq: * true & obj -> true or false * * And---Returns false if obj is * nil or false, true otherwise. */ static mrb_value true_and(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.5.3.2 */ /* * call-seq: * true ^ obj -> !obj * * Exclusive Or---Returns true if obj is * nil or false, false * otherwise. */ static mrb_value true_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(!obj2); } /* 15.2.5.3.3 */ /* * call-seq: * true.to_s -> "true" * * The string representation of true is "true". */ static mrb_value true_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "true"); } /* 15.2.5.3.4 */ /* * call-seq: * true | obj -> true * * Or---Returns true. As anObject is an argument to * a method call, it is always evaluated; there is no short-circuit * evaluation in this case. * * true | puts("or") * true || puts("logical or") * * produces: * * or */ static mrb_value true_or(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* * Document-class: FalseClass * * The global value false is the only instance of class * FalseClass and represents a logically false value in * boolean expressions. The class provides operators allowing * false to participate correctly in logical expressions. * */ /* 15.2.4.3.1 */ /* 15.2.6.3.1 */ /* * call-seq: * false & obj -> false * nil & obj -> false * * And---Returns false. obj is always * evaluated as it is the argument to a method call---there is no * short-circuit evaluation in this case. */ static mrb_value false_and(mrb_state *mrb, mrb_value obj) { return mrb_false_value(); } /* 15.2.4.3.2 */ /* 15.2.6.3.2 */ /* * call-seq: * false ^ obj -> true or false * nil ^ obj -> true or false * * Exclusive Or---If obj is nil or * false, returns false; otherwise, returns * true. * */ static mrb_value false_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.4.3.3 */ /* 15.2.6.3.4 */ /* * call-seq: * false | obj -> true or false * nil | obj -> true or false * * Or---Returns false if obj is * nil or false; true otherwise. */ static mrb_value false_or(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.6.3.3 */ /* * call-seq: * false.to_s -> "false" * * 'nuf said... */ static mrb_value false_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "false"); } void mrb_init_object(mrb_state *mrb) { struct RClass *n; struct RClass *t; struct RClass *f; mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); mrb_undef_class_method(mrb, n, "new"); mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ mrb_define_method(mrb, n, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.4.3.3 */ mrb_define_method(mrb, n, "nil?", mrb_true, MRB_ARGS_NONE()); /* 15.2.4.3.4 */ mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */ mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE()); mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class); mrb_undef_class_method(mrb, t, "new"); mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */ mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */ mrb_define_method(mrb, t, "to_s", true_to_s, MRB_ARGS_NONE()); /* 15.2.5.3.3 */ mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */ mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); mrb_undef_class_method(mrb, f, "new"); mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */ mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */ mrb_define_method(mrb, f, "to_s", false_to_s, MRB_ARGS_NONE()); /* 15.2.6.3.3 */ mrb_define_method(mrb, f, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.6.3.4 */ mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE()); } static mrb_value inspect_type(mrb_state *mrb, mrb_value val) { if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) { return mrb_inspect(mrb, val); } else { return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val)); } } static mrb_value convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise) { mrb_sym m = 0; m = mrb_intern_cstr(mrb, method); if (!mrb_respond_to(mrb, val, m)) { if (raise) { mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname)); } return mrb_nil_value(); } return mrb_funcall_argv(mrb, val, m, 0, 0); } MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method) { mrb_value v; if (mrb_fixnum_p(val)) return val; v = convert_type(mrb, val, "Integer", method, FALSE); if (mrb_nil_p(v) || !mrb_fixnum_p(v)) { return mrb_nil_value(); } return v; } MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) { mrb_value v; if (mrb_type(val) == type) return val; v = convert_type(mrb, val, tname, method, TRUE); if (mrb_type(v) != type) { mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val, mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method)); } return v; } MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) { mrb_value v; if (mrb_type(val) == type && type != MRB_TT_DATA) return val; v = convert_type(mrb, val, tname, method, FALSE); if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value(); return v; } static const struct types { unsigned char type; const char *name; } builtin_types[] = { /* {MRB_TT_NIL, "nil"}, */ {MRB_TT_FALSE, "false"}, {MRB_TT_TRUE, "true"}, {MRB_TT_FIXNUM, "Fixnum"}, {MRB_TT_SYMBOL, "Symbol"}, /* :symbol */ {MRB_TT_MODULE, "Module"}, {MRB_TT_OBJECT, "Object"}, {MRB_TT_CLASS, "Class"}, {MRB_TT_ICLASS, "iClass"}, /* internal use: mixed-in module holder */ {MRB_TT_SCLASS, "SClass"}, {MRB_TT_PROC, "Proc"}, {MRB_TT_FLOAT, "Float"}, {MRB_TT_ARRAY, "Array"}, {MRB_TT_HASH, "Hash"}, {MRB_TT_STRING, "String"}, {MRB_TT_RANGE, "Range"}, /* {MRB_TT_BIGNUM, "Bignum"}, */ {MRB_TT_FILE, "File"}, {MRB_TT_DATA, "Data"}, /* internal use: wrapped C pointers */ /* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */ /* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */ /* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */ {-1, 0} }; MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; enum mrb_vtype xt; xt = mrb_type(x); if ((xt != t) || (xt == MRB_TT_DATA)) { while (type->type < MRB_TT_MAXDEFINE) { if (type->type == t) { const char *etype; if (mrb_nil_p(x)) { etype = "nil"; } else if (mrb_fixnum_p(x)) { etype = "Fixnum"; } else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } else if (mrb_immediate_p(x)) { etype = RSTRING_PTR(mrb_obj_as_string(mrb, x)); } else { etype = mrb_obj_classname(mrb, x); } mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name)); } type++; } mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)", mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x))); } } /* 15.3.1.3.46 */ /* * call-seq: * obj.to_s => string * * Returns a string representing obj. The default * to_s prints the object's class and an encoding of the * object id. As a special case, the top-level object that is the * initial execution context of Ruby programs returns "main." */ MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_buf_new(mrb, 20); const char *cname = mrb_obj_classname(mrb, obj); mrb_str_cat_lit(mrb, str, "#<"); mrb_str_cat_cstr(mrb, str, cname); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(obj))); mrb_str_cat_lit(mrb, str, ">"); return str; } /* * call-seq: * obj.is_a?(class) => true or false * obj.kind_of?(class) => true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) { struct RClass *cl = mrb_class(mrb, obj); switch (c->tt) { case MRB_TT_MODULE: case MRB_TT_CLASS: case MRB_TT_ICLASS: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; cl = cl->super; } return FALSE; } static mrb_value mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method) { mrb_value v; if (mrb_fixnum_p(val)) return val; v = convert_type(mrb, val, "Integer", method, TRUE); if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) { mrb_value type = inspect_type(mrb, val); mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)", type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v)); } return v; } MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val) { return mrb_to_integer(mrb, val, "to_int"); } MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) { mrb_value tmp; if (mrb_nil_p(val)) { if (base != 0) goto arg_error; mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer"); } switch (mrb_type(val)) { case MRB_TT_FLOAT: if (base != 0) goto arg_error; if (FIXABLE(mrb_float(val))) { break; } return mrb_flo_to_fixnum(mrb, val); case MRB_TT_FIXNUM: if (base != 0) goto arg_error; return val; case MRB_TT_STRING: string_conv: return mrb_str_to_inum(mrb, val, base, TRUE); default: break; } if (base != 0) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { goto string_conv; } arg_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value"); } tmp = convert_type(mrb, val, "Integer", "to_int", FALSE); if (mrb_nil_p(tmp)) { return mrb_to_integer(mrb, val, "to_i"); } return tmp; } MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val) { return mrb_convert_to_integer(mrb, val, 0); } MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val) { if (mrb_nil_p(val)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float"); } switch (mrb_type(val)) { case MRB_TT_FIXNUM: return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val)); case MRB_TT_FLOAT: return val; case MRB_TT_STRING: return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE)); default: return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f"); } } MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj) { return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0)); } MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2)); } mruby-1.2.0+20160315+git4f20d58a/src/opcode.h000066400000000000000000000001101267140355100176070ustar00rootroot00000000000000/* this header file is to be removed soon. */ #include mruby-1.2.0+20160315+git4f20d58a/src/pool.c000066400000000000000000000072171267140355100173210ustar00rootroot00000000000000/* ** pool.c - memory pool ** ** See Copyright Notice in mruby.h */ #include #include #include #include /* configuration section */ /* allocated memory address should be multiple of POOL_ALIGNMENT */ /* or undef it if alignment does not matter */ #ifndef POOL_ALIGNMENT #define POOL_ALIGNMENT 4 #endif /* page size of memory pool */ #ifndef POOL_PAGE_SIZE #define POOL_PAGE_SIZE 16000 #endif /* end of configuration section */ struct mrb_pool_page { struct mrb_pool_page *next; size_t offset; size_t len; void *last; char page[]; }; struct mrb_pool { mrb_state *mrb; struct mrb_pool_page *pages; }; #undef TEST_POOL #ifdef TEST_POOL #define mrb_malloc_simple(m,s) malloc(s) #define mrb_free(m,p) free(p) #endif #ifdef POOL_ALIGNMENT # define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1)) #else # define ALIGN_PADDING(x) (0) #endif MRB_API mrb_pool* mrb_pool_open(mrb_state *mrb) { mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool)); if (pool) { pool->mrb = mrb; pool->pages = NULL; } return pool; } MRB_API void mrb_pool_close(mrb_pool *pool) { struct mrb_pool_page *page, *tmp; if (!pool) return; page = pool->pages; while (page) { tmp = page; page = page->next; mrb_free(pool->mrb, tmp); } mrb_free(pool->mrb, pool); } static struct mrb_pool_page* page_alloc(mrb_pool *pool, size_t len) { struct mrb_pool_page *page; if (len < POOL_PAGE_SIZE) len = POOL_PAGE_SIZE; page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len); if (page) { page->offset = 0; page->len = len; } return page; } MRB_API void* mrb_pool_alloc(mrb_pool *pool, size_t len) { struct mrb_pool_page *page; size_t n; if (!pool) return NULL; len += ALIGN_PADDING(len); page = pool->pages; while (page) { if (page->offset + len <= page->len) { n = page->offset; page->offset += len; page->last = (char*)page->page+n; return page->last; } page = page->next; } page = page_alloc(pool, len); if (!page) return NULL; page->offset = len; page->next = pool->pages; pool->pages = page; page->last = (void*)page->page; return page->last; } MRB_API mrb_bool mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len) { struct mrb_pool_page *page; if (!pool) return FALSE; len += ALIGN_PADDING(len); page = pool->pages; while (page) { if (page->last == p) { size_t beg; beg = (char*)p - page->page; if (beg + len > page->len) return FALSE; return TRUE; } page = page->next; } return FALSE; } MRB_API void* mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen) { struct mrb_pool_page *page; void *np; if (!pool) return NULL; oldlen += ALIGN_PADDING(oldlen); newlen += ALIGN_PADDING(newlen); page = pool->pages; while (page) { if (page->last == p) { size_t beg; beg = (char*)p - page->page; if (beg + oldlen != page->offset) break; if (beg + newlen > page->len) { page->offset = beg; break; } page->offset = beg + newlen; return p; } page = page->next; } np = mrb_pool_alloc(pool, newlen); if (np == NULL) { return NULL; } memcpy(np, p, oldlen); return np; } #ifdef TEST_POOL int main(void) { int i, len = 250; mrb_pool *pool; void *p; pool = mrb_pool_open(NULL); p = mrb_pool_alloc(pool, len); for (i=1; i<20; i++) { printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2)); p = mrb_pool_realloc(pool, p, len, len*2); len *= 2; } mrb_pool_close(pool); return 0; } #endif mruby-1.2.0+20160315+git4f20d58a/src/print.c000066400000000000000000000017171267140355100175030ustar00rootroot00000000000000/* ** print.c - Kernel.#p ** ** See Copyright Notice in mruby.h */ #include #include #include #ifndef MRB_DISABLE_STDIO static void printstr(mrb_value obj, FILE *stream) { if (mrb_string_p(obj)) { fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); putc('\n', stream); } } #else # define printstr(obj, stream) (void)0 #endif MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { printstr(mrb_inspect(mrb, obj), stdout); } MRB_API void mrb_print_error(mrb_state *mrb) { mrb_print_backtrace(mrb); printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr); } MRB_API void mrb_show_version(mrb_state *mrb) { printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout); } MRB_API void mrb_show_copyright(mrb_state *mrb) { printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout); } mruby-1.2.0+20160315+git4f20d58a/src/proc.c000066400000000000000000000150711267140355100173100ustar00rootroot00000000000000/* ** proc.c - Proc class ** ** See Copyright Notice in mruby.h */ #include #include #include #include static mrb_code call_iseq[] = { MKOP_A(OP_CALL, 0), }; struct RProc * mrb_proc_new(mrb_state *mrb, mrb_irep *irep) { struct RProc *p; mrb_callinfo *ci = mrb->c->ci; p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); p->target_class = 0; if (ci) { if (ci->proc) p->target_class = ci->proc->target_class; if (!p->target_class) p->target_class = ci->target_class; } p->body.irep = irep; p->env = 0; mrb_irep_incref(mrb, irep); return p; } static struct REnv* env_new(mrb_state *mrb, int nlocals) { struct REnv *e; e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); MRB_SET_ENV_STACK_LEN(e, nlocals); e->mid = mrb->c->ci->mid; e->cioff = mrb->c->ci - mrb->c->cibase; e->stack = mrb->c->stack; return e; } static void closure_setup(mrb_state *mrb, struct RProc *p, int nlocals) { struct REnv *e; if (!mrb->c->ci->env) { e = env_new(mrb, nlocals); mrb->c->ci->env = e; } else { e = mrb->c->ci->env; } p->env = e; } struct RProc * mrb_closure_new(mrb_state *mrb, mrb_irep *irep) { struct RProc *p = mrb_proc_new(mrb, irep); closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals); return p; } MRB_API struct RProc * mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) { struct RProc *p; p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); p->body.func = func; p->flags |= MRB_PROC_CFUNC; p->env = 0; return p; } MRB_API struct RProc * mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_new_cfunc(mrb, func); struct REnv *e; int i; p->env = e = env_new(mrb, argc); mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env); MRB_ENV_UNSHARE_STACK(e); e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); if (argv) { for (i = 0; i < argc; ++i) { e->stack[i] = argv[i]; } } else { for (i = 0; i < argc; ++i) { SET_NIL_VALUE(e->stack[i]); } } return p; } MRB_API struct RProc * mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) { return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL); } MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx) { struct RProc *p = mrb->c->ci->proc; struct REnv *e = p->env; if (!MRB_PROC_CFUNC_P(p)) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); } if (!e) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv."); } if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) { mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)", mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e))); } return e->stack[idx]; } MRB_API void mrb_proc_copy(struct RProc *a, struct RProc *b) { a->flags = b->flags; a->body = b->body; if (!MRB_PROC_CFUNC_P(a)) { a->body.irep->refcnt++; } a->target_class = b->target_class; a->env = b->env; } static mrb_value mrb_proc_initialize(mrb_state *mrb, mrb_value self) { mrb_value blk; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { /* Calling Proc.new without a block is not implemented yet */ mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } else { mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk)); } return self; } static mrb_value mrb_proc_init_copy(mrb_state *mrb, mrb_value self) { mrb_value proc; mrb_get_args(mrb, "o", &proc); if (mrb_type(proc) != MRB_TT_PROC) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc)); return self; } int mrb_proc_cfunc_p(struct RProc *p) { return MRB_PROC_CFUNC_P(p); } mrb_value mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self) { return (p->body.func)(mrb, self); } mrb_code* mrb_proc_iseq(mrb_state *mrb, struct RProc *p) { return p->body.irep->iseq; } /* 15.2.17.4.2 */ static mrb_value mrb_proc_arity(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_code *iseq = mrb_proc_iseq(mrb, p); mrb_aspec aspec; int ma, op, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { /* TODO cfunc aspec not implemented yet */ return mrb_fixnum_value(-1); } /* arity is depend on OP_ENTER */ if (GET_OPCODE(*iseq) != OP_ENTER) { return mrb_fixnum_value(0); } aspec = GETARG_Ax(*iseq); ma = MRB_ASPEC_REQ(aspec); op = MRB_ASPEC_OPT(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; return mrb_fixnum_value(arity); } /* 15.3.1.2.6 */ /* 15.3.1.3.27 */ /* * call-seq: * lambda { |...| block } -> a_proc * * Equivalent to Proc.new, except the resulting Proc objects * check the number of parameters passed when called. */ static mrb_value proc_lambda(mrb_state *mrb, mrb_value self) { mrb_value blk; struct RProc *p; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } if (mrb_type(blk) != MRB_TT_PROC) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } p = mrb_proc_ptr(blk); if (!MRB_PROC_STRICT_P(p)) { struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c); mrb_proc_copy(p2, p); p2->flags |= MRB_PROC_STRICT; return mrb_obj_value(p2); } return blk; } void mrb_init_proc(mrb_state *mrb) { struct RProc *m; mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); static const mrb_irep mrb_irep_zero = { 0 }; *call_irep = mrb_irep_zero; call_irep->flags = MRB_ISEQ_NO_FREE; call_irep->iseq = call_iseq; call_irep->ilen = 1; mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE()); m = mrb_proc_new(mrb, call_irep); mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m); mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m); mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */ mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */ } mruby-1.2.0+20160315+git4f20d58a/src/range.c000066400000000000000000000262271267140355100174460ustar00rootroot00000000000000/* ** range.c - Range class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #define RANGE_CLASS (mrb_class_get(mrb, "Range")) static void range_check(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value ans; enum mrb_vtype ta; enum mrb_vtype tb; ta = mrb_type(a); tb = mrb_type(b); if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) && (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) { return; } ans = mrb_funcall(mrb, a, "<=>", 1, b); if (mrb_nil_p(ans)) { /* can not be compared */ mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range"); } } MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) { struct RRange *r; range_check(mrb, beg, end); r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS); r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); r->edges->beg = beg; r->edges->end = end; r->excl = excl; return mrb_range_value(r); } /* * call-seq: * rng.first => obj * rng.begin => obj * * Returns the first object in rng. */ mrb_value mrb_range_beg(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(range); return r->edges->beg; } /* * call-seq: * rng.end => obj * rng.last => obj * * Returns the object that defines the end of rng. * * (1..10).end #=> 10 * (1...10).end #=> 10 */ mrb_value mrb_range_end(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(range); return r->edges->end; } /* * call-seq: * range.exclude_end? => true or false * * Returns true if range excludes its end value. */ mrb_value mrb_range_excl(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(range); return mrb_bool_value(r->excl); } static void range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end) { struct RRange *r = mrb_range_ptr(range); range_check(mrb, beg, end); r->excl = exclude_end; if (!r->edges) { r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); } r->edges->beg = beg; r->edges->end = end; } /* * call-seq: * Range.new(start, end, exclusive=false) => range * * Constructs a range using the given start and end. If the third * parameter is omitted or is false, the range will include * the end object; otherwise, it will be excluded. */ mrb_value mrb_range_initialize(mrb_state *mrb, mrb_value range) { mrb_value beg, end; mrb_bool exclusive; int n; n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); if (n != 3) { exclusive = FALSE; } /* Ranges are immutable, so that they should be initialized only once. */ range_init(mrb, range, beg, end, exclusive); return range; } /* * call-seq: * range == obj => true or false * * Returns true only if * 1) obj is a Range, * 2) obj has equivalent beginning and end items (by comparing them with ==), * 3) obj has the same #exclude_end? setting as rng. * * (0..2) == (0..2) #=> true * (0..2) == Range.new(0,2) #=> true * (0..2) == (0...2) #=> false * */ mrb_value mrb_range_eq(mrb_state *mrb, mrb_value range) { struct RRange *rr; struct RRange *ro; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */ return mrb_false_value(); } rr = mrb_range_ptr(range); ro = mrb_range_ptr(obj); if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) || !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) || rr->excl != ro->excl) { return mrb_false_value(); } return mrb_true_value(); } 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_gt(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; } static mrb_bool r_ge(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; } /* * call-seq: * range === obj => true or false * range.member?(val) => true or false * range.include?(val) => true or false * */ mrb_value mrb_range_include(mrb_state *mrb, mrb_value range) { mrb_value val; struct RRange *r = mrb_range_ptr(range); mrb_value beg, end; mrb_bool include_p; mrb_get_args(mrb, "o", &val); beg = r->edges->beg; end = r->edges->end; include_p = r_le(mrb, beg, val) && /* beg <= val */ ((r->excl && r_gt(mrb, end, val)) || /* end > val */ (r_ge(mrb, end, val))); /* end >= val */ return mrb_bool_value(include_p); } static mrb_bool range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) { mrb_int beg, end; struct RRange *r = mrb_range_ptr(range); if (mrb_type(range) != MRB_TT_RANGE) return FALSE; beg = mrb_int(mrb, r->edges->beg); end = mrb_int(mrb, r->edges->end); if (beg < 0) { beg += len; if (beg < 0) return FALSE; } if (trunc) { if (beg > len) return FALSE; if (end > len) end = len; } if (end < 0) end += len; if (!r->excl && (!trunc || end < len)) end++; /* include end point */ len = end - beg; if (len < 0) len = 0; *begp = beg; *lenp = len; return TRUE; } MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len) { return range_beg_len(mrb, range, begp, lenp, len, TRUE); } /* 15.2.14.4.12(x) */ /* * call-seq: * rng.to_s -> string * * Convert this range object to a printable form. */ static mrb_value range_to_s(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(range); str = mrb_obj_as_string(mrb, r->edges->beg); str2 = mrb_obj_as_string(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); mrb_str_cat_str(mrb, str, str2); return str; } /* 15.2.14.4.13(x) */ /* * call-seq: * rng.inspect -> string * * Convert this range object to a printable form (using * inspect to convert the start and end * objects). */ static mrb_value range_inspect(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(range); str = mrb_inspect(mrb, r->edges->beg); str2 = mrb_inspect(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); mrb_str_cat_str(mrb, str, str2); return str; } /* 15.2.14.4.14(x) */ /* * call-seq: * rng.eql?(obj) -> true or false * * Returns true only if obj is a Range, has equivalent * beginning and end items (by comparing them with #eql?), and has the same * #exclude_end? setting as rng. * * (0..2).eql?(0..2) #=> true * (0..2).eql?(Range.new(0,2)) #=> true * (0..2).eql?(0...2) #=> false * */ static mrb_value range_eql(mrb_state *mrb, mrb_value range) { mrb_value obj; struct RRange *r, *o; mrb_get_args(mrb, "o", &obj); if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) { return mrb_false_value(); } if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value(); r = mrb_range_ptr(range); o = mrb_range_ptr(obj); if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) || !mrb_eql(mrb, r->edges->end, o->edges->end) || (r->excl != o->excl)) { return mrb_false_value(); } return mrb_true_value(); } /* 15.2.14.4.15(x) */ static mrb_value range_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value src; struct RRange *r; 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"); } r = mrb_range_ptr(src); range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl); return copy; } mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)) { mrb_int i, j, beg, len; mrb_value result; result = mrb_ary_new(mrb); for (i = 0; i < argc; ++i) { if (mrb_fixnum_p(argv[i])) { mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i]))); } else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) { mrb_int const end = olen < beg + len ? olen : beg + len; for (j = beg; j < end; ++j) { mrb_ary_push(mrb, result, func(mrb, obj, j)); } for (; j < beg + len; ++j) { mrb_ary_push(mrb, result, mrb_nil_value()); } } else { mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]); } } return result; } void mrb_init_range(mrb_state *mrb) { struct RClass *r; r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */ MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE); mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */ mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */ mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */ mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */ mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */ mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */ mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */ mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */ mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */ mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */ mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */ mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */ mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */ mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */ } mruby-1.2.0+20160315+git4f20d58a/src/state.c000066400000000000000000000143241267140355100174650ustar00rootroot00000000000000/* ** state.c - mrb_state open/close functions ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include void mrb_init_core(mrb_state*); void mrb_init_mrbgems(mrb_state*); void mrb_gc_init(mrb_state*, mrb_gc *gc); void mrb_gc_destroy(mrb_state*, mrb_gc *gc); static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) { return mrb_str_new_lit(mrb, "main"); } MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud) { static const mrb_state mrb_state_zero = { 0 }; static const struct mrb_context mrb_context_zero = { 0 }; mrb_state *mrb; mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); if (mrb == NULL) return NULL; *mrb = mrb_state_zero; mrb->allocf_ud = ud; mrb->allocf = f; mrb->atexit_stack_len = 0; mrb_gc_init(mrb, &mrb->gc); mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *mrb->c = mrb_context_zero; mrb->root_c = mrb->c; mrb_init_core(mrb); return mrb; } void* mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) { if (size == 0) { free(p); return NULL; } else { return realloc(p, size); } } struct alloca_header { struct alloca_header *next; char buf[]; }; MRB_API void* mrb_alloca(mrb_state *mrb, size_t size) { struct alloca_header *p; p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size); p->next = mrb->mems; mrb->mems = p; return (void*)p->buf; } static void mrb_alloca_free(mrb_state *mrb) { struct alloca_header *p; struct alloca_header *tmp; if (mrb == NULL) return; p = mrb->mems; while (p) { tmp = p; p = p->next; mrb_free(mrb, tmp); } } MRB_API mrb_state* mrb_open(void) { mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL); return mrb; } MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud) { mrb_state *mrb = mrb_open_core(f, ud); if (mrb == NULL) { return NULL; } #ifndef DISABLE_GEMS mrb_init_mrbgems(mrb); mrb_gc_arena_restore(mrb, 0); #endif return mrb; } void mrb_free_symtbl(mrb_state *mrb); void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) { irep->refcnt++; } void mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) { irep->refcnt--; if (irep->refcnt == 0) { mrb_irep_free(mrb, irep); } } void mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { size_t i; if (!(irep->flags & MRB_ISEQ_NO_FREE)) mrb_free(mrb, irep->iseq); for (i=0; iplen; i++) { if (mrb_type(irep->pool[i]) == MRB_TT_STRING) { mrb_gc_free_str(mrb, RSTRING(irep->pool[i])); mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); } #ifdef MRB_WORD_BOXING else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) { mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); } #endif } mrb_free(mrb, irep->pool); mrb_free(mrb, irep->syms); for (i=0; irlen; i++) { mrb_irep_decref(mrb, irep->reps[i]); } mrb_free(mrb, irep->reps); mrb_free(mrb, irep->lv); mrb_free(mrb, (void *)irep->filename); mrb_free(mrb, irep->lines); mrb_debug_info_free(mrb, irep->debug_info); mrb_free(mrb, irep); } mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); struct RString *ns; char *ptr; mrb_int len; ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString)); ns->tt = MRB_TT_STRING; ns->c = mrb->string_class; if (RSTR_NOFREE_P(s)) { ns->flags = MRB_STR_NOFREE; ns->as.heap.ptr = s->as.heap.ptr; ns->as.heap.len = s->as.heap.len; ns->as.heap.aux.capa = 0; } else { ns->flags = 0; if (RSTR_EMBED_P(s)) { ptr = s->as.ary; len = RSTR_EMBED_LEN(s); } else { ptr = s->as.heap.ptr; len = s->as.heap.len; } if (len < RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(ns); RSTR_SET_EMBED_LEN(ns, len); if (ptr) { memcpy(ns->as.ary, ptr, len); } ns->as.ary[len] = '\0'; } else { ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); ns->as.heap.len = len; ns->as.heap.aux.capa = len; if (ptr) { memcpy(ns->as.heap.ptr, ptr, len); } ns->as.heap.ptr[len] = '\0'; } } return mrb_obj_value(ns); } void mrb_free_backtrace(mrb_state *mrb); MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { if (!c) return; mrb_free(mrb, c->stbase); mrb_free(mrb, c->cibase); mrb_free(mrb, c->rescue); mrb_free(mrb, c->ensure); mrb_free(mrb, c); } MRB_API void mrb_close(mrb_state *mrb) { if (!mrb) return; if (mrb->atexit_stack_len > 0) { mrb_int i; for (i = mrb->atexit_stack_len; i > 0; --i) { mrb->atexit_stack[i - 1](mrb); } #ifndef MRB_FIXED_STATE_ATEXIT_STACK mrb_free(mrb, mrb->atexit_stack); #endif } /* free */ mrb_gc_free_gv(mrb); mrb_free_backtrace(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_alloca_free(mrb); mrb_gc_destroy(mrb, &mrb->gc); mrb_free(mrb, mrb); } MRB_API mrb_irep* mrb_add_irep(mrb_state *mrb) { static const mrb_irep mrb_irep_zero = { 0 }; mrb_irep *irep; irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); *irep = mrb_irep_zero; irep->refcnt = 1; return irep; } MRB_API mrb_value mrb_top_self(mrb_state *mrb) { if (!mrb->top_self) { mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class); mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE()); } return mrb_obj_value(mrb->top_self); } MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) { #ifdef MRB_FIXED_STATE_ATEXIT_STACK if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) { mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit"); } #else size_t stack_size; stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); if (mrb->atexit_stack_len == 0) { mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); } else { mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); } #endif mrb->atexit_stack[mrb->atexit_stack_len++] = f; } mruby-1.2.0+20160315+git4f20d58a/src/string.c000066400000000000000000002066421267140355100176610ustar00rootroot00000000000000/* ** string.c - String class ** ** See Copyright Notice in mruby.h */ #ifdef _MSC_VER # define _CRT_NONSTDC_NO_DEPRECATE #endif #include #include #include #include #include #include #include #include #include #include #include typedef struct mrb_shared_string { mrb_bool nofree : 1; int refcnt; char *ptr; mrb_int len; } mrb_shared_string; const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) static struct RString* str_new_static(mrb_state *mrb, const char *p, size_t len) { struct RString *s; if (len >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } s = mrb_obj_alloc_string(mrb); s->as.heap.len = len; s->as.heap.aux.capa = 0; /* nofree */ s->as.heap.ptr = (char *)p; s->flags = MRB_STR_NOFREE; return s; } static struct RString* str_new(mrb_state *mrb, const char *p, size_t len) { struct RString *s; if (p && mrb_ro_data_p(p)) { return str_new_static(mrb, p, len); } s = mrb_obj_alloc_string(mrb); if (len < RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(s); RSTR_SET_EMBED_LEN(s, len); if (p) { memcpy(s->as.ary, p, len); } } else { if (len >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } s->as.heap.len = len; s->as.heap.aux.capa = len; s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1); if (p) { memcpy(s->as.heap.ptr, p, len); } } RSTR_PTR(s)[len] = '\0'; return s; } static inline void str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj) { s->c = mrb_str_ptr(obj)->c; } static mrb_value mrb_str_new_empty(mrb_state *mrb, mrb_value str) { struct RString *s = str_new(mrb, 0, 0); str_with_class(mrb, s, str); return mrb_obj_value(s); } #ifndef MRB_STR_BUF_MIN_SIZE # define MRB_STR_BUF_MIN_SIZE 128 #endif MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa) { struct RString *s; s = mrb_obj_alloc_string(mrb); if (capa >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big"); } if (capa < MRB_STR_BUF_MIN_SIZE) { capa = MRB_STR_BUF_MIN_SIZE; } s->as.heap.len = 0; s->as.heap.aux.capa = capa; s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); RSTR_PTR(s)[0] = '\0'; return mrb_obj_value(s); } static inline void resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) { if (RSTR_EMBED_P(s)) { if (RSTRING_EMBED_LEN_MAX < capacity) { char *const tmp = (char *)mrb_malloc(mrb, capacity+1); const mrb_int len = RSTR_EMBED_LEN(s); memcpy(tmp, s->as.ary, len); RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; s->as.heap.aux.capa = capacity; } } else { s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); s->as.heap.aux.capa = capacity; } } static void str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) { size_t capa; size_t total; ptrdiff_t off = -1; if (len == 0) return; mrb_str_modify(mrb, s); if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { off = ptr - RSTR_PTR(s); } if (RSTR_EMBED_P(s)) capa = RSTRING_EMBED_LEN_MAX; else capa = s->as.heap.aux.capa; if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } total = RSTR_LEN(s)+len; if (capa <= total) { while (total > capa) { if (capa + 1 >= MRB_INT_MAX / 2) { capa = (total + 4095) / 4096; break; } capa = (capa + 1) * 2; } resize_capa(mrb, s, capa); } if (off != -1) { ptr = RSTR_PTR(s) + off; } memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); RSTR_SET_LEN(s, total); RSTR_PTR(s)[total] = '\0'; /* sentinel */ } MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len) { return mrb_obj_value(str_new(mrb, p, len)); } /* * call-seq: (Caution! NULL string) * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { struct RString *s; size_t len; if (p) { len = strlen(p); } else { len = 0; } s = str_new(mrb, p, len); return mrb_obj_value(s); } MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) { struct RString *s = str_new_static(mrb, p, len); return mrb_obj_value(s); } static void str_decref(mrb_state *mrb, mrb_shared_string *shared) { shared->refcnt--; if (shared->refcnt == 0) { if (!shared->nofree) { mrb_free(mrb, shared->ptr); } mrb_free(mrb, shared); } } void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { if (RSTR_EMBED_P(str)) /* no code */; else if (RSTR_SHARED_P(str)) str_decref(mrb, str->as.heap.aux.shared); else if (!RSTR_NOFREE_P(str)) mrb_free(mrb, str->as.heap.ptr); } #ifdef MRB_UTF8_STRING static const char utf8len_codepage[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, 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, 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,1,1,1,1,1,1,1,1,1,1,1, }; static mrb_int utf8len(const char* p, const char* e) { mrb_int len; mrb_int i; len = utf8len_codepage[(unsigned char)*p]; if (p + len > e) return 1; for (i = 1; i < len; ++i) if ((p[i] & 0xc0) != 0x80) return 1; return len; } static mrb_int utf8_strlen(mrb_value str, mrb_int len) { mrb_int total = 0; char* p = RSTRING_PTR(str); char* e = p; if (RSTRING(str)->flags & MRB_STR_NO_UTF) { return RSTRING_LEN(str); } e += len < 0 ? RSTRING_LEN(str) : len; while (pflags |= MRB_STR_NO_UTF; } return total; } #define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) /* map character index to byte offset index */ static mrb_int chars2bytes(mrb_value s, mrb_int off, mrb_int idx) { mrb_int i, b, n; const char *p = RSTRING_PTR(s) + off; const char *e = RSTRING_END(s); for (b=i=0; p n) return -1; else if (m == n) { return memcmp(x0, y0, m) == 0 ? 0 : -1; } else if (m < 1) { return 0; } else if (m == 1) { const unsigned char *ys = memchr(y, *x, n); if (ys) return ys - y; else return -1; } return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } static void str_make_shared(mrb_state *mrb, struct RString *s) { if (!RSTR_SHARED_P(s)) { mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 1; if (RSTR_EMBED_P(s)) { const mrb_int len = RSTR_EMBED_LEN(s); char *const tmp = (char *)mrb_malloc(mrb, len+1); memcpy(tmp, s->as.ary, len); tmp[len] = '\0'; RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; shared->nofree = FALSE; shared->ptr = s->as.heap.ptr; } else if (RSTR_NOFREE_P(s)) { shared->nofree = TRUE; shared->ptr = s->as.heap.ptr; RSTR_UNSET_NOFREE_FLAG(s); } else { shared->nofree = FALSE; if (s->as.heap.aux.capa > s->as.heap.len) { s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1); } else { shared->ptr = s->as.heap.ptr; } } shared->len = s->as.heap.len; s->as.heap.aux.shared = shared; RSTR_SET_SHARED_FLAG(s); } } static mrb_value byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { struct RString *orig, *s; mrb_shared_string *shared; orig = mrb_str_ptr(str); if (RSTR_EMBED_P(orig)) { s = str_new(mrb, orig->as.ary+beg, len); } else { str_make_shared(mrb, orig); shared = orig->as.heap.aux.shared; s = mrb_obj_alloc_string(mrb); s->as.heap.ptr = orig->as.heap.ptr + beg; s->as.heap.len = len; s->as.heap.aux.shared = shared; RSTR_SET_SHARED_FLAG(s); shared->refcnt++; } return mrb_obj_value(s); } #ifdef MRB_UTF8_STRING static inline mrb_value str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { beg = chars2bytes(str, 0, beg); len = chars2bytes(str, beg, len); return byte_subseq(mrb, str, beg, len); } #else #define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len) #endif static mrb_value str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { mrb_int clen = RSTRING_CHAR_LEN(str); if (len < 0) return mrb_nil_value(); if (clen == 0) { len = 0; } else if (beg < 0) { beg = clen + beg; } if (beg > clen) return mrb_nil_value(); if (beg < 0) { beg += clen; if (beg < 0) return mrb_nil_value(); } if (beg + len > clen) len = clen - beg; if (len <= 0) { len = 0; } return str_subseq(mrb, str, beg, len); } static mrb_int str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) { mrb_int pos; char *s, *sptr; mrb_int len, slen; len = RSTRING_LEN(str); slen = RSTRING_LEN(sub); if (offset < 0) { offset += len; if (offset < 0) return -1; } if (len - offset < slen) return -1; s = RSTRING_PTR(str); if (offset) { s += offset; } if (slen == 0) return offset; /* need proceed one character at a time */ sptr = RSTRING_PTR(sub); slen = RSTRING_LEN(sub); len = RSTRING_LEN(str) - offset; pos = mrb_memsearch(sptr, slen, s, len); if (pos < 0) return pos; return pos + offset; } static void check_frozen(mrb_state *mrb, struct RString *s) { if (RSTR_FROZEN_P(s)) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string"); } } static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { long len; check_frozen(mrb, s1); len = RSTR_LEN(s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); } else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) { mrb_free(mrb, s1->as.heap.ptr); } RSTR_UNSET_NOFREE_FLAG(s1); if (RSTR_SHARED_P(s2)) { L_SHARE: RSTR_UNSET_EMBED_FLAG(s1); s1->as.heap.ptr = s2->as.heap.ptr; s1->as.heap.len = len; s1->as.heap.aux.shared = s2->as.heap.aux.shared; RSTR_SET_SHARED_FLAG(s1); s1->as.heap.aux.shared->refcnt++; } else { if (len <= RSTRING_EMBED_LEN_MAX) { RSTR_UNSET_SHARED_FLAG(s1); RSTR_SET_EMBED_FLAG(s1); memcpy(s1->as.ary, RSTR_PTR(s2), len); RSTR_SET_EMBED_LEN(s1, len); } else { str_make_shared(mrb, s2); goto L_SHARE; } } return mrb_obj_value(s1); } static mrb_int str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { char *s, *sbeg, *t; struct RString *ps = mrb_str_ptr(str); mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ if (RSTR_LEN(ps) < len) return -1; if (RSTR_LEN(ps) - pos < len) { pos = RSTR_LEN(ps) - len; } sbeg = RSTR_PTR(ps); s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { return s - RSTR_PTR(ps); } s--; } return -1; } else { return pos; } } MRB_API mrb_int mrb_str_strlen(mrb_state *mrb, struct RString *s) { mrb_int i, max = RSTR_LEN(s); char *p = RSTR_PTR(s); if (!p) return 0; for (i=0; i char* mrb_utf8_from_locale(const char *str, size_t len) { wchar_t* wcsp; char* mbsp; size_t mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = strlen(str); wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) return NULL; wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } char* mrb_locale_from_utf8(const char *utf8, size_t len) { wchar_t* wcsp; char* mbsp; size_t mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = strlen(utf8); wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) return NULL; wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } #endif MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s) { check_frozen(mrb, s); s->flags &= ~MRB_STR_NO_UTF; if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { s->as.heap.ptr = shared->ptr; s->as.heap.aux.capa = shared->len; RSTR_PTR(s)[s->as.heap.len] = '\0'; mrb_free(mrb, shared); } else { char *ptr, *p; mrb_int len; p = RSTR_PTR(s); len = s->as.heap.len; ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); if (p) { memcpy(ptr, p, len); } ptr[len] = '\0'; s->as.heap.ptr = ptr; s->as.heap.aux.capa = len; str_decref(mrb, shared); } RSTR_UNSET_SHARED_FLAG(s); return; } if (RSTR_NOFREE_P(s)) { char *p = s->as.heap.ptr; s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); if (p) { memcpy(RSTR_PTR(s), p, s->as.heap.len); } RSTR_PTR(s)[s->as.heap.len] = '\0'; s->as.heap.aux.capa = s->as.heap.len; RSTR_UNSET_NOFREE_FLAG(s); return; } } static mrb_value mrb_str_freeze(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); RSTR_SET_FROZEN_FLAG(s); return str; } MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { mrb_int slen; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; /* sentinel */ } return str; } MRB_API char* mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) { struct RString *s; if (!mrb_string_p(str0)) { mrb_raise(mrb, E_TYPE_ERROR, "expected String"); } s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } return RSTR_PTR(s); } /* * call-seq: (Caution! String("abcd") change) * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RString *s1 = mrb_str_ptr(self), *s2; mrb_int len; mrb_str_modify(mrb, s1); if (!mrb_string_p(other)) { other = mrb_str_to_str(mrb, other); } s2 = mrb_str_ptr(other); len = RSTR_LEN(s1) + RSTR_LEN(s2); if (RSTRING_CAPA(self) < len) { resize_capa(mrb, s1, len); } memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2)); RSTR_SET_LEN(s1, len); RSTR_PTR(s1)[len] = '\0'; } /* * call-seq: (Caution! String("abcd") remain) * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { struct RString *s = mrb_str_ptr(a); struct RString *s2 = mrb_str_ptr(b); struct RString *t; t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2)); memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s)); memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2)); return mrb_obj_value(t); } /* 15.2.10.5.2 */ /* * call-seq: (Caution! String("abcd") remain) for stack_argument * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_plus_m(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "S", &str); return mrb_str_plus(mrb, self, str); } /* 15.2.10.5.26 */ /* 15.2.10.5.33 */ /* * call-seq: * "abcd".size => int * * Returns the length of string. */ static mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { mrb_int len = RSTRING_CHAR_LEN(self); return mrb_fixnum_value(len); } static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { mrb_int len = RSTRING_LEN(self); return mrb_fixnum_value(len); } /* 15.2.10.5.1 */ /* * call-seq: * str * integer => new_str * * Copy---Returns a new String containing integer copies of * the receiver. * * "Ho! " * 3 #=> "Ho! Ho! Ho! " */ static mrb_value mrb_str_times(mrb_state *mrb, mrb_value self) { mrb_int n,len,times; struct RString *str2; char *p; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big"); } len = RSTRING_LEN(self)*times; str2 = str_new(mrb, 0, len); str_with_class(mrb, str2, self); p = RSTR_PTR(str2); if (len > 0) { n = RSTRING_LEN(self); memcpy(p, RSTRING_PTR(self), n); while (n <= len/2) { memcpy(p + n, p, n); n *= 2; } memcpy(p + n, p, len-n); } p[RSTR_LEN(str2)] = '\0'; return mrb_obj_value(str2); } /* -------------------------------------------------------------- */ #define lesser(a,b) (((a)>(b))?(b):(a)) /* ---------------------------*/ /* * call-seq: * mrb_value str1 <=> mrb_value str2 => int * > 1 * = 0 * < -1 */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) { mrb_int len; mrb_int retval; struct RString *s1 = mrb_str_ptr(str1); struct RString *s2 = mrb_str_ptr(str2); len = lesser(RSTR_LEN(s1), RSTR_LEN(s2)); retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); if (retval == 0) { if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0; if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1; return -1; } if (retval > 0) return 1; return -1; } /* 15.2.10.5.3 */ /* * call-seq: * str <=> other_str => -1, 0, +1 * * Comparison---Returns -1 if other_str is less than, 0 if * other_str is equal to, and +1 if other_str is greater than * str. If the strings are of different lengths, and the strings are * equal when compared up to the shortest length, then the longer string is * considered greater than the shorter one. If the variable $= is * false, the comparison is based on comparing the binary values * of each character in the string. In older versions of Ruby, setting * $= allowed case-insensitive comparisons; this is now deprecated * in favor of using String#casecmp. * * <=> is the basis for the methods <, * <=, >, >=, and between?, * included from module Comparable. The method * String#== does not use Comparable#==. * * "abcdef" <=> "abcde" #=> 1 * "abcdef" <=> "abcdef" #=> 0 * "abcdef" <=> "abcdefg" #=> -1 * "abcdef" <=> "ABCDEF" #=> 1 */ static mrb_value mrb_str_cmp_m(mrb_state *mrb, mrb_value str1) { mrb_value str2; mrb_int result; mrb_get_args(mrb, "o", &str2); if (!mrb_string_p(str2)) { if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) { return mrb_nil_value(); } else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) { return mrb_nil_value(); } else { mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1); if (mrb_nil_p(tmp)) return mrb_nil_value(); if (!mrb_fixnum(tmp)) { return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp); } result = -mrb_fixnum(tmp); } } else { result = mrb_str_cmp(mrb, str1, str2); } return mrb_fixnum_value(result); } static mrb_bool str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) { const mrb_int len = RSTRING_LEN(str1); if (len != RSTRING_LEN(str2)) return FALSE; if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0) return TRUE; return FALSE; } MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { if (mrb_immediate_p(str2)) return FALSE; if (!mrb_string_p(str2)) { if (mrb_nil_p(str2)) return FALSE; if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) { return FALSE; } str2 = mrb_funcall(mrb, str2, "to_str", 0); return mrb_equal(mrb, str2, str1); } return str_eql(mrb, str1, str2); } /* 15.2.10.5.4 */ /* * call-seq: * str == obj => true or false * * Equality--- * If obj is not a String, returns false. * Otherwise, returns false or true * * caution:if str <=> obj returns zero. */ static mrb_value mrb_str_equal_m(mrb_state *mrb, mrb_value str1) { mrb_value str2; mrb_get_args(mrb, "o", &str2); return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str) { mrb_value s; if (!mrb_string_p(str)) { s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); if (mrb_nil_p(s)) { s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s"); } return s; } return str; } MRB_API const char* mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr) { mrb_value str = mrb_str_to_str(mrb, ptr); return RSTRING_PTR(str); } MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value ptr) { mrb_value str = mrb_str_to_str(mrb, ptr); return RSTRING_LEN(str); } void mrb_noregexp(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); } void mrb_regexp_check(mrb_state *mrb, mrb_value obj) { if (mrb_regexp_p(mrb, obj)) { mrb_noregexp(mrb, obj); } } MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); struct RString *dup = str_new(mrb, 0, 0); str_with_class(mrb, dup, str); return str_replace(mrb, dup, s); } static mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) { mrb_int idx; mrb_regexp_check(mrb, indx); switch (mrb_type(indx)) { case MRB_TT_FIXNUM: idx = mrb_fixnum(indx); num_index: str = str_substr(mrb, str, idx, 1); if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case MRB_TT_STRING: if (str_index(mrb, str, indx, 0) != -1) return mrb_str_dup(mrb, indx); return mrb_nil_value(); case MRB_TT_RANGE: /* check if indx is Range */ { mrb_int beg, len; len = RSTRING_CHAR_LEN(str); if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { return str_subseq(mrb, str, beg, len); } else { return mrb_nil_value(); } } case MRB_TT_FLOAT: default: indx = mrb_Integer(mrb, indx); if (mrb_nil_p(indx)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); } idx = mrb_fixnum(indx); goto num_index; } return mrb_nil_value(); /* not reached */ } /* 15.2.10.5.6 */ /* 15.2.10.5.34 */ /* * call-seq: * str[fixnum] => fixnum or nil * str[fixnum, fixnum] => new_str or nil * str[range] => new_str or nil * str[regexp] => new_str or nil * str[regexp, fixnum] => new_str or nil * str[other_str] => new_str or nil * str.slice(fixnum) => fixnum 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 * * Element Reference---If passed a single Fixnum, returns the code * of the character at that position. If passed two Fixnum * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a range, a substring containing * characters at offsets given by the range is returned. In all three cases, if * an offset is negative, it is counted from the end of str. Returns * nil if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * * If a String is given, that string is returned if it occurs in * str. In both cases, nil is returned if there is no * match. * * a = "hello there" * a[1] #=> 101(1.8.7) "e"(1.9.2) * a[1.1] #=> "e"(1.9.2) * a[1,3] #=> "ell" * a[1..3] #=> "ell" * a[-3,2] #=> "er" * a[-4..-2] #=> "her" * a[12..-1] #=> nil * a[-2..-4] #=> "" * a["lo"] #=> "lo" * a["bye"] #=> nil */ static mrb_value mrb_str_aref_m(mrb_state *mrb, mrb_value str) { mrb_value a1, a2; int argc; argc = mrb_get_args(mrb, "o|o", &a1, &a2); if (argc == 2) { mrb_regexp_check(mrb, a1); return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); } if (argc != 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); } return mrb_str_aref(mrb, str, a1); } /* 15.2.10.5.8 */ /* * call-seq: * str.capitalize! => str or nil * * Modifies str by converting the first character to uppercase and the * remainder to lowercase. Returns nil if no changes are made. * * a = "hello" * a.capitalize! #=> "Hello" * a #=> "Hello" * a.capitalize! #=> nil */ static mrb_value mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value(); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } while (++p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.7 */ /* * call-seq: * str.capitalize => new_str * * Returns a copy of str with the first character converted to uppercase * and the remainder to lowercase. * * "hello".capitalize #=> "Hello" * "HELLO".capitalize #=> "Hello" * "123ABC".capitalize #=> "123abc" */ static mrb_value mrb_str_capitalize(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_capitalize_bang(mrb, str); return str; } /* 15.2.10.5.10 */ /* * call-seq: * str.chomp!(separator="\n") => str or nil * * Modifies str in place as described for String#chomp, * returning str, or nil if no modifications were made. */ static mrb_value mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) { mrb_value rs; mrb_int newline; char *p, *pp; mrb_int rslen; mrb_int len; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); len = RSTR_LEN(s); if (mrb_get_args(mrb, "|S", &rs) == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: if (RSTR_PTR(s)[len-1] == '\n') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); if (RSTR_LEN(s) > 0 && RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } } else if (RSTR_PTR(s)[len-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } else { return mrb_nil_value(); } RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); p = RSTR_PTR(s); rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { len--; if (len>0 && p[len-1] == '\r') len--; } if (len < RSTR_LEN(s)) { RSTR_SET_LEN(s, len); p[len] = '\0'; return str; } return mrb_nil_value(); } if (rslen > len) return mrb_nil_value(); newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') goto smart_chomp; pp = p + len - rslen; if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { RSTR_SET_LEN(s, len - rslen); p[RSTR_LEN(s)] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.9 */ /* * call-seq: * str.chomp(separator="\n") => new_str * * Returns a new String with the given record separator removed * from the end of str (if present). If $/ has not been * changed from the default Ruby record separator, then chomp also * removes carriage return characters (that is it will remove \n, * \r, and \r\n). * * "hello".chomp #=> "hello" * "hello\n".chomp #=> "hello" * "hello\r\n".chomp #=> "hello" * "hello\n\r".chomp #=> "hello\n" * "hello\r".chomp #=> "hello" * "hello \n there".chomp #=> "hello \n there" * "hello".chomp("llo") #=> "he" */ static mrb_value mrb_str_chomp(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chomp_bang(mrb, str); return str; } /* 15.2.10.5.12 */ /* * call-seq: * str.chop! => str or nil * * Processes str as for String#chop, returning str, * or nil if str is the empty string. See also * String#chomp!. */ static mrb_value mrb_str_chop_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); if (RSTR_LEN(s) > 0) { mrb_int len; #ifdef MRB_UTF8_STRING const char* t = RSTR_PTR(s), *p = t; const char* e = p + RSTR_LEN(s); while (p=e) break; p += clen; } len = p - t; #else len = RSTR_LEN(s) - 1; #endif if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && RSTR_PTR(s)[len-1] == '\r') { len--; } } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.11 */ /* * call-seq: * str.chop => new_str * * Returns a new String with the last character removed. If the * string ends with \r\n, both characters are removed. Applying * chop to an empty string returns an empty * string. String#chomp is often a safer alternative, as it leaves * the string unchanged if it doesn't end in a record separator. * * "string\r\n".chop #=> "string" * "string\n\r".chop #=> "string\n" * "string\n".chop #=> "string" * "string".chop #=> "strin" * "x".chop #=> "" */ static mrb_value mrb_str_chop(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chop_bang(mrb, str); return str; } /* 15.2.10.5.14 */ /* * call-seq: * str.downcase! => str or nil * * Downcases the contents of str, returning nil if no * changes were made. */ static mrb_value mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.13 */ /* * call-seq: * str.downcase => new_str * * Returns a copy of str with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only * characters 'A' to 'Z' are affected. * * "hEllO".downcase #=> "hello" */ static mrb_value mrb_str_downcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_downcase_bang(mrb, str); return str; } /* 15.2.10.5.16 */ /* * call-seq: * str.empty? => true or false * * Returns true if str has a length of zero. * * "hello".empty? #=> false * "".empty? #=> true */ static mrb_value mrb_str_empty_p(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); return mrb_bool_value(RSTR_LEN(s) == 0); } /* 15.2.10.5.17 */ /* * call-seq: * str.eql?(other) => true or false * * Two strings are equal if the have the same length and content. */ static mrb_value mrb_str_eql(mrb_state *mrb, mrb_value self) { mrb_value str2; mrb_bool eql_p; mrb_get_args(mrb, "o", &str2); eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2); return mrb_bool_value(eql_p); } MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { return str_substr(mrb, str, beg, len); } mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); mrb_int len = RSTR_LEN(s); char *p = RSTR_PTR(s); mrb_int key = 0; while (len--) { key = key*65599 + *p; p++; } return key + (key>>5); } /* 15.2.10.5.20 */ /* * call-seq: * str.hash => fixnum * * Return a hash based on the string's length and content. */ static mrb_value mrb_str_hash_m(mrb_state *mrb, mrb_value self) { mrb_int key = mrb_str_hash(mrb, self); return mrb_fixnum_value(key); } /* 15.2.10.5.21 */ /* * call-seq: * str.include? other_str => true or false * str.include? fixnum => true or false * * Returns true if str contains the given string or * character. * * "hello".include? "lo" #=> true * "hello".include? "ol" #=> false * "hello".include? ?h #=> true */ static mrb_value mrb_str_include(mrb_state *mrb, mrb_value self) { mrb_int i; mrb_value str2; mrb_bool include_p; mrb_get_args(mrb, "o", &str2); if (mrb_fixnum_p(str2)) { include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL); } else { str2 = mrb_str_to_str(mrb, str2); i = str_index(mrb, self, str2, 0); include_p = (i != -1); } return mrb_bool_value(include_p); } /* 15.2.10.5.22 */ /* * call-seq: * str.index(substring [, offset]) => fixnum or nil * str.index(fixnum [, offset]) => fixnum or nil * str.index(regexp [, offset]) => fixnum or nil * * Returns the index of the first occurrence of the given * substring, * character (fixnum), or pattern (regexp) in str. * Returns * nil if not found. * If the second parameter is present, it * specifies the position in the string to begin the search. * * "hello".index('e') #=> 1 * "hello".index('lo') #=> 3 * "hello".index('a') #=> nil * "hello".index(101) #=> 1(101=0x65='e') * "hello".index(/[aeiou]/, -3) #=> 4 */ static mrb_value mrb_str_index(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_int pos, clen; mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { pos = mrb_fixnum(argv[1]); sub = argv[0]; } else { pos = 0; if (argc > 0) sub = argv[0]; else sub = mrb_nil_value(); } mrb_regexp_check(mrb, sub); clen = RSTRING_CHAR_LEN(str); if (pos < 0) { pos += clen; if (pos < 0) { return mrb_nil_value(); } } if (pos >= clen) return mrb_nil_value(); pos = chars2bytes(str, 0, pos); switch (mrb_type(sub)) { default: { mrb_value tmp; tmp = mrb_check_string_type(mrb, sub); if (mrb_nil_p(tmp)) { mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); } sub = tmp; } /* fall through */ case MRB_TT_STRING: pos = str_index(mrb, str, sub, pos); break; } if (pos == -1) return mrb_nil_value(); pos = bytes2chars(RSTRING_PTR(str), pos); BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } #define STR_REPLACE_SHARED_MIN 10 /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* * call-seq: * str.replace(other_str) => str * * s = "hello" #=> "hello" * s.replace "world" #=> "world" */ static mrb_value mrb_str_replace(mrb_state *mrb, mrb_value str) { mrb_value str2; mrb_get_args(mrb, "S", &str2); return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2)); } /* 15.2.10.5.23 */ /* * call-seq: * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_init(mrb_state *mrb, mrb_value self) { mrb_value str2; if (mrb_get_args(mrb, "|S", &str2) == 1) { str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2)); } return self; } /* 15.2.10.5.25 */ /* 15.2.10.5.41 */ /* * call-seq: * str.intern => symbol * str.to_sym => symbol * * Returns the Symbol corresponding to str, creating the * symbol if it did not previously exist. See Symbol#id2name. * * "Koala".intern #=> :Koala * s = 'cat'.to_sym #=> :cat * s == :cat #=> true * s = '@cat'.to_sym #=> :@cat * s == :@cat #=> true * * This can also be used to create symbols that cannot be represented using the * :xxx notation. * * 'cat and dog'.to_sym #=> :"cat and dog" */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self) { return mrb_symbol_value(mrb_intern_str(mrb, self)); } /* ---------------------------------- */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { mrb_value str; if (mrb_string_p(obj)) { return obj; } str = mrb_funcall(mrb, obj, "to_s", 0); if (!mrb_string_p(str)) return mrb_any_to_s(mrb, obj); return str; } MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p) { struct RString *p_str; char *p1; char *p2; uintptr_t n = (uintptr_t)p; p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); p1 = RSTR_PTR(p_str); *p1++ = '0'; *p1++ = 'x'; p2 = p1; do { *p2++ = mrb_digitmap[n % 16]; n /= 16; } while (n > 0); *p2 = '\0'; RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); while (p1 < p2) { const char c = *p1; *p1++ = *--p2; *p2 = c; } return mrb_obj_value(p_str); } MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str) { return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); } MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str) { return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); } /* 15.2.10.5.30 */ /* * call-seq: * str.reverse! => str * * Reverses str in place. */ static mrb_value mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { #ifdef MRB_UTF8_STRING mrb_int utf8_len = RSTRING_CHAR_LEN(str); mrb_int len = RSTRING_LEN(str); if (utf8_len == len) goto bytes; if (utf8_len > 1) { char *buf; char *p, *e, *r; mrb_str_modify(mrb, mrb_str_ptr(str)); len = RSTRING_LEN(str); buf = mrb_malloc(mrb, (size_t)len); p = buf; e = buf + len; memcpy(buf, RSTRING_PTR(str), len); r = RSTRING_PTR(str) + len; while (p 1) { p = RSTR_PTR(s); e = p + RSTR_LEN(s) - 1; while (p < e) { c = *p; *p++ = *e; *e-- = c; } } return str; } } /* ---------------------------------- */ /* 15.2.10.5.29 */ /* * call-seq: * str.reverse => new_str * * Returns a new string with the characters from str in reverse order. * * "stressed".reverse #=> "desserts" */ static mrb_value mrb_str_reverse(mrb_state *mrb, mrb_value str) { mrb_value str2 = mrb_str_dup(mrb, str); mrb_str_reverse_bang(mrb, str2); return str2; } /* 15.2.10.5.31 */ /* * call-seq: * str.rindex(substring [, fixnum]) => fixnum or nil * str.rindex(fixnum [, fixnum]) => fixnum or nil * str.rindex(regexp [, fixnum]) => fixnum or nil * * Returns the index of the last occurrence of the given substring, * character (fixnum), or pattern (regexp) in str. Returns * nil if not found. If the second parameter is present, it * specifies the position in the string to end the search---characters beyond * this point will not be considered. * * "hello".rindex('e') #=> 1 * "hello".rindex('l') #=> 3 * "hello".rindex('a') #=> nil * "hello".rindex(101) #=> 1 * "hello".rindex(/[aeiou]/, -2) #=> 1 */ static mrb_value mrb_str_rindex(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_value vpos; mrb_int pos, len = RSTRING_CHAR_LEN(str); mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { sub = argv[0]; vpos = argv[1]; pos = mrb_fixnum(vpos); if (pos < 0) { pos += len; if (pos < 0) { mrb_regexp_check(mrb, sub); return mrb_nil_value(); } } if (pos > len) pos = len; } else { pos = len; if (argc > 0) sub = argv[0]; else sub = mrb_nil_value(); } pos = chars2bytes(str, 0, pos); mrb_regexp_check(mrb, sub); switch (mrb_type(sub)) { default: { mrb_value tmp; tmp = mrb_check_string_type(mrb, sub); if (mrb_nil_p(tmp)) { mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); } sub = tmp; } /* fall through */ case MRB_TT_STRING: pos = str_rindex(mrb, str, sub, pos); if (pos >= 0) { pos = bytes2chars(RSTRING_PTR(str), pos); BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } break; } /* end of switch (TYPE(sub)) */ return mrb_nil_value(); } /* 15.2.10.5.35 */ /* * call-seq: * str.split(pattern="\n", [limit]) => anArray * * Divides str into substrings based on a delimiter, returning an array * of these substrings. * * If pattern is a String, then its contents are used as * the delimiter when splitting str. If pattern is a single * space, str is split on whitespace, with leading whitespace and runs * of contiguous whitespace characters ignored. * * If pattern is a Regexp, str is divided where the * pattern matches. Whenever the pattern matches a zero-length string, * str is split into individual characters. * * If pattern is omitted, the value of $; is used. If * $; is nil (which is the default), str is * split on whitespace as if ' ' were specified. * * If the limit parameter is omitted, trailing null fields are * suppressed. If limit is a positive number, at most that number of * fields will be returned (if limit is 1, the entire * string is returned as the only entry in an array). If negative, there is no * limit to the number of fields returned, and trailing null fields are not * suppressed. * * " now's the time".split #=> ["now's", "the", "time"] * " now's the time".split(' ') #=> ["now's", "the", "time"] * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"] * "hello".split(//) #=> ["h", "e", "l", "l", "o"] * "hello".split(//, 3) #=> ["h", "e", "llo"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"] * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""] */ static mrb_value mrb_str_split_m(mrb_state *mrb, mrb_value str) { int argc; mrb_value spat = mrb_nil_value(); enum {awk, string, regexp} split_type = string; long i = 0, lim_p; mrb_int beg; mrb_int end; mrb_int lim = 0; mrb_value result, tmp; argc = mrb_get_args(mrb, "|oi", &spat, &lim); lim_p = (lim > 0 && argc == 2); if (argc == 2) { if (lim == 1) { if (RSTRING_LEN(str) == 0) return mrb_ary_new_capa(mrb, 0); return mrb_ary_new_from_values(mrb, 1, &str); } i = 1; } if (argc == 0 || mrb_nil_p(spat)) { split_type = awk; } else { if (mrb_string_p(spat)) { split_type = string; if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { split_type = awk; } } else { mrb_noregexp(mrb, str); } } result = mrb_ary_new(mrb); beg = 0; if (split_type == awk) { mrb_bool skip = TRUE; mrb_int idx = 0; mrb_int str_len = RSTRING_LEN(str); unsigned int c; int ai = mrb_gc_arena_save(mrb); idx = end = beg; while (idx < str_len) { c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { if (ISSPACE(c)) { beg = idx; } else { end = idx; skip = FALSE; if (lim_p && lim <= i) break; } } else if (ISSPACE(c)) { mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; beg = idx; if (lim_p) ++i; } else { end = idx; } } } else if (split_type == string) { mrb_int str_len = RSTRING_LEN(str); mrb_int pat_len = RSTRING_LEN(spat); mrb_int idx = 0; int ai = mrb_gc_arena_save(mrb); while (idx < str_len) { if (pat_len > 0) { end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); if (end < 0) break; } else { end = chars2bytes(str, idx, 1); } mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end)); mrb_gc_arena_restore(mrb, ai); idx += end + pat_len; if (lim_p && lim <= ++i) break; } beg = idx; } else { mrb_noregexp(mrb, str); } if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { if (RSTRING_LEN(str) == beg) { tmp = mrb_str_new_empty(mrb, str); } else { tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } if (!lim_p && lim == 0) { mrb_int len; while ((len = RARRAY_LEN(result)) > 0 && (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0)) mrb_ary_pop(mrb, result); } return result; } MRB_API mrb_value mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck) { const char *p = str; const char *pend = str + len; char sign = 1; int c; uint64_t n = 0; mrb_int val; #define conv_digit(c) \ (ISDIGIT(c) ? ((c) - '0') : \ ISLOWER(c) ? ((c) - 'a' + 10) : \ ISUPPER(c) ? ((c) - 'A' + 10) : \ -1) if (!p) { if (badcheck) goto bad; return mrb_fixnum_value(0); } while (p=pend) { if (badcheck) goto bad; return mrb_fixnum_value(0); } if (*p == '0') { /* squeeze preceding 0s */ p++; while (p= base) { break; } n *= base; n += c; if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new(mrb, str, pend-str)); } } val = (mrb_int)n; if (badcheck) { if (p == str) goto bad; /* no number */ while (p integer * * Returns the result of interpreting leading characters in str as an * integer base base (between 2 and 36). Extraneous characters past the * end of a valid number are ignored. If there is not a valid number at the * start of str, 0 is returned. This method never raises an * exception. * * "12345".to_i #=> 12345 * "99 red balloons".to_i #=> 99 * "0a".to_i #=> 0 * "0a".to_i(16) #=> 10 * "hello".to_i #=> 0 * "1100101".to_i(2) #=> 101 * "1100101".to_i(8) #=> 294977 * "1100101".to_i(10) #=> 1100101 * "1100101".to_i(16) #=> 17826049 */ static mrb_value mrb_str_to_i(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); if (base < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); } return mrb_str_to_inum(mrb, self, base, FALSE); } MRB_API double mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck) { char *end; char buf[DBL_DIG * 4 + 10]; double d; enum {max_width = 20}; if (!p) return 0.0; while (ISSPACE(*p)) p++; if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { return 0.0; } d = strtod(p, &end); if (p == end) { if (badcheck) { bad: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p)); /* not reached */ } return d; } if (*end) { char *n = buf; char *e = buf + sizeof(buf) - 1; char prev = 0; while (p < end && n < e) prev = *n++ = *p++; while (*p) { if (*p == '_') { /* remove underscores between digits */ if (badcheck) { if (n == buf || !ISDIGIT(prev)) goto bad; ++p; if (!ISDIGIT(*p)) goto bad; } else { while (*++p == '_'); continue; } } prev = *p++; if (n < e) *n++ = prev; } *n = '\0'; p = buf; if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { return 0.0; } d = strtod(p, &end); if (badcheck) { if (!end || p == end) goto bad; while (*end && ISSPACE(*end)) end++; if (*end) goto bad; } } return d; } MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) { char *s; mrb_int len; str = mrb_str_to_str(mrb, str); s = RSTRING_PTR(str); len = RSTRING_LEN(str); if (s) { if (badcheck && memchr(s, '\0', len)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte"); } if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); s = RSTR_PTR(temp_str); } } return mrb_cstr_to_dbl(mrb, s, badcheck); } /* 15.2.10.5.39 */ /* * call-seq: * str.to_f => float * * Returns the result of interpreting leading characters in str as a * floating point number. Extraneous characters past the end of a valid number * are ignored. If there is not a valid number at the start of str, * 0.0 is returned. This method never raises an exception. * * "123.45e1".to_f #=> 1234.5 * "45.67 degrees".to_f #=> 45.67 * "thx1138".to_f #=> 0.0 */ static mrb_value mrb_str_to_f(mrb_state *mrb, mrb_value self) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); } /* 15.2.10.5.40 */ /* * call-seq: * str.to_s => str * str.to_str => str * * Returns the receiver. */ static mrb_value mrb_str_to_s(mrb_state *mrb, mrb_value self) { if (mrb_obj_class(mrb, self) != mrb->string_class) { return mrb_str_dup(mrb, self); } return self; } /* 15.2.10.5.43 */ /* * call-seq: * str.upcase! => str or nil * * Upcases the contents of str, returning nil if no changes * were made. */ static mrb_value mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); char *p, *pend; mrb_bool modify = FALSE; mrb_str_modify(mrb, s); p = RSTRING_PTR(str); pend = RSTRING_END(str); while (p < pend) { if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.42 */ /* * call-seq: * str.upcase => new_str * * Returns a copy of str with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only * characters 'a' to 'z' are affected. * * "hEllO".upcase #=> "HELLO" */ static mrb_value mrb_str_upcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_upcase_bang(mrb, str); return str; } #define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) /* * call-seq: * str.dump -> new_str * * Produces a version of str with all nonprinting characters replaced by * \nnn notation and all special characters escaped. */ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str) { mrb_int len; const char *p, *pend; char *q; struct RString *result; len = 2; /* "" */ p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); while (p < pend) { unsigned char c = *p++; switch (c) { case '"': case '\\': case '\n': case '\r': case '\t': case '\f': case '\013': case '\010': case '\007': case '\033': len += 2; break; case '#': len += IS_EVSTR(p, pend) ? 2 : 1; break; default: if (ISPRINT(c)) { len++; } else { len += 4; /* \NNN */ } break; } } result = str_new(mrb, 0, len); str_with_class(mrb, result, str); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); q = RSTR_PTR(result); *q++ = '"'; while (p < pend) { unsigned char c = *p++; switch (c) { case '"': case '\\': *q++ = '\\'; *q++ = c; break; case '\n': *q++ = '\\'; *q++ = 'n'; break; case '\r': *q++ = '\\'; *q++ = 'r'; break; case '\t': *q++ = '\\'; *q++ = 't'; break; case '\f': *q++ = '\\'; *q++ = 'f'; break; case '\013': *q++ = '\\'; *q++ = 'v'; break; case '\010': *q++ = '\\'; *q++ = 'b'; break; case '\007': *q++ = '\\'; *q++ = 'a'; break; case '\033': *q++ = '\\'; *q++ = 'e'; break; case '#': if (IS_EVSTR(p, pend)) *q++ = '\\'; *q++ = '#'; break; default: if (ISPRINT(c)) { *q++ = c; } else { *q++ = '\\'; q[2] = '0' + c % 8; c /= 8; q[1] = '0' + c % 8; c /= 8; q[0] = '0' + c % 8; q += 3; } } } *q = '"'; return mrb_obj_value(result); } MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { str_buf_cat(mrb, mrb_str_ptr(str), ptr, len); return str; } MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr) { return mrb_str_cat(mrb, str, ptr, strlen(ptr)); } MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) { return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); } MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { str2 = mrb_str_to_str(mrb, str2); return mrb_str_cat_str(mrb, str1, str2); } #define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ /* * call-seq: * str.inspect -> string * * Returns a printable version of _str_, surrounded by quote marks, * with special characters escaped. * * str = "hello" * str[3] = "\b" * str.inspect #=> "\"hel\\bo\"" */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str) { const char *p, *pend; char buf[CHAR_ESC_LEN + 1]; mrb_value result = mrb_str_new_lit(mrb, "\""); p = RSTRING_PTR(str); pend = RSTRING_END(str); for (;p < pend; p++) { unsigned char c, cc; #ifdef MRB_UTF8_STRING mrb_int clen; clen = utf8len(p, pend); if (clen > 1) { mrb_int i; for (i=0; i array of fixnums * * Returns an array of bytes in _str_. * * str = "hello" * str.bytes #=> [104, 101, 108, 108, 111] */ static mrb_value mrb_str_bytes(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); while (p < pend) { mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); p++; } return a; } /* ---------------------------*/ void mrb_init_string(mrb_state *mrb) { struct RClass *s; mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "<=>", mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ mrb_define_method(mrb, s, "==", mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */ mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */ mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */ mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */ mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */ mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */ mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */ mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */ mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */ mrb_define_method(mrb, s, "eql?", mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */ mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ mrb_define_method(mrb, s, "index", mrb_str_index, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */ mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ mrb_define_method(mrb, s, "to_i", mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */ mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */ mrb_define_method(mrb, s, "to_str", mrb_str_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "to_sym", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */ mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */ mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE()); } mruby-1.2.0+20160315+git4f20d58a/src/symbol.c000066400000000000000000000264141267140355100176550ustar00rootroot00000000000000/* ** symbol.c - Symbol class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include /* ------------------------------------------------------ */ typedef struct symbol_name { mrb_bool lit : 1; uint16_t len; const char *name; } symbol_name; static inline khint_t sym_hash_func(mrb_state *mrb, mrb_sym s) { khint_t h = 0; size_t i, len = mrb->symtbl[s].len; const char *p = mrb->symtbl[s].name; for (i=0; isymtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0) KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE) KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal) /* ------------------------------------------------------ */ static void sym_validate_len(mrb_state *mrb, size_t len) { if (len >= RITE_LV_NULL_MARK) { mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); } } static mrb_sym sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) { khash_t(n2s) *h = mrb->name2sym; symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */ khiter_t k; mrb_sym sym; char *p; sym_validate_len(mrb, len); if (sname) { sname->lit = lit; sname->len = (uint16_t)len; sname->name = name; k = kh_get(n2s, mrb, h, 0); if (k != kh_end(h)) return kh_key(h, k); } /* registering a new symbol */ sym = ++mrb->symidx; if (mrb->symcapa < sym) { if (mrb->symcapa == 0) mrb->symcapa = 100; else mrb->symcapa = (size_t)(mrb->symcapa * 1.2); mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1)); } sname = &mrb->symtbl[sym]; sname->len = (uint16_t)len; if (lit || mrb_ro_data_p(name)) { sname->name = name; sname->lit = TRUE; } else { p = (char *)mrb_malloc(mrb, len+1); memcpy(p, name, len); p[len] = 0; sname->name = (const char*)p; sname->lit = FALSE; } kh_put(n2s, mrb, h, sym); return sym; } MRB_API mrb_sym mrb_intern(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, FALSE); } MRB_API mrb_sym mrb_intern_static(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, TRUE); } MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char *name) { return mrb_intern(mrb, name, strlen(name)); } MRB_API mrb_sym mrb_intern_str(mrb_state *mrb, mrb_value str) { return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } MRB_API mrb_value mrb_check_intern(mrb_state *mrb, const char *name, size_t len) { khash_t(n2s) *h = mrb->name2sym; symbol_name *sname = mrb->symtbl; khiter_t k; sym_validate_len(mrb, len); sname->len = (uint16_t)len; sname->name = name; k = kh_get(n2s, mrb, h, 0); if (k != kh_end(h)) { return mrb_symbol_value(kh_key(h, k)); } return mrb_nil_value(); } MRB_API mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { return mrb_check_intern(mrb, name, (mrb_int)strlen(name)); } MRB_API mrb_value mrb_check_intern_str(mrb_state *mrb, mrb_value str) { return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } /* lenp must be a pointer to a size_t variable */ MRB_API const char* mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) { if (sym == 0 || mrb->symidx < sym) { if (lenp) *lenp = 0; return NULL; } if (lenp) *lenp = mrb->symtbl[sym].len; return mrb->symtbl[sym].name; } void mrb_free_symtbl(mrb_state *mrb) { mrb_sym i, lim; for (i=1, lim=mrb->symidx+1; isymtbl[i].lit) { mrb_free(mrb, (char*)mrb->symtbl[i].name); } } mrb_free(mrb, mrb->symtbl); kh_destroy(n2s, mrb, mrb->name2sym); } void mrb_init_symtbl(mrb_state *mrb) { mrb->name2sym = kh_init(n2s, mrb); } /********************************************************************** * Document-class: Symbol * * Symbol objects represent names and some strings * inside the Ruby * interpreter. They are generated using the :name and * :"string" literals * syntax, and by the various to_sym methods. The same * Symbol object will be created for a given name or string * for the duration of a program's execution, regardless of the context * or meaning of that name. Thus if Fred is a constant in * one context, a method in another, and a class in a third, the * Symbol :Fred will be the same object in * all three contexts. * * module One * class Fred * end * $f1 = :Fred * end * module Two * Fred = 1 * $f2 = :Fred * end * def Fred() * end * $f3 = :Fred * $f1.object_id #=> 2514190 * $f2.object_id #=> 2514190 * $f3.object_id #=> 2514190 * */ /* 15.2.11.3.1 */ /* * call-seq: * sym == obj -> true or false * * Equality---If sym and obj are exactly the same * symbol, returns true. */ static mrb_value sym_equal(mrb_state *mrb, mrb_value sym1) { mrb_value sym2; mrb_get_args(mrb, "o", &sym2); return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2)); } /* 15.2.11.3.2 */ /* 15.2.11.3.3 */ /* * call-seq: * sym.id2name -> string * sym.to_s -> string * * Returns the name or string corresponding to sym. * * :fred.id2name #=> "fred" */ static mrb_value mrb_sym_to_s(mrb_state *mrb, mrb_value sym) { mrb_sym id = mrb_symbol(sym); const char *p; mrb_int len; p = mrb_sym2name_len(mrb, id, &len); return mrb_str_new_static(mrb, p, len); } /* 15.2.11.3.4 */ /* * call-seq: * sym.to_sym -> sym * sym.intern -> sym * * In general, to_sym returns the Symbol corresponding * to an object. As sym is already a symbol, self is returned * in this case. */ static mrb_value sym_to_sym(mrb_state *mrb, mrb_value sym) { return sym; } /* 15.2.11.3.5(x) */ /* * call-seq: * sym.inspect -> string * * Returns the representation of sym as a symbol literal. * * :fred.inspect #=> ":fred" */ #if __STDC__ # define SIGN_EXTEND_CHAR(c) ((signed char)(c)) #else /* not __STDC__ */ /* As in Harbison and Steele. */ # define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128) #endif #define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_')) static mrb_bool is_special_global_name(const char* m) { switch (*m) { case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\': case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"': case '&': case '`': case '\'': case '+': case '0': ++m; break; case '-': ++m; if (is_identchar(*m)) m += 1; break; default: if (!ISDIGIT(*m)) return FALSE; do ++m; while (ISDIGIT(*m)); break; } return !*m; } static mrb_bool symname_p(const char *name) { const char *m = name; mrb_bool localid = FALSE; if (!m) return FALSE; switch (*m) { case '\0': return FALSE; case '$': if (is_special_global_name(++m)) return TRUE; goto id; case '@': if (*++m == '@') ++m; goto id; case '<': switch (*++m) { case '<': ++m; break; case '=': if (*++m == '>') ++m; break; default: break; } break; case '>': switch (*++m) { case '>': case '=': ++m; break; default: break; } break; case '=': switch (*++m) { case '~': ++m; break; case '=': if (*++m == '=') ++m; break; default: return FALSE; } break; case '*': if (*++m == '*') ++m; break; case '!': switch (*++m) { case '=': case '~': ++m; } break; case '+': case '-': if (*++m == '@') ++m; break; case '|': if (*++m == '|') ++m; break; case '&': if (*++m == '&') ++m; break; case '^': case '/': case '%': case '~': case '`': ++m; break; case '[': if (*++m != ']') return FALSE; if (*++m == '=') ++m; break; default: localid = !ISUPPER(*m); id: if (*m != '_' && !ISALPHA(*m)) return FALSE; while (is_identchar(*m)) m += 1; if (localid) { switch (*m) { case '!': case '?': case '=': ++m; default: break; } } break; } return *m ? FALSE : TRUE; } static mrb_value sym_inspect(mrb_state *mrb, mrb_value sym) { mrb_value str; const char *name; mrb_int len; mrb_sym id = mrb_symbol(sym); char *sp; name = mrb_sym2name_len(mrb, id, &len); str = mrb_str_new(mrb, 0, len+1); sp = RSTRING_PTR(str); RSTRING_PTR(str)[0] = ':'; memcpy(sp+1, name, len); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); if (!symname_p(name) || strlen(name) != (size_t)len) { str = mrb_str_dump(mrb, str); sp = RSTRING_PTR(str); sp[0] = ':'; sp[1] = '"'; } return str; } MRB_API mrb_value mrb_sym2str(mrb_state *mrb, mrb_sym sym) { mrb_int len; const char *name = mrb_sym2name_len(mrb, sym, &len); if (!name) return mrb_undef_value(); /* can't happen */ return mrb_str_new_static(mrb, name, len); } MRB_API const char* mrb_sym2name(mrb_state *mrb, mrb_sym sym) { mrb_int len; const char *name = mrb_sym2name_len(mrb, sym, &len); if (!name) return NULL; if (symname_p(name) && strlen(name) == (size_t)len) { return name; } else { mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len)); return RSTRING_PTR(str); } } #define lesser(a,b) (((a)>(b))?(b):(a)) static mrb_value sym_cmp(mrb_state *mrb, mrb_value s1) { mrb_value s2; mrb_sym sym1, sym2; mrb_get_args(mrb, "o", &s2); if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value(); sym1 = mrb_symbol(s1); sym2 = mrb_symbol(s2); if (sym1 == sym2) return mrb_fixnum_value(0); else { const char *p1, *p2; int retval; mrb_int len, len1, len2; p1 = mrb_sym2name_len(mrb, sym1, &len1); p2 = mrb_sym2name_len(mrb, sym2, &len2); len = lesser(len1, len2); retval = memcmp(p1, p2, len); if (retval == 0) { if (len1 == len2) return mrb_fixnum_value(0); if (len1 > len2) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } if (retval > 0) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } } void mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */ mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */ mrb_define_method(mrb, sym, "to_s", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */ mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */ mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */ mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1)); } mruby-1.2.0+20160315+git4f20d58a/src/value_array.h000066400000000000000000000006421267140355100206620ustar00rootroot00000000000000#ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ #include static inline void value_move(mrb_value *s1, const mrb_value *s2, size_t n) { if (s1 > s2 && s1 < s2 + n) { s1 += n; s2 += n; while (n-- > 0) { *--s1 = *--s2; } } else if (s1 != s2) { while (n-- > 0) { *s1++ = *s2++; } } else { /* nothing to do. */ } } #endif /* MRB_VALUE_ARRAY_H__ */ mruby-1.2.0+20160315+git4f20d58a/src/variable.c000066400000000000000000000555571267140355100201470ustar00rootroot00000000000000/* ** variable.c - mruby variables ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); #ifdef MRB_USE_IV_SEGLIST #ifndef MRB_SEGMENT_SIZE #define MRB_SEGMENT_SIZE 4 #endif typedef struct segment { mrb_sym key[MRB_SEGMENT_SIZE]; mrb_value val[MRB_SEGMENT_SIZE]; struct segment *next; } segment; /* Instance variable table structure */ typedef struct iv_tbl { segment *rootseg; size_t size; size_t last_len; } iv_tbl; /* * Creates the instance variable table. * * Parameters * mrb * Returns * the instance variable table. */ static iv_tbl* iv_new(mrb_state *mrb) { iv_tbl *t; t = mrb_malloc(mrb, sizeof(iv_tbl)); t->size = 0; t->rootseg = NULL; t->last_len = 0; return t; } /* * Set the value for the symbol in the instance variable table. * * Parameters * mrb * t the instance variable table to be set in. * sym the symbol to be used as the key. * val the value to be set. */ static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) { segment *seg = t->rootseg; segment *prev = NULL; segment *matched_seg = NULL; size_t matched_idx = 0; size_t i; while (seg) { for (i=0; ikey[i]; /* Found room in last segment after last_len */ if (!seg->next && i >= t->last_len) { seg->key[i] = sym; seg->val[i] = val; t->last_len = i+1; t->size++; return; } if (!matched_seg && key == 0) { matched_seg = seg; matched_idx = i; } else if (key == sym) { seg->val[i] = val; return; } } prev = seg; seg = seg->next; } /* Not found */ t->size++; if (matched_seg) { matched_seg->key[matched_idx] = sym; matched_seg->val[matched_idx] = val; return; } seg = mrb_malloc(mrb, sizeof(segment)); if (!seg) return; seg->next = NULL; seg->key[0] = sym; seg->val[0] = val; t->last_len = 1; if (prev) { prev->next = seg; } else { t->rootseg = seg; } } /* * Get a value for a symbol from the instance variable table. * * Parameters * mrb * t the variable table to be searched. * sym the symbol to be used as the key. * vp the value pointer. Receives the value if the specified symbol is * contained in the instance variable table. * Returns * true if the specified symbol is contained in the instance variable table. */ static mrb_bool iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { segment *seg; size_t i; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; if (!seg->next && i >= t->last_len) { return FALSE; } if (key == sym) { if (vp) *vp = seg->val[i]; return TRUE; } } seg = seg->next; } return FALSE; } /* * Deletes the value for the symbol from the instance variable table. * * Parameters * t the variable table to be searched. * sym the symbol to be used as the key. * vp the value pointer. Receive the deleted value if the symbol is * contained in the instance variable table. * Returns * true if the specified symbol is contained in the instance variable table. */ static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { segment *seg; size_t i; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; if (!seg->next && i >= t->last_len) { return FALSE; } if (key == sym) { t->size--; seg->key[i] = 0; if (vp) *vp = seg->val[i]; return TRUE; } } seg = seg->next; } return FALSE; } static mrb_bool iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) { segment *seg; size_t i; int n; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; /* no value in last segment after last_len */ if (!seg->next && i >= t->last_len) { return FALSE; } if (key != 0) { n =(*func)(mrb, key, seg->val[i], p); if (n > 0) return FALSE; if (n < 0) { t->size--; seg->key[i] = 0; } } } seg = seg->next; } return TRUE; } static size_t iv_size(mrb_state *mrb, iv_tbl *t) { segment *seg; size_t size = 0; if (!t) return 0; if (t->size > 0) return t->size; seg = t->rootseg; while (seg) { if (seg->next == NULL) { size += t->last_len; return size; } seg = seg->next; size += MRB_SEGMENT_SIZE; } /* empty iv_tbl */ return 0; } static iv_tbl* iv_copy(mrb_state *mrb, iv_tbl *t) { segment *seg; iv_tbl *t2; size_t i; seg = t->rootseg; t2 = iv_new(mrb); while (seg != NULL) { for (i=0; ikey[i]; mrb_value val = seg->val[i]; if ((seg->next == NULL) && (i >= t->last_len)) { return t2; } iv_put(mrb, t2, key, val); } seg = seg->next; } return t2; } static void iv_free(mrb_state *mrb, iv_tbl *t) { segment *seg; seg = t->rootseg; while (seg) { segment *p = seg; seg = seg->next; mrb_free(mrb, p); } mrb_free(mrb, t); } #else #include #ifndef MRB_IVHASH_INIT_SIZE #define MRB_IVHASH_INIT_SIZE 8 #endif KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE) KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal) typedef struct iv_tbl { khash_t(iv) h; } iv_tbl; static iv_tbl* iv_new(mrb_state *mrb) { return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE); } static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) { khash_t(iv) *h = &t->h; khiter_t k; k = kh_put(iv, mrb, h, sym); kh_value(h, k) = val; } static mrb_bool iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { khash_t(iv) *h = &t->h; khiter_t k; k = kh_get(iv, mrb, h, sym); if (k != kh_end(h)) { if (vp) *vp = kh_value(h, k); return TRUE; } return FALSE; } static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { khash_t(iv) *h = &t->h; khiter_t k; if (h) { k = kh_get(iv, mrb, h, sym); if (k != kh_end(h)) { mrb_value val = kh_value(h, k); kh_del(iv, mrb, h, k); if (vp) *vp = val; return TRUE; } } return FALSE; } static mrb_bool iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) { khash_t(iv) *h = &t->h; khiter_t k; int n; if (h) { for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p); if (n > 0) return FALSE; if (n < 0) { kh_del(iv, mrb, h, k); } } } } return TRUE; } static size_t iv_size(mrb_state *mrb, iv_tbl *t) { khash_t(iv) *h; if (t && (h = &t->h)) { return kh_size(h); } return 0; } static iv_tbl* iv_copy(mrb_state *mrb, iv_tbl *t) { return (iv_tbl*)kh_copy(iv, mrb, &t->h); } static void iv_free(mrb_state *mrb, iv_tbl *t) { kh_destroy(iv, mrb, &t->h); } #endif static int iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_gc_mark_value(mrb, v); return 0; } static void mark_tbl(mrb_state *mrb, iv_tbl *t) { if (t) { iv_foreach(mrb, t, iv_mark_i, 0); } } void mrb_gc_mark_gv(mrb_state *mrb) { mark_tbl(mrb, mrb->globals); } void mrb_gc_free_gv(mrb_state *mrb) { if (mrb->globals) iv_free(mrb, mrb->globals); } void mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj) { mark_tbl(mrb, obj->iv); } size_t mrb_gc_mark_iv_size(mrb_state *mrb, struct RObject *obj) { return iv_size(mrb, obj->iv); } void mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj) { if (obj->iv) { iv_free(mrb, obj->iv); } } mrb_value mrb_vm_special_get(mrb_state *mrb, mrb_sym i) { return mrb_fixnum_value(0); } void mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v) { } static mrb_bool obj_iv_p(mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: case MRB_TT_EXCEPTION: return TRUE; default: return FALSE; } } MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { mrb_value v; if (obj->iv && iv_get(mrb, obj->iv, sym, &v)) return v; return mrb_nil_value(); } MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym); } return mrb_nil_value(); } MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { iv_tbl *t = obj->iv; if (!t) { t = obj->iv = iv_new(mrb); } mrb_write_barrier(mrb, (struct RBasic*)obj); iv_put(mrb, t, sym, v); } MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { iv_tbl *t = obj->iv; if (!t) { t = obj->iv = iv_new(mrb); } else if (iv_get(mrb, t, sym, &v)) { return; } mrb_write_barrier(mrb, (struct RBasic*)obj); iv_put(mrb, t, sym, v); } MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) { if (obj_iv_p(obj)) { mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable"); } } MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { iv_tbl *t; t = obj->iv; if (t) { return iv_get(mrb, t, sym, NULL); } return FALSE; } MRB_API mrb_bool mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (!obj_iv_p(obj)) return FALSE; return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym); } #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) MRB_API mrb_bool mrb_iv_p(mrb_state *mrb, mrb_sym iv_name) { const char *s; mrb_int i, len; s = mrb_sym2name_len(mrb, iv_name, &len); if (len < 2) return FALSE; if (s[0] != '@') return FALSE; if (s[1] == '@') return FALSE; for (i=1; iiv) { iv_free(mrb, d->iv); d->iv = 0; } if (s->iv) { mrb_write_barrier(mrb, (struct RBasic*)d); d->iv = iv_copy(mrb, s->iv); } } static int inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value str = *(mrb_value*)p; const char *s; mrb_int len; mrb_value ins; char *sp = RSTRING_PTR(str); /* need not to show internal data */ if (sp[0] == '-') { /* first element */ sp[0] = '#'; mrb_str_cat_lit(mrb, str, " "); } else { mrb_str_cat_lit(mrb, str, ", "); } s = mrb_sym2name_len(mrb, sym, &len); mrb_str_cat(mrb, str, s, len); mrb_str_cat_lit(mrb, str, "="); if (mrb_type(v) == MRB_TT_OBJECT) { ins = mrb_any_to_s(mrb, v); } else { ins = mrb_inspect(mrb, v); } mrb_str_cat_str(mrb, str, ins); return 0; } mrb_value mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) { iv_tbl *t = obj->iv; size_t len = iv_size(mrb, t); if (len > 0) { const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj)); mrb_value str = mrb_str_buf_new(mrb, 30); mrb_str_cat_lit(mrb, str, "-<"); mrb_str_cat_cstr(mrb, str, cn); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj)); iv_foreach(mrb, t, inspect_i, &str); mrb_str_cat_lit(mrb, str, ">"); return str; } return mrb_any_to_s(mrb, mrb_obj_value(obj)); } MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { iv_tbl *t = mrb_obj_ptr(obj)->iv; mrb_value val; if (t && iv_del(mrb, t, sym, &val)) { return val; } } return mrb_undef_value(); } mrb_value mrb_vm_iv_get(mrb_state *mrb, mrb_sym sym) { /* get self */ return mrb_iv_get(mrb, mrb->c->stack[0], sym); } void mrb_vm_iv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { /* get self */ mrb_iv_set(mrb, mrb->c->stack[0], sym, v); } static int iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len > 1 && s[0] == '@' && s[1] != '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.3.1.3.23 */ /* * call-seq: * obj.instance_variables -> array * * Returns an array of instance variable names for the receiver. Note * that simply defining an accessor does not create the corresponding * instance variable. * * class Fred * attr_accessor :a1 * def initialize * @iv = 3 * end * end * Fred.new.instance_variables #=> [:@iv] */ mrb_value mrb_obj_instance_variables(mrb_state *mrb, mrb_value self) { mrb_value ary; ary = mrb_ary_new(mrb); if (obj_iv_p(self) && mrb_obj_ptr(self)->iv) { iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary); } return ary; } static int cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len > 2 && s[0] == '@' && s[1] == '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.2.2.4.19 */ /* * call-seq: * mod.class_variables -> array * * Returns an array of the names of class variables in mod. * * class One * @@var1 = 1 * end * class Two < One * @@var2 = 2 * end * One.class_variables #=> [:@@var1] * Two.class_variables #=> [:@@var2] */ mrb_value mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) { mrb_value ary; struct RClass *c; ary = mrb_ary_new(mrb); c = mrb_class_ptr(mod); while (c) { if (c->iv) { iv_foreach(mrb, c->iv, cv_i, &ary); } c = c->super; } return ary; } MRB_API mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym) { struct RClass * cls = c; mrb_value v; while (c) { if (c->iv && iv_get(mrb, c->iv, sym, &v)) { return v; } c = c->super; } if (cls && cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, mrb_intern_lit(mrb, "__attached__")); c = mrb_class_ptr(klass); if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { while (c) { if (c->iv && iv_get(mrb, c->iv, sym, &v)) { return v; } c = c->super; } } } mrb_name_error(mrb, sym, "uninitialized class variable %S in %S", mrb_sym2str(mrb, sym), mrb_obj_value(cls)); /* not reached */ return mrb_nil_value(); } MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym); } MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) { struct RClass * cls = c; while (c) { if (c->iv) { iv_tbl *t = c->iv; if (iv_get(mrb, t, sym, NULL)) { mrb_write_barrier(mrb, (struct RBasic*)c); iv_put(mrb, t, sym, v); return; } } c = c->super; } if (!cls->iv) { cls->iv = iv_new(mrb); } mrb_write_barrier(mrb, (struct RBasic*)cls); iv_put(mrb, cls->iv, sym, v); } MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v); } MRB_API mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) { while (c) { if (c->iv) { iv_tbl *t = c->iv; if (iv_get(mrb, t, sym, NULL)) return TRUE; } c = c->super; } return FALSE; } MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym); } mrb_value mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c = mrb->c->ci->proc->target_class; if (!c) c = mrb->c->ci->target_class; return mrb_mod_cv_get(mrb, c, sym); } void mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c = mrb->c->ci->proc->target_class; if (!c) c = mrb->c->ci->target_class; mrb_mod_cv_set(mrb, c, sym, v); } static void mod_const_check(mrb_state *mrb, mrb_value mod) { switch (mrb_type(mod)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module"); break; } } static mrb_value const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym) { struct RClass *c = base; mrb_value v; iv_tbl *t; mrb_bool retry = FALSE; mrb_value name; L_RETRY: while (c) { if (c->iv) { t = c->iv; if (iv_get(mrb, t, sym, &v)) return v; } c = c->super; } if (!retry && base && base->tt == MRB_TT_MODULE) { c = mrb->object_class; retry = TRUE; goto L_RETRY; } name = mrb_symbol_value(sym); return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name); } MRB_API mrb_value mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); return const_get(mrb, mrb_class_ptr(mod), sym); } mrb_value mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c = mrb->c->ci->proc->target_class; if (!c) c = mrb->c->ci->target_class; if (c) { struct RClass *c2; mrb_value v; if (c->iv && iv_get(mrb, c->iv, sym, &v)) { return v; } if (c->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject *)c, mrb_intern_lit(mrb, "__attached__")); c2 = mrb_class_ptr(klass); if (c2->tt == MRB_TT_CLASS) c = c2; } c2 = c; for (;;) { c2 = mrb_class_outer_module(mrb, c2); if (!c2) break; if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) { return v; } } } return const_get(mrb, c, sym); } MRB_API void mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mod_const_check(mrb, mod); mrb_iv_set(mrb, mod, sym, v); } void mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c = mrb->c->ci->proc->target_class; if (!c) c = mrb->c->ci->target_class; mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v); } MRB_API void mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); mrb_iv_remove(mrb, mod, sym); } MRB_API void mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v) { mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v); } MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val) { mrb_define_const(mrb, mrb->object_class, name, val); } static int const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len >= 1 && ISUPPER(s[0])) { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.2.2.4.24 */ /* * call-seq: * mod.constants -> array * * Returns an array of all names of contants defined in the receiver. */ mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod) { mrb_value ary; mrb_bool inherit = TRUE; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); while (c) { if (c->iv) { iv_foreach(mrb, c->iv, const_i, &ary); } if (!inherit) break; c = c->super; if (c == mrb->object_class) break; } return ary; } MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym) { mrb_value v; if (!mrb->globals) { return mrb_nil_value(); } if (iv_get(mrb, mrb->globals, sym, &v)) return v; return mrb_nil_value(); } MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { iv_tbl *t; if (!mrb->globals) { t = mrb->globals = iv_new(mrb); } else { t = mrb->globals; } iv_put(mrb, t, sym, v); } MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym) { if (!mrb->globals) { return; } iv_del(mrb, mrb->globals, sym, NULL); } static int gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; ary = *(mrb_value*)p; mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); return 0; } /* 15.3.1.2.4 */ /* 15.3.1.3.14 */ /* * call-seq: * global_variables -> array * * Returns an array of the names of global variables. * * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr] */ mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self) { iv_tbl *t = mrb->globals; mrb_value ary = mrb_ary_new(mrb); size_t i; char buf[3]; if (t) { iv_foreach(mrb, t, gv_i, &ary); } buf[0] = '$'; buf[2] = 0; for (i = 1; i <= 9; ++i) { buf[1] = (char)(i + '0'); mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2))); } return ary; } static mrb_bool mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse) { struct RClass *klass = mrb_class_ptr(mod); struct RClass *tmp; mrb_bool mod_retry = 0; tmp = klass; retry: while (tmp) { if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) { return TRUE; } if (!recurse && (klass != mrb->object_class)) break; tmp = tmp->super; } if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) { mod_retry = 1; tmp = mrb->object_class; goto retry; } return FALSE; } MRB_API mrb_bool mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id) { return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE); } MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id) { return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE); } MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id) { return mrb_iv_get(mrb, obj, id); } struct csym_arg { struct RClass *c; mrb_sym sym; }; static int csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { struct csym_arg *a = (struct csym_arg*)p; struct RClass *c = a->c; if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) { a->sym = sym; return 1; /* stop iteration */ } return 0; } mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer) { mrb_value name; name = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__")); if (mrb_nil_p(name)) { if (!outer) return 0; else { struct csym_arg arg; arg.c = c; arg.sym = 0; iv_foreach(mrb, outer->iv, csym_i, &arg); return arg.sym; } } return mrb_symbol(name); } mruby-1.2.0+20160315+git4f20d58a/src/version.c000066400000000000000000000015321267140355100200270ustar00rootroot00000000000000#include #include void mrb_init_version(mrb_state* mrb) { mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION); mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version); mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION)); mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO)); mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE)); mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION)); mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT)); } mruby-1.2.0+20160315+git4f20d58a/src/vm.c000066400000000000000000001727541267140355100170030ustar00rootroot00000000000000/* ** vm.c - virtual machine for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "value_array.h" #include #ifndef MRB_DISABLE_STDIO #if defined(__cplusplus) extern "C" { #endif void abort(void); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif #define STACK_INIT_SIZE 128 #define CALLINFO_INIT_SIZE 32 /* Define amount of linear stack growth. */ #ifndef MRB_STACK_GROWTH #define MRB_STACK_GROWTH 128 #endif /* Maximum stack depth. Should be set lower on memory constrained systems. The value below allows about 60000 recursive calls in the simplest case. */ #ifndef MRB_STACK_MAX #define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH) #endif #ifdef VM_DEBUG # define DEBUG(x) (x) #else # define DEBUG(x) #endif #define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai) static inline void stack_clear(mrb_value *from, size_t count) { #ifndef MRB_NAN_BOXING const mrb_value mrb_value_zero = { { 0 } }; while (count-- > 0) { *from++ = mrb_value_zero; } #else while (count-- > 0) { SET_NIL_VALUE(*from); from++; } #endif } static inline void stack_copy(mrb_value *dst, const mrb_value *src, size_t size) { while (size-- > 0) { *dst++ = *src++; } } static void stack_init(mrb_state *mrb) { struct mrb_context *c = mrb->c; /* mrb_assert(mrb->stack == NULL); */ c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value)); c->stend = c->stbase + STACK_INIT_SIZE; c->stack = c->stbase; /* mrb_assert(ci == NULL); */ c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo)); c->ciend = c->cibase + CALLINFO_INIT_SIZE; c->ci = c->cibase; c->ci->target_class = mrb->object_class; c->ci->stackent = c->stack; } static inline void envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) { mrb_callinfo *ci = mrb->c->cibase; if (newbase == oldbase) return; while (ci <= mrb->c->ci) { struct REnv *e = ci->env; if (e && MRB_ENV_STACK_SHARED_P(e)) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; } ci->stackent = newbase + (ci->stackent - oldbase); ci++; } } static inline void init_new_stack_space(mrb_state *mrb, int room, int keep) { if (room > keep) { /* do not leave uninitialized malloc region */ stack_clear(&(mrb->c->stack[keep]), room - keep); } } /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ static void stack_extend_alloc(mrb_state *mrb, int room, int keep) { mrb_value *oldbase = mrb->c->stbase; int size = mrb->c->stend - mrb->c->stbase; int off = mrb->c->stack - mrb->c->stbase; #ifdef MRB_STACK_EXTEND_DOUBLING if (room <= size) size *= 2; else size += room; #else /* Use linear stack growth. It is slightly slower than doubling the stack space, but it saves memory on small devices. */ if (room <= MRB_STACK_GROWTH) size += MRB_STACK_GROWTH; else size += room; #endif mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); mrb->c->stack = mrb->c->stbase + off; mrb->c->stend = mrb->c->stbase + size; envadjust(mrb, oldbase, mrb->c->stbase); /* Raise an exception if the new stack size will be too large, to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { init_new_stack_space(mrb, room, keep); mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")"); } } static inline void stack_extend(mrb_state *mrb, int room, int keep) { if (mrb->c->stack + room >= mrb->c->stend) { stack_extend_alloc(mrb, room, keep); } init_new_stack_space(mrb, room, keep); } static inline struct REnv* uvenv(mrb_state *mrb, int up) { struct REnv *e = mrb->c->ci->proc->env; while (up--) { if (!e) return NULL; e = (struct REnv*)e->c; } return e; } static inline mrb_bool is_strict(mrb_state *mrb, struct REnv *e) { int cioff = e->cioff; if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc && MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) { return TRUE; } return FALSE; } static inline struct REnv* top_env(mrb_state *mrb, struct RProc *proc) { struct REnv *e = proc->env; if (is_strict(mrb, e)) return e; while (e->c) { e = (struct REnv*)e->c; if (is_strict(mrb, e)) return e; } return e; } #define CI_ACC_SKIP -1 #define CI_ACC_DIRECT -2 #define CI_ACC_RESUMED -3 static mrb_callinfo* cipush(mrb_state *mrb) { struct mrb_context *c = mrb->c; mrb_callinfo *ci = c->ci; int eidx = ci->eidx; int ridx = ci->ridx; if (ci + 1 == c->ciend) { ptrdiff_t size = ci - c->cibase; c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); c->ci = c->cibase + size; c->ciend = c->cibase + size * 2; } ci = ++c->ci; ci->eidx = eidx; ci->ridx = ridx; ci->env = 0; ci->pc = 0; ci->err = 0; ci->proc = 0; return ci; } MRB_API void mrb_env_unshare(mrb_state *mrb, struct REnv *e) { size_t len = (size_t)MRB_ENV_STACK_LEN(e); mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); MRB_ENV_UNSHARE_STACK(e); if (len > 0) { stack_copy(p, e->stack, len); } e->stack = p; mrb_write_barrier(mrb, (struct RBasic *)e); } static void cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; struct REnv *env = c->ci->env; c->ci--; if (env) { mrb_env_unshare(mrb, env); } } void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void ecall(mrb_state *mrb, int i) { struct RProc *p; mrb_callinfo *ci; mrb_value *self = mrb->c->stack; struct RObject *exc; if (i<0) return; p = mrb->c->ensure[i]; if (!p) return; if (mrb->c->ci->eidx > i) mrb->c->ci->eidx = i; ci = cipush(mrb); ci->stackent = mrb->c->stack; ci->mid = ci[-1].mid; ci->acc = CI_ACC_SKIP; ci->argc = 0; ci->proc = p; ci->nregs = p->body.irep->nregs; ci->target_class = p->target_class; mrb->c->stack = mrb->c->stack + ci[-1].nregs; exc = mrb->exc; mrb->exc = 0; mrb_run(mrb, p, *self); mrb->c->ensure[i] = NULL; if (!mrb->exc) mrb->exc = exc; } #ifndef MRB_FUNCALL_ARGC_MAX #define MRB_FUNCALL_ARGC_MAX 16 #endif MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) { mrb_value argv[MRB_FUNCALL_ARGC_MAX]; va_list ap; mrb_int i; mrb_sym mid = mrb_intern_cstr(mrb, name); if (argc > MRB_FUNCALL_ARGC_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); } va_start(ap, argc); for (i = 0; i < argc; i++) { argv[i] = va_arg(ap, mrb_value); } va_end(ap); return mrb_funcall_argv(mrb, self, mid, argc, argv); } MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk) { mrb_value val; if (!mrb->jmp) { struct mrb_jmpbuf c_jmp; ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; /* recursive call */ val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk); mrb->jmp = 0; } MRB_CATCH(&c_jmp) { /* error */ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } mrb->jmp = 0; val = mrb_obj_value(mrb->exc); } MRB_END_EXC(&c_jmp); } else { struct RProc *p; struct RClass *c; mrb_sym undef = 0; mrb_callinfo *ci; int n; ptrdiff_t voff = -1; if (!mrb->c->stack) { stack_init(mrb); } n = mrb->c->ci->nregs; if (argc < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc)); } c = mrb_class(mrb, self); p = mrb_method_search_vm(mrb, &c, mid); if (!p) { undef = mid; mid = mrb_intern_lit(mrb, "method_missing"); p = mrb_method_search_vm(mrb, &c, mid); n++; argc++; } ci = cipush(mrb); ci->mid = mid; ci->proc = p; ci->stackent = mrb->c->stack; ci->argc = argc; ci->target_class = c; mrb->c->stack = mrb->c->stack + n; if (mrb->c->stbase <= argv && argv < mrb->c->stend) { voff = argv - mrb->c->stbase; } if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; stack_extend(mrb, ci->nregs, 0); } else { ci->nregs = p->body.irep->nregs + n; stack_extend(mrb, ci->nregs, argc+2); } if (voff >= 0) { argv = mrb->c->stbase + voff; } mrb->c->stack[0] = self; if (undef) { mrb->c->stack[1] = mrb_symbol_value(undef); if (argc > 1) { stack_copy(mrb->c->stack+2, argv, argc-1); } } else if (argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = blk; if (MRB_PROC_CFUNC_P(p)) { int ai = mrb_gc_arena_save(mrb); ci->acc = CI_ACC_DIRECT; val = p->body.func(mrb, self); mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); mrb_gc_arena_restore(mrb, ai); } else { ci->acc = CI_ACC_SKIP; val = mrb_run(mrb, p, self); } } mrb_gc_protect(mrb, val); return val; } MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv) { return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /* * call-seq: * obj.send(symbol [, args...]) -> obj * obj.__send__(symbol [, args...]) -> obj * * Invokes the method identified by _symbol_, passing it any * arguments specified. You can use __send__ if the name * +send+ clashes with an existing method in _obj_. * * class Klass * def hello(*args) * "Hello " + args.join(' ') * end * end * k = Klass.new * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" */ MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_value block, *argv, *regs; mrb_int argc, i, len; struct RProc *p; struct RClass *c; mrb_callinfo *ci; mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); c = mrb_class(mrb, self); p = mrb_method_search_vm(mrb, &c, name); if (!p) { /* call method_mising */ return mrb_funcall_with_block(mrb, self, name, argc, argv, block); } ci = mrb->c->ci; ci->mid = name; ci->target_class = c; ci->proc = p; regs = mrb->c->stack+1; /* remove first symbol from arguments */ if (ci->argc >= 0) { for (i=0,len=ci->argc; iargc--; } else { /* variable length arguments */ mrb_ary_shift(mrb, regs[0]); } if (MRB_PROC_CFUNC_P(p)) { return p->body.func(mrb, self); } if (ci->argc < 0) { stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3); } else { stack_extend(mrb, p->body.irep->nregs, ci->argc+2); } ci->nregs = p->body.irep->nregs; ci = cipush(mrb); ci->nregs = 0; ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; ci->acc = 0; return self; } static mrb_value eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) { struct RProc *p; mrb_callinfo *ci; mrb_int max = 3; if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } ci = mrb->c->ci; if (ci->acc == CI_ACC_DIRECT) { return mrb_yield_with_class(mrb, blk, 1, &self, self, c); } ci->target_class = c; p = mrb_proc_ptr(blk); ci->proc = p; ci->argc = 1; if (MRB_PROC_CFUNC_P(p)) { stack_extend(mrb, 3, 0); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); return p->body.func(mrb, self); } ci->nregs = p->body.irep->nregs; if (max < ci->nregs) max = ci->nregs; stack_extend(mrb, max, 0); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); ci = cipush(mrb); ci->nregs = 0; ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; ci->acc = 0; return self; } /* 15.2.2.4.35 */ /* * call-seq: * mod.class_eval {| | block } -> obj * mod.module_eval {| | block } -> obj * * Evaluates block in the context of _mod_. This can * be used to add methods to a class. module_eval returns * the result of evaluating its argument. */ mrb_value mrb_mod_module_eval(mrb_state *mrb, mrb_value mod) { mrb_value a, b; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented"); } return eval_under(mrb, mod, b, mrb_class_ptr(mod)); } /* 15.3.1.3.18 */ /* * call-seq: * obj.instance_eval {| | block } -> obj * * Evaluates 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. In the version of instance_eval * that takes a +String+, the optional second and third * parameters supply a filename and starting line number that are used * when reporting compilation errors. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_eval { @secret } #=> 99 */ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value a, b; mrb_value cv; struct RClass *c; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented"); } switch (mrb_type(self)) { case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: case MRB_TT_FLOAT: c = 0; break; default: cv = mrb_singleton_class(mrb, self); c = mrb_class_ptr(cv); break; } return eval_under(mrb, self, b, c); } MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c) { struct RProc *p; mrb_sym mid = mrb->c->ci->mid; mrb_callinfo *ci; int n = mrb->c->ci->nregs; mrb_value val; if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } p = mrb_proc_ptr(b); ci = cipush(mrb); ci->mid = mid; ci->proc = p; ci->stackent = mrb->c->stack; ci->argc = argc; ci->target_class = c; ci->acc = CI_ACC_SKIP; mrb->c->stack = mrb->c->stack + n; if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; stack_extend(mrb, ci->nregs, 0); } else { ci->nregs = p->body.irep->nregs; stack_extend(mrb, ci->nregs, argc+2); } mrb->c->stack[0] = self; if (argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = mrb_nil_value(); if (MRB_PROC_CFUNC_P(p)) { val = p->body.func(mrb, self); mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } else { val = mrb_run(mrb, p, self); } return val; } MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class); } MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class); } typedef enum { LOCALJUMP_ERROR_RETURN = 0, LOCALJUMP_ERROR_BREAK = 1, LOCALJUMP_ERROR_YIELD = 2 } localjump_error_kind; static void localjump_error(mrb_state *mrb, localjump_error_kind kind) { char kind_str[3][7] = { "return", "break", "yield" }; char kind_str_len[] = { 6, 5, 5 }; static const char lead[] = "unexpected "; mrb_value msg; mrb_value exc; msg = mrb_str_buf_new(mrb, sizeof(lead) + 7); mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1); mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]); exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); mrb_exc_set(mrb, exc); } static void argnum_error(mrb_state *mrb, mrb_int num) { mrb_value exc; mrb_value str; if (mrb->c->ci->mid) { str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", mrb_sym2str(mrb, mrb->c->ci->mid), mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); } else { str = mrb_format(mrb, "wrong number of arguments (%S for %S)", mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); } exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); mrb_exc_set(mrb, exc); } #define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; #define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; #ifdef MRB_ENABLE_DEBUG_HOOK #define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs)); #else #define CODE_FETCH_HOOK(mrb, irep, pc, regs) #endif #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER #define DIRECT_THREADED #endif #ifndef DIRECT_THREADED #define INIT_DISPATCH for (;;) { i = *pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) { #define CASE(op) case op: #define NEXT pc++; break #define JUMP break #define END_DISPATCH }} #else #define INIT_DISPATCH JUMP; return mrb_nil_value(); #define CASE(op) L_ ## op: #define NEXT i=*++pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] #define JUMP i=*pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] #define END_DISPATCH #endif #define CALL_MAXARGS 127 void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); MRB_API mrb_value mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { mrb_irep *irep = proc->body.irep; if (!mrb->c->stack) { stack_init(mrb); } stack_extend(mrb, irep->nregs, stack_keep); mrb->c->stack[0] = self; return mrb_vm_exec(mrb, proc, irep->iseq); } MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) { /* mrb_assert(mrb_proc_cfunc_p(proc)) */ mrb_irep *irep = proc->body.irep; mrb_value *pool = irep->pool; mrb_sym *syms = irep->syms; mrb_value *regs = NULL; mrb_code i; int ai = mrb_gc_arena_save(mrb); struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; #ifdef DIRECT_THREADED static void *optable[] = { &&L_OP_NOP, &&L_OP_MOVE, &&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL, &&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF, &&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL, &&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV, &&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST, &&L_OP_GETUPVAR, &&L_OP_SETUPVAR, &&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT, &&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP, &&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND, &&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER, &&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH, &&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV, &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE, &&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST, &&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH, &&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS, &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC, &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS, &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR, }; #endif mrb_bool exc_catched = FALSE; RETRY_TRY_BLOCK: MRB_TRY(&c_jmp) { if (exc_catched) { exc_catched = FALSE; goto L_RAISE; } mrb->jmp = &c_jmp; mrb->c->ci->proc = proc; mrb->c->ci->nregs = irep->nregs; regs = mrb->c->stack; INIT_DISPATCH { CASE(OP_NOP) { /* do nothing */ NEXT; } CASE(OP_MOVE) { /* A B R(A) := R(B) */ regs[GETARG_A(i)] = regs[GETARG_B(i)]; NEXT; } CASE(OP_LOADL) { /* A Bx R(A) := Pool(Bx) */ regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; NEXT; } CASE(OP_LOADI) { /* A sBx R(A) := sBx */ SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i)); NEXT; } CASE(OP_LOADSYM) { /* A Bx R(A) := Syms(Bx) */ SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]); NEXT; } CASE(OP_LOADSELF) { /* A R(A) := self */ regs[GETARG_A(i)] = regs[0]; NEXT; } CASE(OP_LOADT) { /* A R(A) := true */ SET_TRUE_VALUE(regs[GETARG_A(i)]); NEXT; } CASE(OP_LOADF) { /* A R(A) := false */ SET_FALSE_VALUE(regs[GETARG_A(i)]); NEXT; } CASE(OP_GETGLOBAL) { /* A Bx R(A) := getglobal(Syms(Bx)) */ regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]); NEXT; } CASE(OP_SETGLOBAL) { /* setglobal(Syms(Bx), R(A)) */ mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETSPECIAL) { /* A Bx R(A) := Special[Bx] */ regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i)); NEXT; } CASE(OP_SETSPECIAL) { /* A Bx Special[Bx] := R(A) */ mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]); NEXT; } CASE(OP_GETIV) { /* A Bx R(A) := ivget(Bx) */ regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]); NEXT; } CASE(OP_SETIV) { /* ivset(Syms(Bx),R(A)) */ mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETCV) { /* A Bx R(A) := cvget(Syms(Bx)) */ ERR_PC_SET(mrb, pc); regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]); ERR_PC_CLR(mrb); NEXT; } CASE(OP_SETCV) { /* cvset(Syms(Bx),R(A)) */ mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETCONST) { /* A Bx R(A) := constget(Syms(Bx)) */ mrb_value val; ERR_PC_SET(mrb, pc); val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]); ERR_PC_CLR(mrb); regs = mrb->c->stack; regs[GETARG_A(i)] = val; NEXT; } CASE(OP_SETCONST) { /* A Bx constset(Syms(Bx),R(A)) */ mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETMCNST) { /* A Bx R(A) := R(A)::Syms(Bx) */ mrb_value val; int a = GETARG_A(i); ERR_PC_SET(mrb, pc); val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]); ERR_PC_CLR(mrb); regs = mrb->c->stack; regs[a] = val; NEXT; } CASE(OP_SETMCNST) { /* A Bx R(A+1)::Syms(Bx) := R(A) */ int a = GETARG_A(i); mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]); NEXT; } CASE(OP_GETUPVAR) { /* A B C R(A) := uvget(B,C) */ mrb_value *regs_a = regs + GETARG_A(i); int up = GETARG_C(i); struct REnv *e = uvenv(mrb, up); if (!e) { *regs_a = mrb_nil_value(); } else { int idx = GETARG_B(i); *regs_a = e->stack[idx]; } NEXT; } CASE(OP_SETUPVAR) { /* A B C uvset(B,C,R(A)) */ int up = GETARG_C(i); struct REnv *e = uvenv(mrb, up); if (e) { mrb_value *regs_a = regs + GETARG_A(i); int idx = GETARG_B(i); e->stack[idx] = *regs_a; mrb_write_barrier(mrb, (struct RBasic*)e); } NEXT; } CASE(OP_JMP) { /* sBx pc+=sBx */ pc += GETARG_sBx(i); JUMP; } CASE(OP_JMPIF) { /* A sBx if R(A) pc+=sBx */ if (mrb_test(regs[GETARG_A(i)])) { pc += GETARG_sBx(i); JUMP; } NEXT; } CASE(OP_JMPNOT) { /* A sBx if !R(A) pc+=sBx */ if (!mrb_test(regs[GETARG_A(i)])) { pc += GETARG_sBx(i); JUMP; } NEXT; } CASE(OP_ONERR) { /* sBx pc+=sBx on exception */ if (mrb->c->rsize <= mrb->c->ci->ridx) { if (mrb->c->rsize == 0) mrb->c->rsize = 16; else mrb->c->rsize *= 2; mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize); } mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i); NEXT; } CASE(OP_RESCUE) { /* A R(A) := exc; clear(exc) */ SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc); mrb->exc = 0; NEXT; } CASE(OP_POPERR) { /* A A.times{rescue_pop()} */ int a = GETARG_A(i); while (a--) { mrb->c->ci->ridx--; } NEXT; } CASE(OP_RAISE) { /* A raise(R(A)) */ mrb_exc_set(mrb, regs[GETARG_A(i)]); goto L_RAISE; } CASE(OP_EPUSH) { /* Bx ensure_push(SEQ[Bx]) */ struct RProc *p; p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]); /* push ensure_stack */ if (mrb->c->esize <= mrb->c->ci->eidx) { if (mrb->c->esize == 0) mrb->c->esize = 16; else mrb->c->esize *= 2; mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize); } mrb->c->ensure[mrb->c->ci->eidx++] = p; ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_EPOP) { /* A A.times{ensure_pop().call} */ int a = GETARG_A(i); mrb_callinfo *ci = mrb->c->ci; int n, eidx = ci->eidx; for (n=0; nc->cibase || eidx > ci[-1].eidx); n++) { ecall(mrb, --eidx); ARENA_RESTORE(mrb, ai); } NEXT; } CASE(OP_LOADNIL) { /* A R(A) := nil */ int a = GETARG_A(i); SET_NIL_VALUE(regs[a]); NEXT; } CASE(OP_SENDB) { /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/ /* fall through */ }; L_SEND: CASE(OP_SEND) { /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */ int a = GETARG_A(i); int n = GETARG_C(i); struct RProc *m; struct RClass *c; mrb_callinfo *ci; mrb_value recv, result; mrb_sym mid = syms[GETARG_B(i)]; recv = regs[a]; if (GET_OPCODE(i) != OP_SENDB) { if (n == CALL_MAXARGS) { SET_NIL_VALUE(regs[a+2]); } else { SET_NIL_VALUE(regs[a+n+1]); } } c = mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value sym = mrb_symbol_value(mid); mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); m = mrb_method_search_vm(mrb, &c, missing); if (!m) { mrb_value args; if (n == CALL_MAXARGS) { args = regs[a+1]; } else { args = mrb_ary_new_from_values(mrb, n, regs+a+1); } mrb_method_missing(mrb, mid, recv, args); } mid = missing; if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } else { value_move(regs+a+2, regs+a+1, ++n); regs[a+1] = sym; } } /* push callinfo */ ci = cipush(mrb); ci->mid = mid; ci->proc = m; ci->stackent = mrb->c->stack; ci->target_class = c; ci->pc = pc + 1; ci->acc = a; /* prepare stack */ mrb->c->stack += a; if (MRB_PROC_CFUNC_P(m)) { if (n == CALL_MAXARGS) { ci->argc = -1; ci->nregs = 3; } else { ci->argc = n; ci->nregs = n + 2; } result = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ ci = mrb->c->ci; if (!ci->target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; return result; } else { mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); proc = ci[-1].proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; } } mrb->c->stack[0] = result; regs = mrb->c->stack = ci->stackent; pc = ci->pc; cipop(mrb); JUMP; } else { /* setup environment for calling method */ proc = mrb->c->ci->proc = m; irep = m->body.irep; pool = irep->pool; syms = irep->syms; ci->nregs = irep->nregs; if (n == CALL_MAXARGS) { ci->argc = -1; stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); } else { ci->argc = n; stack_extend(mrb, irep->nregs, n+2); } regs = mrb->c->stack; pc = irep->iseq; JUMP; } } CASE(OP_FSEND) { /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */ NEXT; } CASE(OP_CALL) { /* A R(A) := self.call(frame.argc, frame.argv) */ mrb_callinfo *ci; mrb_value recv = mrb->c->stack[0]; struct RProc *m = mrb_proc_ptr(recv); /* replace callinfo */ ci = mrb->c->ci; ci->target_class = m->target_class; ci->proc = m; if (m->env) { if (m->env->mid) { ci->mid = m->env->mid; } if (!m->env->stack) { m->env->stack = mrb->c->stack; } } /* prepare stack */ if (MRB_PROC_CFUNC_P(m)) { recv = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ ci = mrb->c->ci; regs = mrb->c->stack = ci->stackent; regs[ci->acc] = recv; pc = ci->pc; cipop(mrb); irep = mrb->c->ci->proc->body.irep; pool = irep->pool; syms = irep->syms; JUMP; } else { /* setup environment for calling method */ proc = m; irep = m->body.irep; if (!irep) { mrb->c->stack[0] = mrb_nil_value(); goto L_RETURN; } pool = irep->pool; syms = irep->syms; ci->nregs = irep->nregs; if (ci->argc < 0) { stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); } else { stack_extend(mrb, irep->nregs, ci->argc+2); } regs = mrb->c->stack; regs[0] = m->env->stack[0]; pc = irep->iseq; JUMP; } } CASE(OP_SUPER) { /* A C R(A) := super(R(A+1),... ,R(A+C+1)) */ mrb_value recv; mrb_callinfo *ci = mrb->c->ci; struct RProc *m; struct RClass *c; mrb_sym mid = ci->mid; int a = GETARG_A(i); int n = GETARG_C(i); if (mid == 0) { mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } recv = regs[0]; c = mrb->c->ci->target_class->super; m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mid = mrb_intern_lit(mrb, "method_missing"); m = mrb_method_search_vm(mrb, &c, mid); if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); } else { value_move(regs+a+2, regs+a+1, ++n); SET_SYM_VALUE(regs[a+1], ci->mid); } } /* push callinfo */ ci = cipush(mrb); ci->mid = mid; ci->proc = m; ci->stackent = mrb->c->stack; if (n == CALL_MAXARGS) { ci->argc = -1; } else { ci->argc = n; } ci->target_class = c; ci->pc = pc + 1; /* prepare stack */ mrb->c->stack += a; mrb->c->stack[0] = recv; if (MRB_PROC_CFUNC_P(m)) { if (n == CALL_MAXARGS) { ci->nregs = 3; } else { ci->nregs = n + 2; } mrb->c->stack[0] = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ regs = mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); NEXT; } else { /* fill callinfo */ ci->acc = a; /* setup environment for calling method */ ci->proc = m; irep = m->body.irep; pool = irep->pool; syms = irep->syms; ci->nregs = irep->nregs; if (n == CALL_MAXARGS) { stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); } else { stack_extend(mrb, irep->nregs, ci->argc+2); } regs = mrb->c->stack; pc = irep->iseq; JUMP; } } CASE(OP_ARGARY) { /* A Bx R(A) := argument array (16=6:1:5:4) */ int a = GETARG_A(i); int bx = GETARG_Bx(i); int m1 = (bx>>10)&0x3f; int r = (bx>>9)&0x1; int m2 = (bx>>4)&0x1f; int lv = (bx>>0)&0xf; mrb_value *stack; if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e) { mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } stack = e->stack + 1; } if (r == 0) { regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack); } else { mrb_value *pp = NULL; struct RArray *rest; int len = 0; if (mrb_array_p(stack[m1])) { struct RArray *ary = mrb_ary_ptr(stack[m1]); pp = ary->ptr; len = ary->len; } regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { stack_copy(rest->ptr, stack, m1); } if (len > 0) { stack_copy(rest->ptr+m1, pp, len); } if (m2 > 0) { stack_copy(rest->ptr+m1+len, stack+m1+1, m2); } rest->len = m1+len+m2; } regs[a+1] = stack[m1+r+m2]; ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_ENTER) { /* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */ /* number of optional arguments times OP_JMP should follow */ mrb_aspec ax = GETARG_Ax(i); int m1 = MRB_ASPEC_REQ(ax); int o = MRB_ASPEC_OPT(ax); int r = MRB_ASPEC_REST(ax); int m2 = MRB_ASPEC_POST(ax); /* unused int k = MRB_ASPEC_KEY(ax); int kd = MRB_ASPEC_KDICT(ax); int b = MRB_ASPEC_BLOCK(ax); */ int argc = mrb->c->ci->argc; mrb_value *argv = regs+1; mrb_value *argv0 = argv; int len = m1 + o + r + m2; mrb_value *blk = &argv[argc < 0 ? 1 : argc]; if (!mrb_nil_p(*blk) && mrb_type(*blk) != MRB_TT_PROC) { *blk = mrb_convert_type(mrb, *blk, MRB_TT_PROC, "Proc", "to_proc"); } if (argc < 0) { struct RArray *ary = mrb_ary_ptr(regs[1]); argv = ary->ptr; argc = ary->len; mrb_gc_protect(mrb, regs[1]); } if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { if (argc >= 0) { if (argc < m1 + m2 || (r == 0 && argc > len)) { argnum_error(mrb, m1+m2); goto L_RAISE; } } } else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); argc = mrb_ary_ptr(argv[0])->len; argv = mrb_ary_ptr(argv[0])->ptr; } mrb->c->ci->argc = len; if (argc < len) { int mlen = m2; if (argc < m1+m2) { if (m1 < argc) mlen = argc - m1; else mlen = 0; } regs[len+1] = *blk; /* move block */ SET_NIL_VALUE(regs[argc+1]); if (argv0 != argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } if (o == 0 || argc < m1+m2) pc++; else pc += argc - m1 - m2 + 1; } else { int rnum = 0; if (argv0 != argv) { regs[len+1] = *blk; /* move block */ value_move(®s[1], argv, m1+o); } if (r) { rnum = argc-m1-o-m2; regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); } if (m2) { if (argc-m2 > m1) { value_move(®s[m1+o+r+1], &argv[m1+o+rnum], m2); } } if (argv0 == argv) { regs[len+1] = *blk; /* move block */ } pc += o + 1; } JUMP; } CASE(OP_KARG) { /* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */ /* if C == 2; raise unless kdict.empty? */ /* OP_JMP should follow to skip init code */ NEXT; } CASE(OP_KDICT) { /* A C R(A) := kdict */ NEXT; } L_RETURN: i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL); /* fall through */ CASE(OP_RETURN) { /* A B return R(A) (B=normal,in-block return/break) */ if (mrb->exc) { mrb_callinfo *ci; int eidx; L_RAISE: ci = mrb->c->ci; mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc)); mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase)); eidx = ci->eidx; if (ci == mrb->c->cibase) { if (ci->ridx == 0) goto L_STOP; goto L_RESCUE; } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); ci = mrb->c->ci; mrb->c->stack = ci[1].stackent; if (ci[1].acc == CI_ACC_SKIP && prev_jmp) { mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } if (ci == mrb->c->cibase) { while (eidx > 0) { ecall(mrb, --eidx); } if (ci->ridx == 0) { if (mrb->c == mrb->root_c) { regs = mrb->c->stack = mrb->c->stbase; goto L_STOP; } else { struct mrb_context *c = mrb->c; mrb->c = c->prev; c->prev = NULL; goto L_RAISE; } } break; } /* call ensure only when we skip this callinfo */ if (ci[0].ridx == ci[-1].ridx) { while (eidx > ci[-1].eidx) { ecall(mrb, --eidx); } } } L_RESCUE: if (ci->ridx == 0) goto L_STOP; proc = ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; regs = mrb->c->stack = ci[1].stackent; pc = mrb->c->rescue[--ci->ridx]; } else { mrb_callinfo *ci = mrb->c->ci; int acc, eidx = mrb->c->ci->eidx; mrb_value v = regs[GETARG_A(i)]; switch (GETARG_B(i)) { case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ if (proc->env && !MRB_PROC_STRICT_P(proc)) { struct REnv *e = top_env(mrb, proc); if (!MRB_ENV_STACK_SHARED_P(e)) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } ci = mrb->c->cibase + e->cioff; if (ci == mrb->c->cibase) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } mrb->c->stack = mrb->c->ci->stackent; mrb->c->ci = ci; break; } case OP_R_NORMAL: if (ci == mrb->c->cibase) { if (!mrb->c->prev) { /* toplevel return */ localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } if (mrb->c->prev->ci == mrb->c->prev->cibase) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); mrb_exc_set(mrb, exc); goto L_RAISE; } /* automatic yield at the end */ mrb->c->status = MRB_FIBER_TERMINATED; mrb->c = mrb->c->prev; mrb->c->status = MRB_FIBER_RUNNING; } ci = mrb->c->ci; break; case OP_R_BREAK: if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { localjump_error(mrb, LOCALJUMP_ERROR_BREAK); goto L_RAISE; } /* break from fiber block */ if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) { struct mrb_context *c = mrb->c; mrb->c = c->prev; c->prev = NULL; } ci = mrb->c->ci; mrb->c->stack = ci->stackent; mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; while (ci > mrb->c->ci) { if (ci[-1].acc == CI_ACC_SKIP) { mrb->c->ci = ci; break; } ci--; } break; default: /* cannot happen */ break; } while (eidx > mrb->c->ci[-1].eidx) { ecall(mrb, --eidx); } if (mrb->c->vmexec && !mrb->c->ci->target_class) { mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; } cipop(mrb); acc = ci->acc; regs = mrb->c->stack = ci->stackent; if (acc == CI_ACC_SKIP) { mrb->jmp = prev_jmp; return v; } pc = ci->pc; DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid))); proc = mrb->c->ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; regs[acc] = v; } JUMP; } CASE(OP_TAILCALL) { /* A B C return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */ int a = GETARG_A(i); int n = GETARG_C(i); struct RProc *m; struct RClass *c; mrb_callinfo *ci; mrb_value recv; mrb_sym mid = syms[GETARG_B(i)]; recv = regs[a]; c = mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value sym = mrb_symbol_value(mid); mid = mrb_intern_lit(mrb, "method_missing"); m = mrb_method_search_vm(mrb, &c, mid); if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } else { value_move(regs+a+2, regs+a+1, ++n); regs[a+1] = sym; } } /* replace callinfo */ ci = mrb->c->ci; ci->mid = mid; ci->target_class = c; if (n == CALL_MAXARGS) { ci->argc = -1; } else { ci->argc = n; } /* move stack */ value_move(mrb->c->stack, ®s[a], ci->argc+1); if (MRB_PROC_CFUNC_P(m)) { mrb->c->stack[0] = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); goto L_RETURN; } else { /* setup environment for calling method */ irep = m->body.irep; pool = irep->pool; syms = irep->syms; if (ci->argc < 0) { stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); } else { stack_extend(mrb, irep->nregs, ci->argc+2); } regs = mrb->c->stack; pc = irep->iseq; } JUMP; } CASE(OP_BLKPUSH) { /* A Bx R(A) := block (16=6:1:5:4) */ int a = GETARG_A(i); int bx = GETARG_Bx(i); int m1 = (bx>>10)&0x3f; int r = (bx>>9)&0x1; int m2 = (bx>>4)&0x1f; int lv = (bx>>0)&0xf; mrb_value *stack; if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e) { localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } stack = e->stack + 1; } regs[a] = stack[m1+r+m2]; NEXT; } #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) #define OP_MATH_BODY(op,v1,v2) do {\ v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\ } while(0) CASE(OP_ADD) { /* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/ int a = GETARG_A(i); /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x, y, z; mrb_value *regs_a = regs + a; x = mrb_fixnum(regs_a[0]); y = mrb_fixnum(regs_a[1]); if (mrb_int_add_overflow(x, y, &z)) { SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); break; } SET_INT_VALUE(regs[a], z); } break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,mrb_float,mrb_float); #endif break; case TYPES2(MRB_TT_STRING,MRB_TT_STRING): regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); break; default: goto L_SEND; } ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_SUB) { /* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/ int a = GETARG_A(i); /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x, y, z; x = mrb_fixnum(regs[a]); y = mrb_fixnum(regs[a+1]); if (mrb_int_sub_overflow(x, y, &z)) { SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); break; } SET_INT_VALUE(regs[a], z); } break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,mrb_float,mrb_float); #endif break; default: goto L_SEND; } NEXT; } CASE(OP_MUL) { /* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/ int a = GETARG_A(i); /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_value z; z = mrb_fixnum_mul(mrb, regs[a], regs[a+1]); switch (mrb_type(z)) { case MRB_TT_FIXNUM: { SET_INT_VALUE(regs[a], mrb_fixnum(z)); } break; case MRB_TT_FLOAT: { SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z)); } break; default: /* cannot happen */ break; } } break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,mrb_float,mrb_float); #endif break; default: goto L_SEND; } NEXT; } CASE(OP_DIV) { /* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/ int a = GETARG_A(i); /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y); } break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x / y); } #else OP_MATH_BODY(/,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x / y); } #else OP_MATH_BODY(/,mrb_float,mrb_float); #endif break; default: goto L_SEND; } #ifdef MRB_NAN_BOXING if (isnan(mrb_float(regs[a]))) { regs[a] = mrb_float_value(mrb, mrb_float(regs[a])); } #endif NEXT; } CASE(OP_ADDI) { /* A B C R(A) := R(A)+C (Syms[B]=:+)*/ int a = GETARG_A(i); /* need to check if + is overridden */ switch (mrb_type(regs[a])) { case MRB_TT_FIXNUM: { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = GETARG_C(i); mrb_int z; if (mrb_int_add_overflow(x, y, &z)) { SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); break; } SET_INT_VALUE(regs[a], z); } break; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i)); } #else mrb_float(regs[a]) += GETARG_C(i); #endif break; default: SET_INT_VALUE(regs[a+1], GETARG_C(i)); i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1); goto L_SEND; } NEXT; } CASE(OP_SUBI) { /* A B C R(A) := R(A)-C (Syms[B]=:-)*/ int a = GETARG_A(i); mrb_value *regs_a = regs + a; /* need to check if + is overridden */ switch (mrb_type(regs_a[0])) { case MRB_TT_FIXNUM: { mrb_int x = mrb_fixnum(regs_a[0]); mrb_int y = GETARG_C(i); mrb_int z; if (mrb_int_sub_overflow(x, y, &z)) { SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); } else { SET_INT_VALUE(regs_a[0], z); } } break; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i)); } #else mrb_float(regs_a[0]) -= GETARG_C(i); #endif break; default: SET_INT_VALUE(regs_a[1], GETARG_C(i)); i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1); goto L_SEND; } NEXT; } #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) #define OP_CMP(op) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_float,mrb_float);\ break;\ default:\ goto L_SEND;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) CASE(OP_EQ) { /* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/ int a = GETARG_A(i); if (mrb_obj_eq(mrb, regs[a], regs[a+1])) { SET_TRUE_VALUE(regs[a]); } else { OP_CMP(==); } NEXT; } CASE(OP_LT) { /* A B C R(A) := R(A)R(A+1) (Syms[B]=:>,C=1)*/ int a = GETARG_A(i); OP_CMP(>); NEXT; } CASE(OP_GE) { /* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/ int a = GETARG_A(i); OP_CMP(>=); NEXT; } CASE(OP_ARRAY) { /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), ®s[GETARG_B(i)]); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_ARYCAT) { /* A B mrb_ary_concat(R(A),R(B)) */ mrb_ary_concat(mrb, regs[GETARG_A(i)], mrb_ary_splat(mrb, regs[GETARG_B(i)])); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_ARYPUSH) { /* A B R(A).push(R(B)) */ mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); NEXT; } CASE(OP_AREF) { /* A B C R(A) := R(B)[C] */ int a = GETARG_A(i); int c = GETARG_C(i); mrb_value v = regs[GETARG_B(i)]; if (!mrb_array_p(v)) { if (c == 0) { regs[GETARG_A(i)] = v; } else { SET_NIL_VALUE(regs[a]); } } else { regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c); } NEXT; } CASE(OP_ASET) { /* A B C R(B)[C] := R(A) */ mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]); NEXT; } CASE(OP_APOST) { /* A B C *R(A),R(A+1)..R(A+C) := R(A) */ int a = GETARG_A(i); mrb_value v = regs[a]; int pre = GETARG_B(i); int post = GETARG_C(i); struct RArray *ary; int len, idx; if (!mrb_array_p(v)) { v = mrb_ary_new_from_values(mrb, 1, ®s[a]); } ary = mrb_ary_ptr(v); len = ary->len; if (len > pre + post) { regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); while (post--) { regs[a++] = ary->ptr[len-post-1]; } } else { regs[a++] = mrb_ary_new_capa(mrb, 0); for (idx=0; idx+preptr[pre+idx]; } while (idx < post) { SET_NIL_VALUE(regs[a+idx]); idx++; } } ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_STRING) { /* A Bx R(A) := str_new(Lit(Bx)) */ regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_STRCAT) { /* A B R(A).concat(R(B)) */ mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); regs = mrb->c->stack; NEXT; } CASE(OP_HASH) { /* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */ int b = GETARG_B(i); int c = GETARG_C(i); int lim = b+c*2; mrb_value hash = mrb_hash_new_capa(mrb, c); while (b < lim) { mrb_hash_set(mrb, hash, regs[b], regs[b+1]); b+=2; } regs[GETARG_A(i)] = hash; ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_LAMBDA) { /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */ struct RProc *p; int c = GETARG_c(i); if (c & OP_L_CAPTURE) { p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]); } else { p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]); } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; regs[GETARG_A(i)] = mrb_obj_value(p); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_OCLASS) { /* A R(A) := ::Object */ regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class); NEXT; } CASE(OP_CLASS) { /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ struct RClass *c = 0; int a = GETARG_A(i); mrb_value base, super; mrb_sym id = syms[GETARG_B(i)]; base = regs[a]; super = regs[a+1]; if (mrb_nil_p(base)) { base = mrb_obj_value(mrb->c->ci->target_class); } c = mrb_vm_define_class(mrb, base, super, id); regs[a] = mrb_obj_value(c); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_MODULE) { /* A B R(A) := newmodule(R(A),Syms(B)) */ struct RClass *c = 0; int a = GETARG_A(i); mrb_value base; mrb_sym id = syms[GETARG_B(i)]; base = regs[a]; if (mrb_nil_p(base)) { base = mrb_obj_value(mrb->c->ci->target_class); } c = mrb_vm_define_module(mrb, base, id); regs[a] = mrb_obj_value(c); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_EXEC) { /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ int a = GETARG_A(i); mrb_callinfo *ci; mrb_value recv = regs[a]; struct RProc *p; /* prepare stack */ ci = cipush(mrb); ci->pc = pc + 1; ci->acc = a; ci->mid = 0; ci->stackent = mrb->c->stack; ci->argc = 0; ci->target_class = mrb_class_ptr(recv); /* prepare stack */ mrb->c->stack += a; p = mrb_proc_new(mrb, irep->reps[GETARG_Bx(i)]); p->target_class = ci->target_class; ci->proc = p; if (MRB_PROC_CFUNC_P(p)) { ci->nregs = 0; mrb->c->stack[0] = p->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ regs = mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); NEXT; } else { irep = p->body.irep; pool = irep->pool; syms = irep->syms; stack_extend(mrb, irep->nregs, 1); ci->nregs = irep->nregs; regs = mrb->c->stack; pc = irep->iseq; JUMP; } } CASE(OP_METHOD) { /* A B R(A).newmethod(Syms(B),R(A+1)) */ int a = GETARG_A(i); struct RClass *c = mrb_class_ptr(regs[a]); struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_SCLASS) { /* A B R(A) := R(B).singleton_class */ regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_TCLASS) { /* A R(A) := target_class */ if (!mrb->c->ci->target_class) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module"); mrb_exc_set(mrb, exc); goto L_RAISE; } regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class); NEXT; } CASE(OP_RANGE) { /* A B C R(A) := range_new(R(B),R(B+1),C) */ int b = GETARG_B(i); regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_DEBUG) { /* A B C debug print R(A),R(B),R(C) */ #ifdef MRB_ENABLE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, pc, regs); #else #ifndef MRB_DISABLE_STDIO printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i)); #else abort(); #endif #endif NEXT; } CASE(OP_STOP) { /* stop VM */ L_STOP: { int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx; int eidx = mrb->c->ci->eidx; while (eidx > eidx_stop) { ecall(mrb, --eidx); } } ERR_PC_CLR(mrb); mrb->jmp = prev_jmp; if (mrb->exc) { return mrb_obj_value(mrb->exc); } return regs[irep->nlocals]; } CASE(OP_ERR) { /* Bx raise RuntimeError with message Lit(Bx) */ mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); mrb_value exc; if (GETARG_A(i) == 0) { exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg); } else { exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); } mrb_exc_set(mrb, exc); goto L_RAISE; } } END_DISPATCH; } MRB_CATCH(&c_jmp) { exc_catched = TRUE; goto RETRY_TRY_BLOCK; } MRB_END_EXC(&c_jmp); } MRB_API mrb_value mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) { return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ } MRB_API mrb_value mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { mrb_callinfo *ci; mrb_value v; if (!mrb->c->cibase || mrb->c->ci == mrb->c->cibase) { return mrb_vm_run(mrb, proc, self, stack_keep); } ci = cipush(mrb); ci->nregs = 1; /* protect the receiver */ ci->acc = CI_ACC_SKIP; ci->target_class = mrb->object_class; v = mrb_vm_run(mrb, proc, self, stack_keep); cipop(mrb); return v; } mruby-1.2.0+20160315+git4f20d58a/tasks/000077500000000000000000000000001267140355100165335ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/tasks/benchmark.rake000066400000000000000000000037341267140355100213400ustar00rootroot00000000000000module MRuby BENCHMARK_REPEAT = 4 end $dat_files = [] def bm_files Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb") end def build_config_name if ENV['MRUBY_CONFIG'] File.basename(ENV['MRUBY_CONFIG'], '.rb').gsub('build_config_', '') else "build" end end def plot_file File.join(MRUBY_ROOT, 'benchmark', "#{build_config_name}.png") end def plot opts_file = "#{MRUBY_ROOT}/benchmark/plot.gpl" opts = File.read(opts_file).each_line.to_a.map(&:strip).join(';') dat_files = $dat_files.group_by {|f| File.dirname(f).split(File::SEPARATOR)[-1]} opts += ";set output '#{plot_file}'" opts += ';plot ' opts += dat_files.keys.map do |data_file| %Q['-' u 2:3:4:xtic(1) w hist title columnheader(1)] end.join(',') opts += ';' cmd = %Q{gnuplot -p -e "#{opts}"} IO.popen(cmd, 'w') do |p| dat_files.each do |target_name, bm_files| p.puts target_name.gsub('_', '-') bm_files.each do |bm_file| p.write File.read(bm_file) end p.puts "e" end end end MRuby.each_target do |target| next if target.name == 'host' mruby_bin = "#{target.build_dir}/bin/mruby" bm_files.each do |bm_file| bm_name = File.basename bm_file, ".rb" dat_dir = File.join('benchmark', build_config_name, target.name) dat_file = File.join(dat_dir, "#{bm_name}.dat") $dat_files << dat_file directory dat_dir file dat_file => [bm_file, dat_dir, mruby_bin] do |task| print bm_name puts "..." data = (0...MRuby::BENCHMARK_REPEAT).map do |n| str = %x{(time -f "%e %S %U" #{mruby_bin} #{bm_file}) 2>&1 >/dev/null} str.split(' ').map(&:to_f) end File.open(task.name, "w") do |f| data = data.map {|_,r,s| (r + s) / 2.0} min = data.min max = data.max avg = data.inject(&:+) / data.size f.puts "#{bm_name.gsub('_', '-')} #{avg} #{min} #{max}" end end end end file plot_file => $dat_files do plot end task :benchmark => plot_file do plot end mruby-1.2.0+20160315+git4f20d58a/tasks/libmruby.rake000066400000000000000000000020651267140355100212270ustar00rootroot00000000000000MRuby.each_target do file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t| archiver.run t.name, t.prerequisites end file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t| open(t.name, 'w') do |f| f.puts "MRUBY_CFLAGS = #{cc.all_flags.gsub('"', '\\"')}" gem_flags = gems.map { |g| g.linker.flags } gem_library_paths = gems.map { |g| g.linker.library_paths } f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags).gsub('"', '\\"')} #{linker.option_library_path % "#{build_dir}/lib"}" gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ').gsub('"', '\\"')}" gem_libraries = gems.map { |g| g.linker.libraries } f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries).gsub('"', '\\"')}" end end task :all => "#{build_dir}/lib/libmruby.flags.mak" end mruby-1.2.0+20160315+git4f20d58a/tasks/mrbgem_spec.rake000066400000000000000000000312351267140355100216660ustar00rootroot00000000000000require 'pathname' require 'forwardable' require 'tsort' module MRuby module Gem class << self attr_accessor :current end LinkerConfig = Struct.new(:libraries, :library_paths, :flags, :flags_before_libraries, :flags_after_libraries) class Specification include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile attr_accessor :name, :dir, :build alias mruby build attr_accessor :build_config_initializer attr_accessor :version attr_accessor :description, :summary attr_accessor :homepage attr_accessor :licenses, :authors alias :license= :licenses= alias :author= :authors= attr_accessor :rbfiles, :objs attr_accessor :test_objs, :test_rbfiles, :test_args attr_accessor :test_preload attr_accessor :bins attr_accessor :requirements attr_reader :dependencies, :conflicts attr_accessor :export_include_paths attr_reader :generate_functions attr_block MRuby::Build::COMMANDS def initialize(name, &block) @name = name @initializer = block @version = "0.0.0" MRuby::Gem.current = self end def setup MRuby::Gem.current = self MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end @linker = LinkerConfig.new([], [], [], [], []) @rbfiles = Dir.glob("#{dir}/mrblib/**/*.rb").sort @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end @generate_functions = !(@rbfiles.empty? && @objs.empty?) @objs << objfile("#{build_dir}/gem_init") if @generate_functions @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb") @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end @custom_test_init = !@test_objs.empty? @test_preload = nil # 'test/assert.rb' @test_args = {} @bins = [] @requirements = [] @dependencies, @conflicts = [], [] @export_include_paths = [] @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include" instance_eval(&@initializer) if !name || !licenses || !authors fail "#{name || dir} required to set name, license(s) and author(s)" end build.libmruby << @objs instance_eval(&@build_config_initializer) if @build_config_initializer compilers.each do |compiler| compiler.define_rules build_dir, "#{dir}" compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}] compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include" end define_gem_init_builder if @generate_functions end def add_dependency(name, *requirements) default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil requirements = ['>= 0.0.0'] if requirements.empty? requirements.flatten! @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end def add_test_dependency(*args) add_dependency(*args) if build.test_enabled? end def add_conflict(name, *req) @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} end def self.bin=(bin) @bins = [bin].flatten end def build_dir "#{build.build_dir}/mrbgems/#{name}" end def test_rbireps "#{build_dir}/gem_test.c" end def funcname @funcname ||= @name.gsub('-', '_') end def compilers MRuby::Build::COMPILERS.map do |c| instance_variable_get("@#{c}") end end def define_gem_init_builder file objfile("#{build_dir}/gem_init") => [ "#{build_dir}/gem_init.c", File.join(dir, "mrbgem.rake") ] file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t| FileUtils.mkdir_p build_dir generate_gem_init("#{build_dir}/gem_init.c") end end def generate_gem_init(fname) open(fname, 'w') do |f| print_gem_init_header f build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty? f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);] f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {] f.puts %Q[ int ai = mrb_gc_arena_save(mrb);] f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")] unless rbfiles.empty? f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});] f.puts %Q[ if (mrb->exc) {] f.puts %Q[ mrb_print_error(mrb);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] end f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] f.puts %Q[}] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {] f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")] f.puts %Q[}] end end # generate_gem_init def print_gem_comment(f) f.puts %Q[/*] f.puts %Q[ * This file is loading the irep] f.puts %Q[ * Ruby GEM code.] 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[ */] end def print_gem_init_header(f) print_gem_comment(f) f.puts %Q[#include ] unless rbfiles.empty? f.puts %Q[#include ] f.puts %Q[#include ] unless rbfiles.empty? end def print_gem_test_header(f) print_gem_comment(f) f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] unless test_args.empty? end def test_dependencies [@name] end def custom_test_init? @custom_test_init end def version_ok?(req_versions) req_versions.map do |req| cmp, ver = req.split cmp_result = Version.new(version) <=> Version.new(ver) case cmp when '=' then cmp_result == 0 when '!=' then cmp_result != 0 when '>' then cmp_result == 1 when '<' then cmp_result == -1 when '>=' then cmp_result >= 0 when '<=' then cmp_result <= 0 when '~>' Version.new(version).twiddle_wakka_ok?(Version.new(ver)) else fail "Comparison not possible with '#{cmp}'" end end.all? end end # Specification class Version include Comparable include Enumerable def <=>(other) ret = 0 own = to_enum other.each do |oth| begin ret = own.next <=> oth rescue StopIteration ret = 0 <=> oth end break unless ret == 0 end ret end # ~> compare algorithm # # Example: # ~> 2.2 means >= 2.2.0 and < 3.0.0 # ~> 2.2.0 means >= 2.2.0 and < 2.3.0 def twiddle_wakka_ok?(other) gr_or_eql = (self <=> other) >= 0 still_minor = (self <=> other.skip_minor) < 0 gr_or_eql and still_minor end def skip_minor a = @ary.dup a.slice!(-1) a[-1] = a[-1].succ a end def initialize(str) @str = str @ary = @str.split('.').map(&:to_i) end def each(&block); @ary.each(&block); end def [](index); @ary[index]; end def []=(index, value) @ary[index] = value @str = @ary.join('.') end def slice!(index) @ary.slice!(index) @str = @ary.join('.') end end # Version class List include Enumerable def initialize @ary = [] end def each(&b) @ary.each(&b) end def <<(gem) unless @ary.detect {|g| g.dir == gem.dir } @ary << gem else # GEM was already added to this list end end def empty? @ary.empty? end def generate_gem_table build gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } default_gems = [] each do |g| g.dependencies.each do |dep| unless gem_table.key? dep[:gem] if dep[:default]; default_gems << dep elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core default_gems << { :gem => dep[:gem], :default => { :core => dep[:gem] } } else # fallback to mgem-list default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } } end end end end until default_gems.empty? def_gem = default_gems.pop spec = build.gem def_gem[:default] fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem] spec.setup spec.dependencies.each do |dep| unless gem_table.key? dep[:gem] if dep[:default]; default_gems << dep else default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } } end end end gem_table[spec.name] = spec end each do |g| g.dependencies.each do |dep| name = dep[:gem] req_versions = dep[:requirements] dep_g = gem_table[name] # check each GEM dependency against all available GEMs if dep_g.nil? fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found" end unless dep_g.version_ok? req_versions fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end end cfls = g.conflicts.select { |c| cfl_g = gem_table[c[:gem]] cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0']) }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" } fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end gem_table end def tsort_dependencies ary, table, all_dependency_listed = false unless all_dependency_listed left = ary.dup until left.empty? v = left.pop table[v].dependencies.each do |dep| left.push dep[:gem] ary.push dep[:gem] end end end ary.uniq! table.instance_variable_set :@root_gems, ary class << table include TSort def tsort_each_node &b @root_gems.each &b end def tsort_each_child(n, &b) fetch(n).dependencies.each do |v| b.call v[:gem] end end end begin table.tsort.map { |v| table[v] } rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end end def check(build) gem_table = generate_gem_table build @ary = tsort_dependencies gem_table.keys, gem_table, true each do |g| import_include_paths(g) end end def import_include_paths(g) gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } g.dependencies.each do |dep| dep_g = gem_table[dep[:gem]] # We can do recursive call safely # as circular dependency has already detected in the caller. import_include_paths(dep_g) dep_g.export_include_paths.uniq! g.compilers.each do |compiler| compiler.include_paths += dep_g.export_include_paths g.export_include_paths += dep_g.export_include_paths compiler.include_paths.uniq! g.export_include_paths.uniq! end end end end # List end # Gem GemBox = Object.new class << GemBox attr_accessor :path def new(&block); block.call(self); end def config=(obj); @config = obj; end def gem(gemdir, &block); @config.gem(gemdir, &block); end end # GemBox end # MRuby mruby-1.2.0+20160315+git4f20d58a/tasks/mrbgems.rake000066400000000000000000000071151267140355100210370ustar00rootroot00000000000000MRuby.each_target do if enable_gems? # set up all gems gems.each(&:setup) gems.check self # loader all gems self.libmruby << objfile("#{build_dir}/mrbgems/gem_init") file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"] file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t| FileUtils.mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| gem_func_gems = gems.select { |g| g.generate_functions } gem_func_decls = gem_func_gems.each_with_object('') do |g, s| s << "void GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb_state*);\n" \ "void GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb_state*);\n" end gem_init_calls = gem_func_gems.each_with_object('') do |g, s| s << " GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb);\n" end gem_final_calls = gem_func_gems.each_with_object('') do |g, s| s << " GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n" end f.puts %Q[/*] f.puts %Q[ * This file contains a list of all] f.puts %Q[ * initializing methods which are] f.puts %Q[ * necessary to bootstrap all gems.] 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 %Q[#include ] f.puts %Q[] f.write gem_func_decls f.puts %Q[] f.puts %Q[static void] f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] f.write gem_final_calls f.puts %Q[}] f.puts %Q[] f.puts %Q[void] f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] f.write gem_init_calls f.puts %Q[ mrb_state_atexit(mrb, mrb_final_mrbgems);] unless gem_final_calls.empty? f.puts %Q[}] end end end # legal documents file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t| open(t.name, 'w+') do |f| f.puts < [src, __FILE__] do |t| FileUtils.mkdir_p File.dirname t.name IO.write t.name, < cxx_src do |t| cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes end obj end def enable_bintest @enable_bintest = true end def bintest_enabled? @enable_bintest end def toolchain(name, params={}) tc = Toolchain.toolchains[name.to_s] fail "Unknown #{name} toolchain" unless tc tc.setup(self, params) @toolchains.unshift name.to_s end def primary_toolchain @toolchains.first end def root MRUBY_ROOT end def enable_test @enable_test = true end def test_enabled? @enable_test end def build_mrbtest gem :core => 'mruby-test' end def build_mrbc_exec gem :core => 'mruby-bin-mrbc' end def mrbcfile return @mrbcfile if @mrbcfile mrbc_build = MRuby.targets['host'] gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' } @mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc") end def compilers COMPILERS.map do |c| instance_variable_get("@#{c}") end end def define_rules compilers.each do |compiler| if respond_to?(:enable_gems?) && enable_gems? compiler.defines -= %w(DISABLE_GEMS) else compiler.defines += %w(DISABLE_GEMS) end compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..')) end end def filename(name) if name.is_a?(Array) name.flatten.map { |n| filename(n) } else '"%s"' % name.gsub('/', file_separator) end end def cygwin_filename(name) if name.is_a?(Array) name.flatten.map { |n| cygwin_filename(n) } else '"%s"' % `cygpath -w "#{filename(name)}"`.strip end end def exefile(name) if name.is_a?(Array) name.flatten.map { |n| exefile(n) } else "#{name}#{exts.executable}" end end def objfile(name) if name.is_a?(Array) name.flatten.map { |n| objfile(n) } else "#{name}#{exts.object}" end end def libfile(name) if name.is_a?(Array) name.flatten.map { |n| libfile(n) } else "#{name}#{exts.library}" end end def build_mrbtest_lib_only @build_mrbtest_lib_only = true end def build_mrbtest_lib_only? @build_mrbtest_lib_only end def run_test puts ">>> Test #{name} <<<" mrbtest = exefile("#{build_dir}/bin/mrbtest") sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}" puts run_bintest if bintest_enabled? end def run_bintest targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } targets << filename(".") if File.directory? "./bintest" sh "ruby test/bintest.rb #{targets.join ' '}" end def print_build_summary puts "================================================" puts " Config Name: #{@name}" puts " Output Directory: #{self.build_dir.relative_path}" puts " Binaries: #{@bins.join(', ')}" unless @bins.empty? unless @gems.empty? puts " Included Gems:" @gems.map do |gem| gem_version = " - #{gem.version}" if gem.version != '0.0.0' gem_summary = " - #{gem.summary}" if gem.summary puts " #{gem.name}#{gem_version}#{gem_summary}" puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty? end end puts "================================================" puts end end # Build class CrossBuild < Build attr_block %w(test_runner) # cross compiling targets for building native extensions. # host - arch of where the built binary will run # build - arch of the machine building the binary attr_accessor :host_target, :build_target def initialize(name, build_dir=nil, &block) @test_runner = Command::CrossTestRunner.new(self) super end def mrbcfile MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc") end def run_test mrbtest = exefile("#{build_dir}/bin/mrbtest") if (@test_runner.command == nil) puts "You should run #{mrbtest} on target device." puts else @test_runner.run(mrbtest) end end end # CrossBuild end # MRuby mruby-1.2.0+20160315+git4f20d58a/tasks/mruby_build_commands.rake000066400000000000000000000244771267140355100236130ustar00rootroot00000000000000require 'forwardable' module MRuby class Command include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile, :cygwin_filename attr_accessor :build, :command def initialize(build) @build = build end # clone is deep clone without @build def clone target = super excepts = %w(@build) instance_variables.each do |attr| unless excepts.include?(attr.to_s) val = Marshal::load(Marshal.dump(instance_variable_get(attr))) # deep clone target.instance_variable_set(attr, val) end end target end NotFoundCommands = {} private def _run(options, params={}) return sh command + ' ' + ( options % params ) if NotFoundCommands.key? @command begin sh build.filename(command) + ' ' + ( options % params ) rescue RuntimeError NotFoundCommands[@command] = true _run options, params end end end class Command::Compiler < Command attr_accessor :flags, :include_paths, :defines, :source_exts attr_accessor :compile_options, :option_define, :option_include_path, :out_ext def initialize(build, source_exts=[]) super(build) @command = ENV['CC'] || 'cc' @flags = [ENV['CFLAGS'] || []] @source_exts = source_exts @include_paths = ["#{MRUBY_ROOT}/include"] @defines = %w() @option_include_path = '-I%s' @option_define = '-D%s' @compile_options = '%{flags} -o %{outfile} -c %{infile}' end alias header_search_paths include_paths def search_header_path(name) header_search_paths.find do |v| File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1') end end def search_header(name) path = search_header_path name path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1') end def all_flags(_defineds=[], _include_paths=[], _flags=[]) define_flags = [defines, _defineds].flatten.map{ |d| option_define % d } include_path_flags = [include_paths, _include_paths].flatten.map do |f| if MRUBY_BUILD_HOST_IS_CYGWIN option_include_path % cygwin_filename(f) else option_include_path % filename(f) end end [flags, define_flags, include_path_flags, _flags].flatten.join(' ') end def run(outfile, infile, _defineds=[], _include_paths=[], _flags=[]) FileUtils.mkdir_p File.dirname(outfile) _pp "CC", infile.relative_path, outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags), :infile => cygwin_filename(infile), :outfile => cygwin_filename(outfile) } else _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags), :infile => filename(infile), :outfile => filename(outfile) } end end def define_rules(build_dir, source_dir='') @out_ext = build.exts.object gemrake = File.join(source_dir, "mrbgem.rake") rakedep = File.exist?(gemrake) ? [ gemrake ] : [] if build_dir.include? "mrbgems/" generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(.*)#{Regexp.escape out_ext}$") else generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbgems/.+/)(.*)#{Regexp.escape out_ext}$") end source_exts.each do |ext, compile| rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end end end private def get_dependencies(file) file = file.ext('d') unless File.extname(file) == '.d' if File.exist?(file) File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten else [] end + [ MRUBY_CONFIG ] end end class Command::Linker < Command attr_accessor :flags, :library_paths, :flags_before_libraries, :libraries, :flags_after_libraries attr_accessor :link_options, :option_library, :option_library_path def initialize(build) super @command = ENV['LD'] || 'ld' @flags = (ENV['LDFLAGS'] || []) @flags_before_libraries, @flags_after_libraries = [], [] @libraries = [] @library_paths = [] @option_library = '-l%s' @option_library_path = '-L%s' @link_options = "%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}" end def all_flags(_library_paths=[], _flags=[]) library_path_flags = [library_paths, _library_paths].flatten.map do |f| if MRUBY_BUILD_HOST_IS_CYGWIN option_library_path % cygwin_filename(f) else option_library_path % filename(f) end end [flags, library_path_flags, _flags].flatten.join(' ') end def library_flags(_libraries) [libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ') end def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[]) FileUtils.mkdir_p File.dirname(outfile) library_flags = [libraries, _libraries].flatten.map { |d| option_library % d } _pp "LD", outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run link_options, { :flags => all_flags(_library_paths, _flags), :outfile => cygwin_filename(outfile) , :objs => cygwin_filename(objfiles).join(' '), :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '), :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '), :libs => library_flags.join(' ') } else _run link_options, { :flags => all_flags(_library_paths, _flags), :outfile => filename(outfile) , :objs => filename(objfiles).join(' '), :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '), :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '), :libs => library_flags.join(' ') } end end end class Command::Archiver < Command attr_accessor :archive_options def initialize(build) super @command = ENV['AR'] || 'ar' @archive_options = 'rs %{outfile} %{objs}' end def run(outfile, objfiles) FileUtils.mkdir_p File.dirname(outfile) _pp "AR", outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run archive_options, { :outfile => cygwin_filename(outfile), :objs => cygwin_filename(objfiles).join(' ') } else _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).join(' ') } end end end class Command::Yacc < Command attr_accessor :compile_options def initialize(build) super @command = 'bison' @compile_options = '-o %{outfile} %{infile}' end def run(outfile, infile) FileUtils.mkdir_p File.dirname(outfile) _pp "YACC", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Gperf < Command attr_accessor :compile_options def initialize(build) super @command = 'gperf' @compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' end def run(outfile, infile) FileUtils.mkdir_p File.dirname(outfile) _pp "GPERF", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Git < Command attr_accessor :flags attr_accessor :clone_options, :pull_options, :checkout_options def initialize(build) super @command = 'git' @flags = %w[] @clone_options = "clone %{flags} %{url} %{dir}" @pull_options = "pull" @checkout_options = "checkout %{checksum_hash}" end def run_clone(dir, url, _flags = []) _pp "GIT", url, dir.relative_path _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => url, :dir => filename(dir) } end def run_pull(dir, url) root = Dir.pwd Dir.chdir dir _pp "GIT PULL", url, dir.relative_path _run pull_options Dir.chdir root end def run_checkout(dir, checksum_hash) root = Dir.pwd Dir.chdir dir _pp "GIT CHECKOUT", checksum_hash _run checkout_options, { :checksum_hash => checksum_hash } Dir.chdir root end end class Command::Mrbc < Command attr_accessor :compile_options def initialize(build) super @command = nil @compile_options = "-B%{funcname} -o-" end def run(out, infiles, funcname) @command ||= @build.mrbcfile infiles = [infiles].flatten infiles.each do |f| _pp "MRBC", f.relative_path, nil, :indent => 2 end IO.popen("#{filename @command} #{@compile_options % {:funcname => funcname}} #{filename(infiles).join(' ')}", 'r+') do |io| out.puts io.read end # if mrbc execution fail, drop the file if $?.exitstatus != 0 File.delete(out.path) exit(-1) end end end class Command::CrossTestRunner < Command attr_accessor :runner_options attr_accessor :verbose_flag attr_accessor :flags def initialize(build) super @command = nil @runner_options = '%{flags} %{infile}' @verbose_flag = '' @flags = [] end def run(testbinfile) puts "TEST for " + @build.name _run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile } end end end mruby-1.2.0+20160315+git4f20d58a/tasks/mruby_build_gem.rake000066400000000000000000000075111267140355100225500ustar00rootroot00000000000000module MRuby module LoadGems def gembox(gemboxfile) gembox = File.expand_path("#{gemboxfile}.gembox", "#{MRUBY_ROOT}/mrbgems") fail "Can't find gembox '#{gembox}'" unless File.exist?(gembox) GemBox.config = self GemBox.path = gembox instance_eval File.read(gembox) GemBox.path = nil end def gem(gemdir, &block) caller_dir = File.expand_path(File.dirname(/^(.*?):\d/.match(caller.first).to_a[1])) if gemdir.is_a?(Hash) gemdir = load_special_path_gem(gemdir) elsif GemBox.path && gemdir.is_a?(String) gemdir = File.expand_path(gemdir, File.dirname(GemBox.path)) else gemdir = File.expand_path(gemdir, caller_dir) end gemrake = File.join(gemdir, "mrbgem.rake") fail "Can't find #{gemrake}" unless File.exist?(gemrake) Gem.current = nil load gemrake return nil unless Gem.current Gem.current.dir = gemdir Gem.current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current Gem.current.build_config_initializer = block gems << Gem.current cxx_srcs = ['src', 'test', 'tools'].map do |subdir| Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}") end.flatten enable_cxx_abi unless cxx_srcs.empty? Gem.current end def load_special_path_gem(params) if params[:github] params[:git] = "https://github.com/#{params[:github]}.git" elsif params[:bitbucket] if params[:method] == "ssh" params[:git] = "git@bitbucket.org:#{params[:bitbucket]}.git" else params[:git] = "https://bitbucket.org/#{params[:bitbucket]}.git" end elsif params[:mgem] mgem_list_dir = "#{gem_clone_dir}/mgem-list" mgem_list_url = 'https://github.com/mruby/mgem-list.git' if File.exist? mgem_list_dir git.run_pull mgem_list_dir, mgem_list_url if $pull_gems else FileUtils.mkdir_p mgem_list_dir git.run_clone mgem_list_dir, mgem_list_url, "--depth 1" end require 'yaml' conf_path = "#{mgem_list_dir}/#{params[:mgem]}.gem" conf_path = "#{mgem_list_dir}/mruby-#{params[:mgem]}.gem" unless File.exist? conf_path fail "mgem not found: #{params[:mgem]}" unless File.exist? conf_path conf = YAML.load File.read conf_path fail "unknown mgem protocol: #{conf['protocol']}" if conf['protocol'] != 'git' params[:git] = conf['repository'] params[:branch] = conf['branch'] if conf['branch'] end if params[:core] gemdir = "#{root}/mrbgems/#{params[:core]}" elsif params[:path] require 'pathname' gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}" elsif params[:git] url = params[:git] gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}" # by default the 'master' branch is used branch = params[:branch] ? params[:branch] : 'master' if File.exist?(gemdir) if $pull_gems git.run_pull gemdir, url else gemdir end else options = [params[:options]] || [] options << "--recursive" options << "--branch \"#{branch}\"" options << "--depth 1" unless params[:checksum_hash] FileUtils.mkdir_p "#{gem_clone_dir}" git.run_clone gemdir, url, options end if params[:checksum_hash] # Jump to the specified commit git.run_checkout gemdir, params[:checksum_hash] else # Jump to the top of the branch git.run_checkout gemdir, branch if $pull_gems end else fail "unknown gem option #{params}" end gemdir end def enable_gems? !@gems.empty? end end # LoadGems end # MRuby mruby-1.2.0+20160315+git4f20d58a/tasks/ruby_ext.rake000066400000000000000000000031401267140355100212360ustar00rootroot00000000000000class Object class << self def attr_block(*syms) syms.flatten.each do |sym| class_eval "def #{sym}(&block);block.call(@#{sym}) if block_given?;@#{sym};end" end end end end class String def relative_path_from(dir) Pathname.new(File.expand_path(self)).relative_path_from(Pathname.new(File.expand_path(dir))).to_s end def relative_path relative_path_from(Dir.pwd) end # Compatible with 1.9 on 1.8 def %(params) if params.is_a?(Hash) str = self.clone params.each do |k, v| str.gsub!("%{#{k}}") { v } end str else if params.is_a?(Array) sprintf(self, *params) else sprintf(self, params) end end end end class Symbol # Compatible with 1.9 on 1.8 def to_proc proc { |obj, *args| obj.send(self, *args) } end end module Enumerable # Compatible with 1.9 on 1.8 def each_with_object(memo) return to_enum :each_with_object, memo unless block_given? each { |obj| yield obj, memo } memo end end $pp_show = true if $verbose.nil? if Rake.respond_to?(:verbose) && !Rake.verbose.nil? if Rake.verbose.class == TrueClass # verbose message logging $pp_show = false else $pp_show = true Rake.verbose(false) end else # could not identify rake version $pp_show = false end else $pp_show = false if $verbose end def _pp(cmd, src, tgt=nil, options={}) return unless $pp_show width = 5 template = options[:indent] ? "%#{width*options[:indent]}s %s %s" : "%-#{width}s %s %s" puts template % [cmd, src, tgt ? "-> #{tgt}" : nil] end mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/000077500000000000000000000000001267140355100206765ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/android.rake000066400000000000000000000126041267140355100231650ustar00rootroot00000000000000class MRuby::Toolchain::Android DEFAULT_ARCH = 'armeabi' DEFAULT_PLATFORM = 'android-14' DEFAULT_TOOLCHAIN = :gcc DEFAULT_NDK_HOMES = %w{ /usr/local/opt/android-ndk } TOOLCHAINS = [:gcc, :clang] ARCHITECTURES = %w{ armeabi armeabi-v7a arm64-v8a mips mips64 x86 x86_64 } class AndroidNDKHomeNotFound < StandardError def message <<-EOM Couldn't find Android NDK Home. Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter EOM end end attr_reader :params def initialize(params) @params = params end def home_path @home_path ||= Pathname( params[:ndk_home] || ENV['ANDROID_NDK_HOME'] || DEFAULT_NDK_HOMES.find{ |path| File.directory?(path) } || raise(AndroidNDKHomeNotFound) ) end def arch params.fetch(:arch){ DEFAULT_ARCH } end def platform params.fetch(:platform){ DEFAULT_PLATFORM } end def toolchain params.fetch(:toolchain){ DEFAULT_TOOLCHAIN } end def toolchain_version params.fetch(:toolchain_version) do test = case toolchain when :gcc case arch when /armeabi/ 'arm-linux-androideabi-*' when /arm64/ 'aarch64-linux-android-*' when /mips64/ 'mips64el-linux-android-*' when /mips/ 'mipsel-linux-android-*' when /x86_64/ 'x86_64-*' when /x86/ 'x86-*' end when :clang 'llvm-*' end Dir[home_path.join('toolchains',test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max end end def toolchain_path prefix = case toolchain when :clang then 'llvm-' when :gcc case arch when /armeabi/ then 'arm-linux-androideabi-' when /arm64/ then 'aarch64-linux-android-' when /x86_64/ then 'x86_64-' when /x86/ then 'x86-' when /mips64/ then 'mips64el-linux-android-' when /mips/ then 'mipsel-linux-android-' end end home_path.join('toolchains', prefix + toolchain_version.to_s, 'prebuilt', host_platform) end def sysroot path = case arch when /armeabi/ then 'arch-arm' when /arm64/ then 'arch-arm64' when /x86_64/ then 'arch-x86_64' when /x86/ then 'arch-x86' when /mips64/ then 'arch-mips64' when /mips/ then 'arch-mips' end home_path.join('platforms', platform, path).to_s end def bin(command) command = command.to_s if toolchain == :gcc command = case arch when /armeabi/ then 'arm-linux-androideabi-' when /arm64/ then 'aarch64-linux-android-' when /x86_64/ then 'x86_64-linux-android-' when /x86/ then 'i686-linux-android-' when /mips64/ then 'mips64el-linux-android-' when /mips/ then 'mipsel-linux-android-' end + command end toolchain_path.join('bin',command).to_s end def cc case toolchain when :gcc then bin(:gcc) when :clang then bin(:clang) end end def cflags flags = [] case toolchain when :gcc flags += %W(-ffunction-sections -funwind-tables -no-canonical-prefixes) flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}") case arch when /arm64/ flags += %W(-fpic -fstack-protector-strong) when 'armeabi-v7a-hard' flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mhard-float -D_NDK_MATH_NO_SOFTFP=1 -mfpu=vfpv3-d16) when 'armeabi-v7a' flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16) when /arm/ flags += %W(-fpic -fstack-protector-strong -march=armv5te -mtune=xscale -msoft-float) when /mips/ flags += %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers) when /x86/ flags += %W(-fstack-protector-strong) end when :clang end flags end def ld cc end def ldflags flags = [] case toolchain when :gcc flags += %W(-no-canonical-prefixes) flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}") case arch when 'armeabi-v7a-hard' flags += %W(-march=armv7-a -Wl,--fix-cortex-a8 -Wl,--no-warn-mismatch -lm_hard) when 'armeabi-v7a' flags += %W(-march=armv7-a -Wl,--fix-cortex-a8) end end flags end def ar case toolchain when :gcc then bin(:ar) when :clang then bin('llvm-ar') end end def host_platform case RUBY_PLATFORM when /cygwin|mswin|mingw|bccwin|wince|emx/i 'windows' when /x86_64-darwin/i 'darwin-x86_64' when /darwin/i 'darwin-x86' when /x86_64-linux/i 'linux-x86_64' when /linux/i 'linux-x86' else raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})" end end end MRuby::Toolchain.new(:android) do |conf, params| ndk = MRuby::Toolchain::Android.new(params) toolchain ndk.toolchain [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| cc.command = ndk.cc cc.flags = ndk.cflags end conf.linker.command = ndk.ld conf.linker.flags = ndk.ldflags conf.archiver.command = ndk.ar end mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/clang.rake000066400000000000000000000003721267140355100226300ustar00rootroot00000000000000MRuby::Toolchain.new(:clang) do |conf, _params| toolchain :gcc [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'clang' end conf.cxx.command = ENV['CXX'] || 'clang++' conf.linker.command = ENV['LD'] || 'clang' end mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/gcc.rake000066400000000000000000000041161267140355100223000ustar00rootroot00000000000000MRuby::Toolchain.new(:gcc) do |conf, _params| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'gcc' cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings)] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'g++' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end conf.linker do |linker| linker.command = ENV['LD'] || 'gcc' linker.flags = [ENV['LDFLAGS'] || %w()] linker.libraries = %w(m) linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end [[conf.cc, 'c'], [conf.cxx, 'c++']].each do |cc, lang| cc.instance_variable_set :@header_search_language, lang def cc.header_search_paths if @header_search_command != command result = `echo | #{build.filename command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` result = `echo | #{command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` if $?.exitstatus != 0 return include_paths if $?.exitstatus != 0 @frameworks = [] @header_search_paths = result.lines.map { |v| framework = v.match(/^ (.*)(?: \(framework directory\))$/) if framework @frameworks << framework[1] next nil end v.match(/^ (.*)$/) }.compact.map { |v| v[1] }.select { |v| File.directory? v } @header_search_paths += include_paths @header_search_command = command end @header_search_paths end end end mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/openwrt.rake000066400000000000000000000024151267140355100232420ustar00rootroot00000000000000# usage of environmental variables to set the # cross compiling toolchain proper MRuby::Toolchain.new(:openwrt) do |conf| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['TARGET_CC'] cc.flags = ENV['TARGET_CFLAGS'] cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end [conf.cxx].each do |cxx| cxx.command = ENV['TARGET_CXX'] cxx.flags = ENV['TARGET_CXXFLAGS'] cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end conf.linker do |linker| linker.command = ENV['TARGET_CC'] linker.flags = ENV['TARGET_LDFLAGS'] linker.libraries = %w(m) linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end conf.archiver do |archiver| archiver.command = ENV['TARGET_AR'] archiver.archive_options = 'rs %{outfile} %{objs}' end end mruby-1.2.0+20160315+git4f20d58a/tasks/toolchains/visualcpp.rake000066400000000000000000000035111267140355100235500ustar00rootroot00000000000000MRuby::Toolchain.new(:visualcpp) do |conf, _params| [conf.cc].each do |cc| cc.command = ENV['CC'] || 'cl.exe' # C4013: implicit function declaration cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)] cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cc.option_include_path = '/I%s' cc.option_define = '/D%s' cc.compile_options = "%{flags} /Fo%{outfile} %{infile}" end [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'cl.exe' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHs /D_CRT_SECURE_NO_WARNINGS)] cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cxx.option_include_path = '/I%s' cxx.option_define = '/D%s' cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}" end conf.linker do |linker| linker.command = ENV['LD'] || 'link.exe' linker.flags = [ENV['LDFLAGS'] || %w(/NOLOGO /DEBUG /INCREMENTAL:NO /OPT:ICF /OPT:REF)] linker.libraries = %w() linker.library_paths = %w() linker.option_library = '%s.lib' linker.option_library_path = '/LIBPATH:%s' linker.link_options = "%{flags} /OUT:%{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}" end conf.archiver do |archiver| archiver.command = ENV['AR'] || 'lib.exe' archiver.archive_options = '/nologo /OUT:%{outfile} %{objs}' end conf.yacc do |yacc| yacc.command = ENV['YACC'] || 'bison.exe' yacc.compile_options = '-o %{outfile} %{infile}' end conf.gperf do |gperf| gperf.command = 'gperf.exe' gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' end conf.exts do |exts| exts.object = '.obj' exts.executable = '.exe' exts.library = '.lib' end conf.file_separator = '\\' end mruby-1.2.0+20160315+git4f20d58a/test/000077500000000000000000000000001267140355100163655ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/test/assert.rb000066400000000000000000000144621267140355100202220ustar00rootroot00000000000000$ok_test = 0 $ko_test = 0 $kill_test = 0 $asserts = [] $test_start = Time.now if Object.const_defined?(:Time) # Implementation of print due to the reason that there might be no print def t_print(*args) i = 0 len = args.size while i < len str = args[i].to_s __t_printstr__ str rescue print str i += 1 end end ## # Create the assertion in a readable way def assertion_string(err, str, iso=nil, e=nil, bt=nil) msg = "#{err}#{str}" msg += " [#{iso}]" if iso && iso != '' msg += " => #{e.message}" if e msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME) if $mrbtest_assert && $mrbtest_assert.size > 0 $mrbtest_assert.each do |idx, str, diff| msg += "\n - Assertion[#{idx}] Failed: #{str}\n#{diff}" end end msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt msg end ## # Verify a code block. # # str : A remark which will be printed in case # this assertion fails # iso : The ISO reference code of the feature # which will be tested by this # assertion def assert(str = 'Assertion failed', iso = '') t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose begin $mrbtest_assert = [] $mrbtest_assert_idx = 0 if(!yield || $mrbtest_assert.size > 0) $asserts.push(assertion_string('Fail: ', str, iso, nil)) $ko_test += 1 t_print('F') else $ok_test += 1 t_print('.') end rescue Exception => e bt = e.backtrace if $mrbtest_verbose if e.class.to_s == 'MRubyTestSkip' $asserts.push "Skip: #{str} #{iso} #{e.cause}" t_print('?') else $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt)) $kill_test += 1 t_print('X') end ensure $mrbtest_assert = nil end t_print("\n") if $mrbtest_verbose end def assertion_diff(exp, act) " Expected: #{exp.inspect}\n" + " Actual: #{act.inspect}" end def assert_true(ret, msg = nil, diff = nil) if $mrbtest_assert $mrbtest_assert_idx += 1 unless ret msg = "Expected #{ret.inspect} to be true" unless msg diff = assertion_diff(true, ret) unless diff $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) end end ret end def assert_false(ret, msg = nil, diff = nil) if $mrbtest_assert $mrbtest_assert_idx += 1 if ret msg = "Expected #{ret.inspect} to be false" unless msg diff = assertion_diff(false, ret) unless diff $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) end end !ret end def assert_equal(arg1, arg2 = nil, arg3 = nil) if block_given? exp, act, msg = arg1, yield, arg2 else exp, act, msg = arg1, arg2, arg3 end msg = "Expected to be equal" unless msg diff = assertion_diff(exp, act) assert_true(exp == act, msg, diff) end def assert_not_equal(arg1, arg2 = nil, arg3 = nil) if block_given? exp, act, msg = arg1, yield, arg2 else exp, act, msg = arg1, arg2, arg3 end msg = "Expected to be not equal" unless msg diff = assertion_diff(exp, act) assert_false(exp == act, msg, diff) end def assert_nil(obj, msg = nil) msg = "Expected #{obj.inspect} to be nil" unless msg diff = assertion_diff(nil, obj) assert_true(obj.nil?, msg, diff) end def assert_include(collection, obj, msg = nil) msg = "Expected #{collection.inspect} to include #{obj.inspect}" unless msg diff = " Collection: #{collection.inspect}\n" + " Object: #{obj.inspect}" assert_true(collection.include?(obj), msg, diff) end def assert_not_include(collection, obj, msg = nil) msg = "Expected #{collection.inspect} to not include #{obj.inspect}" unless msg diff = " Collection: #{collection.inspect}\n" + " Object: #{obj.inspect}" assert_false(collection.include?(obj), msg, diff) end def assert_raise(*exp) ret = true if $mrbtest_assert $mrbtest_assert_idx += 1 msg = exp.last.class == String ? exp.pop : nil msg = msg.to_s + " : " if msg should_raise = false begin yield should_raise = true rescue Exception => e msg = "#{msg}#{exp.inspect} exception expected, not" diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" if not exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class } $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) ret = false end end exp = exp.first if exp.first if should_raise msg = "#{msg}#{exp.inspect} expected but nothing was raised." $mrbtest_assert.push([$mrbtest_assert_idx, msg, nil]) ret = false end end ret end def assert_nothing_raised(*exp) ret = true if $mrbtest_assert $mrbtest_assert_idx += 1 msg = exp.last.class == String ? exp.pop : "" begin yield rescue Exception => e msg = "#{msg} exception raised." diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) ret = false end end ret end ## # Fails unless +obj+ is a kind of +cls+. def assert_kind_of(cls, obj, msg = nil) msg = "Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}" unless msg diff = assertion_diff(cls, obj.class) assert_true(obj.kind_of?(cls), msg, diff) end ## # Fails unless +exp+ is equal to +act+ in terms of a Float def assert_float(exp, act, msg = nil) msg = "Float #{exp} expected to be equal to float #{act}" unless msg diff = assertion_diff(exp, act) assert_true check_float(exp, act), msg, diff end ## # Report the test result and print all assertions # which were reported broken. def report() t_print("\n") $asserts.each do |msg| puts msg end $total_test = $ok_test+$ko_test+$kill_test t_print("Total: #{$total_test}\n") t_print(" OK: #{$ok_test}\n") t_print(" KO: #{$ko_test}\n") t_print("Crash: #{$kill_test}\n") if Object.const_defined?(:Time) t_time = Time.now - $test_start t_print(" Time: #{t_time.round(2)} seconds\n") end end ## # Performs fuzzy check for equality on methods returning floats def check_float(a, b) tolerance = 1e-12 a = a.to_f b = b.to_f if a.finite? and b.finite? (a-b).abs < tolerance else true end end ## # Skip the test class MRubyTestSkip < NotImplementedError attr_accessor :cause def initialize(cause) @cause = cause end end def skip(cause = "") raise MRubyTestSkip.new(cause) end mruby-1.2.0+20160315+git4f20d58a/test/bintest.rb000066400000000000000000000007451267140355100203700ustar00rootroot00000000000000$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__))) require 'test/assert.rb' def cmd(s) case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|cygwin|bccwin/ "bin\\#{s}.exe" else "bin/#{s}" end end def shellquote(s) case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|cygwin|bccwin/ "\"#{s}\"" else "'#{s}'" end end ARGV.each do |gem| Dir["#{gem}/bintest/**/*.rb"].each do |file| load file end end load 'test/report.rb' mruby-1.2.0+20160315+git4f20d58a/test/report.rb000066400000000000000000000001541267140355100202250ustar00rootroot00000000000000report if $ko_test > 0 or $kill_test > 0 raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})" end mruby-1.2.0+20160315+git4f20d58a/test/t/000077500000000000000000000000001267140355100166305ustar00rootroot00000000000000mruby-1.2.0+20160315+git4f20d58a/test/t/argumenterror.rb000066400000000000000000000004361267140355100220540ustar00rootroot00000000000000## # ArgumentError ISO Test assert('ArgumentError', '15.2.24') do e2 = nil a = [] begin # this will cause an exception due to the wrong arguments a[] rescue => e1 e2 = e1 end assert_equal(Class, ArgumentError.class) assert_equal(ArgumentError, e2.class) end mruby-1.2.0+20160315+git4f20d58a/test/t/array.rb000066400000000000000000000217331267140355100203010ustar00rootroot00000000000000## # Array ISO Test assert('Array', '15.2.12') do assert_equal(Class, Array.class) end assert('Array inclueded modules', '15.2.12.3') do assert_true(Array.include?(Enumerable)) end assert('Array.[]', '15.2.12.4.1') do assert_equal([1, 2, 3], Array.[](1,2,3)) end assert('Array#+', '15.2.12.5.1') do assert_equal([1, 1], [1].+([1])) end assert('Array#*', '15.2.12.5.2') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1].*(-1) end assert_equal([1, 1, 1], [1].*(3)) assert_equal([], [1].*(0)) end assert('Array#<<', '15.2.12.5.3') do assert_equal([1, 1], [1].<<(1)) end assert('Array#[]', '15.2.12.5.4') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[](1,2,3) end assert_equal(2, [1,2,3].[](1)) assert_equal(nil, [1,2,3].[](4)) assert_equal(3, [1,2,3].[](-1)) assert_equal(nil, [1,2,3].[](-4)) a = [ "a", "b", "c", "d", "e" ] assert_equal("b", a[1.1]) assert_equal(["b", "c"], a[1,2]) assert_equal(["b", "c", "d"], a[1..-2]) end assert('Array#[]=', '15.2.12.5.5') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=(1,2,3,4) end assert_raise(IndexError) do # this will cause an exception due to the wrong arguments a = [1,2,3,4,5] a[1, -1] = 10 end assert_equal(4, [1,2,3].[]=(1,4)) assert_equal(3, [1,2,3].[]=(1,2,3)) a = [1,2,3,4,5] a[3..-1] = 6 assert_equal([1,2,3,6], a) a = [1,2,3,4,5] a[3..-1] = [] assert_equal([1,2,3], a) a = [1,2,3,4,5] a[2...4] = 6 assert_equal([1,2,6,5], a) end assert('Array#clear', '15.2.12.5.6') do a = [1] a.clear assert_equal([], a) end assert('Array#collect!', '15.2.12.5.7') do a = [1,2,3] a.collect! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#concat', '15.2.12.5.8') do assert_equal([1,2,3,4], [1, 2].concat([3, 4])) end assert('Array#delete_at', '15.2.12.5.9') do a = [1,2,3] assert_equal(2, a.delete_at(1)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(3)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(-3)) assert_equal([1,3], a) assert_equal(3, a.delete_at(-1)) assert_equal([1], a) end assert('Array#each', '15.2.12.5.10') do a = [1,2,3] b = 0 a.each {|i| b += i} assert_equal(6, b) end assert('Array#each_index', '15.2.12.5.11') do a = [1] b = nil a.each_index {|i| b = i} assert_equal(0, b) end assert('Array#empty?', '15.2.12.5.12') do a = [] b = [b] assert_true([].empty?) assert_false([1].empty?) end assert('Array#first', '15.2.12.5.13') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(-1) end assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(1,2) end assert_nil([].first) b = [1,2,3] assert_equal(1, b.first) assert_equal([], b.first(0)) assert_equal([1], b.first(1)) assert_equal([1,2,3], b.first(4)) end assert('Array#index', '15.2.12.5.14') do a = [1,2,3] assert_equal(1, a.index(2)) assert_equal(nil, a.index(0)) end assert('Array#initialize', '15.2.12.5.15') do a = [].initialize(1) b = [].initialize(2) c = [].initialize(2, 1) d = [].initialize(2) {|i| i} assert_equal([nil], a) assert_equal([nil,nil], b) assert_equal([1,1], c) assert_equal([0,1], d) end assert('Array#initialize_copy', '15.2.12.5.16') do a = [1,2,3] b = [].initialize_copy(a) assert_equal([1,2,3], b) end assert('Array#join', '15.2.12.5.17') do a = [1,2,3].join b = [1,2,3].join(',') assert_equal('123', a) assert_equal('1,2,3', b) end assert('Array#last', '15.2.12.5.18') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].last(-1) end a = [1,2,3] assert_equal(3, a.last) assert_nil([].last) end assert('Array#length', '15.2.12.5.19') do a = [1,2,3] assert_equal(3, a.length) end assert('Array#map!', '15.2.12.5.20') do a = [1,2,3] a.map! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#pop', '15.2.12.5.21') do a = [1,2,3] b = a.pop assert_nil([].pop) assert_equal([1,2], a) assert_equal(3, b) end assert('Array#push', '15.2.12.5.22') do a = [1,2,3] b = a.push(4) assert_equal([1,2,3,4], a) assert_equal([1,2,3,4], b) end assert('Array#replace', '15.2.12.5.23') do a = [1,2,3] b = [].replace(a) assert_equal([1,2,3], b) end assert('Array#reverse', '15.2.12.5.24') do a = [1,2,3] b = a.reverse assert_equal([1,2,3], a) assert_equal([3,2,1], b) end assert('Array#reverse!', '15.2.12.5.25') do a = [1,2,3] b = a.reverse! assert_equal([3,2,1], a) assert_equal([3,2,1], b) end assert('Array#rindex', '15.2.12.5.26') do a = [1,2,3] assert_equal(1, a.rindex(2)) assert_equal(nil, a.rindex(0)) end assert('Array#shift', '15.2.12.5.27') do a = [1,2,3] b = a.shift assert_nil([].shift) assert_equal([2,3], a) assert_equal(1, b) end assert('Array#size', '15.2.12.5.28') do a = [1,2,3] assert_equal(3, a.size) end assert('Array#slice', '15.2.12.5.29') do a = "12345".slice(1, 3) b = a.slice(0) assert_equal("2:", "#{b}:") assert_equal(2, [1,2,3].[](1)) end assert('Array#unshift', '15.2.12.5.30') do a = [2,3] b = a.unshift(1) c = [2,3] d = c.unshift(0, 1) assert_equal([1,2,3], a) assert_equal([1,2,3], b) assert_equal([0,1,2,3], c) assert_equal([0,1,2,3], d) end assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do a = [2, 3, 4, 5] r1 = a.to_s r2 = a.inspect assert_equal(r2, r1) assert_equal("[2, 3, 4, 5]", r1) end assert('Array#==', '15.2.12.5.33') do assert_false(["a", "c"] == ["a", "c", 7]) assert_true(["a", "c", 7] == ["a", "c", 7]) assert_false(["a", "c", 7] == ["a", "d", "f"]) end assert('Array#eql?', '15.2.12.5.34') do a1 = [ 1, 2, 3 ] a2 = [ 1, 2, 3 ] a3 = [ 1.0, 2.0, 3.0 ] assert_true(a1.eql? a2) assert_false(a1.eql? a3) end assert('Array#hash', '15.2.12.5.35') do a = [ 1, 2, 3 ] assert_true(a.hash.is_a? Integer) assert_equal([1,2].hash, [1,2].hash) end assert('Array#<=>', '15.2.12.5.36') do r1 = [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1 r2 = [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1 r3 = [ "a", "b", "c" ] <=> [ "a", "b", "c" ] #=> 0 assert_equal(-1, r1) assert_equal(+1, r2) assert_equal(0, r3) end # Not ISO specified assert("Array (Shared Array Corruption)") do a = [ "a", "b", "c", "d", "e", "f" ] b = a.slice(1, 3) a.clear b.clear end assert("Array (Longish inline array)") do ary = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25], [26, 26], [27, 27], [28, 28], [29, 29], [30, 30], [31, 31], [32, 32], [33, 33], [34, 34], [35, 35], [36, 36], [37, 37], [38, 38], [39, 39], [40, 40], [41, 41], [42, 42], [43, 43], [44, 44], [45, 45], [46, 46], [47, 47], [48, 48], [49, 49], [50, 50], [51, 51], [52, 52], [53, 53], [54, 54], [55, 55], [56, 56], [57, 57], [58, 58], [59, 59], [60, 60], [61, 61], [62, 62], [63, 63], [64, 64], [65, 65], [66, 66], [67, 67], [68, 68], [69, 69], [70, 70], [71, 71], [72, 72], [73, 73], [74, 74], [75, 75], [76, 76], [77, 77], [78, 78], [79, 79], [80, 80], [81, 81], [82, 82], [83, 83], [84, 84], [85, 85], [86, 86], [87, 87], [88, 88], [89, 89], [90, 90], [91, 91], [92, 92], [93, 93], [94, 94], [95, 95], [96, 96], [97, 97], [98, 98], [99, 99], [100, 100], [101, 101], [102, 102], [103, 103], [104, 104], [105, 105], [106, 106], [107, 107], [108, 108], [109, 109], [110, 110], [111, 111], [112, 112], [113, 113], [114, 114], [115, 115], [116, 116], [117, 117], [118, 118], [119, 119], [120, 120], [121, 121], [122, 122], [123, 123], [124, 124], [125, 125], [126, 126], [127, 127], [128, 128], [129, 129], [130, 130], [131, 131], [132, 132], [133, 133], [134, 134], [135, 135], [136, 136], [137, 137], [138, 138], [139, 139], [140, 140], [141, 141], [142, 142], [143, 143], [144, 144], [145, 145], [146, 146], [147, 147], [148, 148], [149, 149], [150, 150], [151, 151], [152, 152], [153, 153], [154, 154], [155, 155], [156, 156], [157, 157], [158, 158], [159, 159], [160, 160], [161, 161], [162, 162], [163, 163], [164, 164], [165, 165], [166, 166], [167, 167], [168, 168], [169, 169], [170, 170], [171, 171], [172, 172], [173, 173], [174, 174], [175, 175], [176, 176], [177, 177], [178, 178], [179, 179], [180, 180], [181, 181], [182, 182], [183, 183], [184, 184], [185, 185], [186, 186], [187, 187], [188, 188], [189, 189], [190, 190], [191, 191], [192, 192], [193, 193], [194, 194], [195, 195], [196, 196], [197, 197], [198, 198], [199, 199]] h = Hash.new(0) ary.each {|p| h[p.class] += 1} assert_equal({Array=>200}, h) end mruby-1.2.0+20160315+git4f20d58a/test/t/basicobject.rb000066400000000000000000000002471267140355100214300ustar00rootroot00000000000000## # BasicObject assert('BasicObject') do assert_equal(Class, BasicObject.class) end assert('BasicObject superclass') do assert_nil(BasicObject.superclass) end mruby-1.2.0+20160315+git4f20d58a/test/t/bs_block.rb000066400000000000000000000151431267140355100207370ustar00rootroot00000000000000## # Bootstrap tests for blocks assert('BS Block 1') do assert_equal(1) do 1.times{ begin a = 1 ensure foo = nil end } end end assert('BS Block 2') do assert_equal 2, [1,2,3].find{|x| x == 2} end assert('BS Block 3') do class E include Enumerable def each(&block) [1, 2, 3].each(&block) end end assert_equal 2, E.new.find {|x| x == 2 } end assert('BS Block 3') do sum = 0 for x in [1, 2, 3] sum += x end assert_equal 6, sum end assert('BS Block 4') do sum = 0 for x in (1..5) sum += x end assert_equal 15, sum end assert('BS Block 5') do sum = 0 for x in [] sum += x end assert_equal 0, sum end assert('BS Block 6') do ans = [] assert_equal(1) do 1.times{ for n in 1..3 a = n ans << a end } end end assert('BS Block 7') do ans = [] assert_equal((1..3)) do for m in 1..3 for n in 2..4 a = [m, n] ans << a end end end end assert('BS Block 8') do assert_equal [1, 2, 3], (1..3).to_a end assert('BS Block 9') do assert_equal([4, 8, 12]) do (1..3).map{|e| e * 4 } end end assert('BS Block 10') do def m yield end def n yield end assert_equal(100) do m{ n{ 100 } } end end assert('BS Block 11') do def m yield 1 end assert_equal(20) do m{|ib| m{|jb| i = 20 } } end end assert('BS Block 12') do def m yield 1 end assert_equal(2) do m{|ib| m{|jb| ib = 20 kb = 2 } } end end assert('BS Block 13') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(3) do iter1{ jb = 2 iter1{ jb = 3 } jb } end end assert('BS Block 14') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(2) do iter1{ jb = 2 iter1{ jb } jb } end end assert('BS Block 15') do def m yield 1 end assert_equal(2) do m{|ib| ib*2 } end end assert('BS Block 16') do def m yield 12345, 67890 end assert_equal(92580) do m{|ib,jb| ib*2+jb } end end assert('BS Block 17') do def iter yield 10 end a = nil assert_equal [10, nil] do [iter{|a| a }, a] end end assert('BS Block 18') do def iter yield 10 end assert_equal(21) do iter{|a| iter{|a| a + 1 } + a } end end assert('BS Block 19') do def iter yield 10, 20, 30, 40 end a = b = c = d = nil assert_equal([10, 20, 30, 40, nil, nil, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b, c, d] end end assert('BS Block 20') do def iter yield 10, 20, 30, 40 end a = b = nil assert_equal([10, 20, 30, 40, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b] end end assert('BS Block 21') do def iter yield 1, 2 end assert_equal([1, [2]]) do iter{|a, *b| [a, b] } end end assert('BS Block 22') do def iter yield 1, 2 end assert_equal([[1, 2]]) do iter{|*a| [a] } end end assert('BS Block 23') do def iter yield 1, 2 end assert_equal([1, 2, []]) do iter{|a, b, *c| [a, b, c] } end end assert('BS Block 24') do def m yield end assert_equal(1) do m{ 1 } end end assert('BS Block 25') do def m yield 123 end assert_equal(15129) do m{|ib| m{|jb| ib*jb } } end end assert('BS Block 26') do def m a yield a end assert_equal(2) do m(1){|ib| m(2){|jb| ib*jb } } end end assert('BS Block 27') do sum = 0 3.times{|ib| 2.times{|jb| sum += ib + jb }} assert_equal sum, 9 end assert('BS Block 28') do assert_equal(10) do 3.times{|bl| break 10 } end end assert('BS Block 29') do def iter yield 1,2,3 end assert_equal([1, 2]) do iter{|i, j| [i, j] } end end assert('BS Block 30') do def iter yield 1 end assert_equal([1, nil]) do iter{|i, j| [i, j] } end end assert('BS Block [ruby-dev:31147]') do def m yield end assert_nil m{|&b| b} end assert('BS Block [ruby-dev:31160]') do def m() yield end assert_nil m {|(v,(*))|} end assert('BS Block [issue #750]') do def m(a, *b) yield end args = [1, 2, 3] assert_equal m(*args){ 1 }, 1 end assert('BS Block 31') do def m() yield end assert_nil m {|((*))|} end assert('BS Block [ruby-dev:31440]') do def m yield [0] end assert_equal m{|v, &b| v}, [0] end assert('BS Block 32') do r = false; 1.times{|&b| r = b} assert_equal NilClass, r.class end assert('BS Block [ruby-core:14395]') do class Controller def respond_to(&block) responder = Responder.new block.call(responder) responder.respond end def test_for_bug respond_to{|format| format.js{ "in test" render{|obj| obj } } } end def render(&block) "in render" end end class Responder def method_missing(symbol, &block) "enter method_missing" @response = Proc.new{ 'in method missing' block.call } "leave method_missing" end def respond @response.call end end t = Controller.new assert_true t.test_for_bug end assert("BS Block 33") do module TestReturnFromNestedBlock def self.test 1.times do 1.times do return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock.test end assert("BS Block 34") do module TestReturnFromNestedBlock_BSBlock34 def self.test 1.times do while true return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock34.test end assert("BS Block 35") do module TestReturnFromNestedBlock_BSBlock35 def self.test 1.times do until false return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock35.test end assert('BS Block 36') do def iter yield 1, 2, 3, 4, 5 end assert_equal([1, 2, [3, 4], 5]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 37') do def iter yield 1, 2, 3 end assert_equal([1, 2, [], 3]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 38') do def iter yield 1,2,3,4,5,6 end assert_equal [1,2,3,4,5], iter{|a,b,c=:c,d,e| [a,b,c,d,e]} end mruby-1.2.0+20160315+git4f20d58a/test/t/bs_literal.rb000066400000000000000000000011171267140355100212750ustar00rootroot00000000000000## # Bootstrap test for literals assert('BS Literal 1') do assert_true true end assert('BS Literal 2') do assert_equal TrueClass, true.class end assert('BS Literal 3') do assert_false false end assert('BS Literal 4') do assert_equal FalseClass, false.class end assert('BS Literal 5') do assert_equal 'nil', nil.inspect end assert('BS Literal 6') do assert_equal NilClass, nil.class end assert('BS Literal 7') do assert_equal Symbol, :sym.class end assert('BS Literal 8') do assert_equal 1234, 1234 end assert('BS Literal 9') do assert_equal Fixnum, 1234.class end mruby-1.2.0+20160315+git4f20d58a/test/t/class.rb000066400000000000000000000160451267140355100202700ustar00rootroot00000000000000## # Class ISO Test assert('Class', '15.2.3') do assert_equal(Class, Class.class) end assert('Class#initialize', '15.2.3.3.1') do c = Class.new do def test :test end end.new assert_equal(c.test, :test) end assert('Class#initialize_copy', '15.2.3.3.2') do class TestClass attr_accessor :n def initialize(n) @n = n end def initialize_copy(obj) @n = n end end c1 = TestClass.new('Foo') c2 = c1.dup c3 = TestClass.new('Bar') assert_equal(c1.n, c2.n) assert_not_equal(c1.n, c3.n) end assert('Class#new', '15.2.3.3.3') do assert_raise(TypeError, 'Singleton should raise TypeError') do "a".singleton_class.new end class TestClass def initialize args, &block @result = if not args.nil? and block.nil? # only arguments :only_args elsif not args.nil? and not block.nil? # args and block is given :args_and_block else # this should never happen :broken end end def result; @result; end end assert_equal(:only_args, TestClass.new(:arg).result) # with block doesn't work yet end assert('Class#superclass', '15.2.3.3.4') do class SubClass < String; end assert_equal(String, SubClass.superclass) end # Not ISO specified assert('Class 1') do class C1; end assert_equal(Class, C1.class) end assert('Class 2') do class C2; end assert_equal(C2, C2.new.class) end assert('Class 3') do class C3; end assert_equal(Class, C3.new.class.class) end assert('Class 4') do class C4_A; end class C4 < C4_A; end assert_equal(Class, C4.class) end assert('Class 5') do class C5_A; end class C5 < C5_A; end assert_equal(C5, C5.new.class) end assert('Class 6') do class C6_A; end class C6 < C6_A; end assert_equal(Class, C6.new.class.class) end assert('Class 7') do class C7_A; end class C7_B; end class C7 < C7_A; end assert_raise(TypeError) do # Different superclass. class C7 < C7_B; end end end assert('Class 8') do class C8_A; end class C8; end # superclass is Object assert_raise(TypeError) do # Different superclass. class C8 < C8_A; end end end assert('Class 9') do Class9Const = "a" assert_raise(TypeError) do class Class9Const; end end end assert('Class Module 1') do module M; end assert_equal(Module, M.class) end assert('Class Module 2') do module M; end class C; include M; end assert_equal(C, C.new.class) end # nested class assert('Class Nested 1') do class A; end class A::B; end assert_equal(A::B, A::B) end assert('Class Nested 2') do class A; end class A::B; end assert_equal(A::B, A::B.new.class) end assert('Class Nested 3') do class A; end class A::B; end assert_equal(Class, A::B.new.class.class) end assert('Class Nested 4') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C) end assert('Class Nested 5') do class A; end class A::B; end class A::B::C; end assert_equal(Class, A::B::C.class) end assert('Class Nested 6') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C.new.class) end assert('Class Nested 7') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(A::B2, A::B2) end assert('Class Nested 8') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(Class, A::B2.class) end assert('Class Colon 1') do class A; end A::C = 1 assert_equal(1, A::C) end assert('Class Colon 2') do class A; class ::C; end end assert_equal(C, C) end assert('Class Colon 3') do class A; class ::C; end end assert_equal(Class, C.class) end assert('Class Dup 1') do class C; end assert_equal(Class, C.dup.class) end assert('Class Dup 2') do module M; end assert_equal(Module, M.dup.class) end assert('Class.new') do assert_equal(Class, Class.new.class) a = [] klass = Class.new do |c| a << c end assert_equal([klass], a) end assert('class to return the last value') do m = class C; :m end assert_equal(m, :m) end assert('raise when superclass is not a class') do module FirstModule; end assert_raise(TypeError, 'should raise TypeError') do class FirstClass < FirstModule; end end class SecondClass; end assert_raise(TypeError, 'should raise TypeError') do class SecondClass < false; end end class ThirdClass; end assert_raise(TypeError, 'should raise TypeError') do class ThirdClass < ThirdClass; end end end assert('Class#inherited') do class Foo @@subclass_name = nil def self.inherited(subclass) @@subclass_name = subclass end def self.subclass_name @@subclass_name end end assert_equal(nil, Foo.subclass_name) class Bar < Foo end assert_equal(Bar, Foo.subclass_name) class Baz < Bar end assert_equal(Baz, Foo.subclass_name) end assert('singleton tests') do module FooMod def run_foo_mod 100 end end bar = String.new baz = class << bar extend FooMod def self.run_baz 200 end end assert_false baz.singleton_methods.include? :run_foo_mod assert_false baz.singleton_methods.include? :run_baz assert_raise(NoMethodError, 'should raise NoMethodError') do baz.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do baz.run_baz end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end baz = class << bar extend FooMod def self.run_baz 300 end self end assert_true baz.singleton_methods.include? :run_baz assert_true baz.singleton_methods.include? :run_foo_mod assert_equal 100, baz.run_foo_mod assert_equal 300, baz.run_baz assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end fv = false class << fv def self.run_false 5 end end nv = nil class << nv def self.run_nil 6 end end tv = true class << tv def self.run_nil 7 end end assert_raise(TypeError, 'should raise TypeError') do num = 1.0 class << num def self.run_nil 7 end end end end assert('clone Class') do class Foo def func true end end Foo.clone.new.func end assert('class variable and class << self style class method') do class ClassVariableTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableTest.class_variable) end assert('class variable in module and class << self style class method') do module ClassVariableInModuleTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableInModuleTest.class_variable) end assert('class with non-class/module outer raises TypeError') do assert_raise(TypeError) { class 0::C1; end } assert_raise(TypeError) { class []::C2; end } end mruby-1.2.0+20160315+git4f20d58a/test/t/comparable.rb000066400000000000000000000026701267140355100212670ustar00rootroot00000000000000 assert('Comparable#<', '15.3.3.2.1') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new < 0) assert_false(Foo.new < 1) assert_true(Foo.new < -1) assert_raise(ArgumentError){ Foo.new < nil } end assert('Comparable#<=', '15.3.3.2.2') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new <= 0) assert_false(Foo.new <= 1) assert_true(Foo.new <= -1) assert_raise(ArgumentError){ Foo.new <= nil } end assert('Comparable#==', '15.3.3.2.3') do class Foo include Comparable def <=>(x) 0 end end assert_true(Foo.new == Foo.new) end assert('Comparable#>', '15.3.3.2.4') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new > 0) assert_true(Foo.new > 1) assert_false(Foo.new > -1) assert_raise(ArgumentError){ Foo.new > nil } end assert('Comparable#>=', '15.3.3.2.5') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new >= 0) assert_true(Foo.new >= 1) assert_false(Foo.new >= -1) assert_raise(ArgumentError){ Foo.new >= nil } end assert('Comparable#between?', '15.3.3.2.6') do class Foo include Comparable def <=>(x) x end end c = Foo.new assert_false(c.between?(-1, 1)) assert_false(c.between?(-1, -1)) assert_false(c.between?( 1, 1)) assert_true(c.between?( 1, -1)) assert_true(c.between?(0, 0)) end mruby-1.2.0+20160315+git4f20d58a/test/t/ensure.rb000066400000000000000000000016461267140355100204650ustar00rootroot00000000000000## # ensure Test assert('ensure - context - yield') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new yielder.try do end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and break') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new yielder.try do break end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and return') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new lambda do yielder.try do return end end.call assert_equal yielder, yielder.ensure_context end mruby-1.2.0+20160315+git4f20d58a/test/t/enumerable.rb000066400000000000000000000060141267140355100212750ustar00rootroot00000000000000## # Enumerable ISO Test assert('Enumerable', '15.3.2') do assert_equal(Module, Enumerable.class) end assert('Enumerable#all?', '15.3.2.2.1') do assert_true([1,2,3].all?) assert_false([1,false,3].all?) a = [2,4,6] all = a.all? do |e| e % 2 == 0 end assert_true(all) a = [2,4,7] all = a.all? do |e| e % 2 == 0 end assert_false(all) end assert('Enumerable#any?', '15.3.2.2.2') do assert_true([false,true,false].any?) assert_false([false,false,false].any?) a = [1,3,6] any = a.any? do |e| e % 2 == 0 end assert_true(any) a = [1,3,5] any = a.any? do |e| e % 2 == 0 end assert_false(any) end assert('Enumerable#collect', '15.3.2.2.3') do assert_true [1,2,3].collect { |i| i + i } == [2,4,6] end assert('Enumerable#detect', '15.3.2.2.4') do assert_equal 1, [1,2,3].detect() { true } assert_equal 'a', [1,2,3].detect("a") { false } end assert('Array#each_with_index', '15.3.2.2.5') do a = nil b = nil [1].each_with_index {|e,i| a = e; b = i} assert_equal(1, a) assert_equal(0, b) end assert('Enumerable#entries', '15.3.2.2.6') do assert_equal([1], [1].entries) end assert('Enumerable#find', '15.3.2.2.7') do assert_equal 1, [1,2,3].find() { true } assert_equal 'a', [1,2,3].find("a") { false } end assert('Enumerable#find_all', '15.3.2.2.8') do assert_true [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}, [2,4,6,8] end assert('Enumerable#grep', '15.3.2.2.9') do assert_equal [4,5,6], [1,2,3,4,5,6,7,8,9].grep(4..6) end assert('Enumerable#include?', '15.3.2.2.10') do assert_true [1,2,3,4,5,6,7,8,9].include?(5) assert_false [1,2,3,4,5,6,7,8,9].include?(0) end assert('Enumerable#inject', '15.3.2.2.11') do assert_equal 21, [1,2,3,4,5,6].inject() {|s, n| s + n} assert_equal 22, [1,2,3,4,5,6].inject(1) {|s, n| s + n} end assert('Enumerable#map', '15.3.2.2.12') do assert_equal [2,4,6], [1,2,3].map { |i| i + i } end assert('Enumerable#max', '15.3.2.2.13') do a = ['aaa', 'bb', 'c'] assert_equal 'c', a.max assert_equal 'aaa', a.max {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#min', '15.3.2.2.14') do a = ['aaa', 'bb', 'c'] assert_equal 'aaa', a.min assert_equal 'c', a.min {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#member?', '15.3.2.2.15') do assert_true [1,2,3,4,5,6,7,8,9].member?(5) assert_false [1,2,3,4,5,6,7,8,9].member?(0) end assert('Enumerable#partition', '15.3.2.2.16') do partition = [0,1,2,3,4,5,6,7,8,9].partition do |i| i % 2 == 0 end assert_equal [[0,2,4,6,8], [1,3,5,7,9]], partition end assert('Enumerable#reject', '15.3.2.2.17') do reject = [0,1,2,3,4,5,6,7,8,9].reject do |i| i % 2 == 0 end assert_equal [1,3,5,7,9], reject end assert('Enumerable#select', '15.3.2.2.18') do assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].select() {|i| i%2 == 0} end assert('Enumerable#sort', '15.3.2.2.19') do assert_equal [1,2,3,4,6,7], [7,3,1,2,6,4].sort assert_equal [7,6,4,3,2,1], [7,3,1,2,6,4].sort {|e1,e2|e2<=>e1} end assert('Enumerable#to_a', '15.3.2.2.20') do assert_equal [1], [1].to_a end mruby-1.2.0+20160315+git4f20d58a/test/t/exception.rb000066400000000000000000000133551267140355100211620ustar00rootroot00000000000000## # Exception ISO Test assert('Exception', '15.2.22') do assert_equal Class, Exception.class end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception('a') assert_equal Exception, e.class end assert('Exception#exception', '15.2.22.5.1') do e = Exception.new re = RuntimeError.new assert_equal e, e.exception assert_equal e, e.exception(e) assert_equal re, re.exception(re) changed_re = re.exception('message has changed') assert_not_equal re, changed_re assert_equal 'message has changed', changed_re.message end assert('Exception#message', '15.2.22.5.2') do e = Exception.exception('a') assert_equal 'a', e.message end assert('Exception#to_s', '15.2.22.5.3') do e = Exception.exception('a') assert_equal 'a', e.to_s end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception() e.initialize('a') assert_equal 'a', e.message end assert('NameError', '15.2.31') do assert_raise(NameError) do raise NameError.new end e = NameError.new "msg", "name" assert_equal "msg", e.message assert_equal "name", e.name end assert('ScriptError', '15.2.37') do assert_raise(ScriptError) do raise ScriptError.new end end assert('SyntaxError', '15.2.38') do assert_raise(SyntaxError) do raise SyntaxError.new end end # Not ISO specified assert('Exception 1') do r=begin 1+1 ensure 2+2 end assert_equal 2, r end assert('Exception 2') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 end assert_equal 4, r end assert('Exception 3') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 begin 5+5 ensure 6+6 end end assert_equal 4, r end assert('Exception 4') do a = nil 1.times{|e| begin rescue => err end a = err.class } assert_equal NilClass, a end assert('Exception 5') do $ans = [] def m $! end def m2 1.times{ begin return ensure $ans << m end } end m2 assert_equal [nil], $ans end assert('Exception 6') do $i = 0 def m iter{ begin $i += 1 begin $i += 2 break ensure end ensure $i += 4 end $i = 0 } end def iter yield end m assert_equal 7, $i end assert('Exception 7') do $i = 0 def m begin $i += 1 begin $i += 2 return ensure $i += 3 end ensure $i += 4 end p :end end m assert_equal 10, $i end assert('Exception 8') do r=begin 1 rescue 2 else 3 end assert_equal 3, r end assert('Exception 9') do r=begin 1+1 rescue 2+2 else 3+3 ensure 4+4 end assert_equal 6, r end assert('Exception 10') do r=begin 1+1 begin 2+2 rescue 3+3 else 4+4 end rescue 5+5 else 6+6 ensure 7+7 end assert_equal 12, r end assert('Exception 11') do a = :ok begin begin raise Exception rescue a = :ng end rescue Exception end assert_equal :ok, a end assert('Exception 12') do a = :ok begin raise Exception rescue a = :ng rescue Exception end assert_equal :ok, a end assert('Exception 13') do a = :ng begin raise StandardError rescue TypeError, ArgumentError a = :ng rescue a = :ok else a = :ng end assert_equal :ok, a end assert('Exception 14') do def exception_test14; UnknownConstant; end a = :ng begin send(:exception_test14) rescue a = :ok end assert_equal :ok, a end assert('Exception 15') do a = begin :ok rescue :ko end assert_equal :ok, a end assert('Exception 16') do begin raise "foo" false rescue => e assert_equal "foo", e.message end end assert('Exception 17') do r=begin raise "a" # RuntimeError rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 2, r end assert('Exception 18') do r=begin 0 rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 3, r end assert('Exception 19') do class Class4Exception19 def a r = @e = false begin b rescue TypeError r = self.z end [ r, @e ] end def b begin 1 * "b" ensure @e = self.z end end def z true end end assert_equal [true, true], Class4Exception19.new.a end assert('Exception#inspect without message') do assert_equal "Exception: Exception", Exception.new.inspect end assert('Exception#backtrace') do assert_nothing_raised do begin raise "get backtrace" rescue => e e.backtrace end end end assert('Raise in ensure') do assert_raise(ArgumentError) do begin raise "" # RuntimeError ensure raise ArgumentError end end end def backtrace_avaialble? begin raise "XXX" rescue => exception not exception.backtrace.empty? end end assert('GC in rescue') do skip "backtrace isn't avaialble" unless backtrace_avaialble? line = nil begin [1].each do [2].each do [3].each do line = __LINE__; raise "XXX" end end end rescue => exception GC.start assert_equal("#{__FILE__}:#{line}:in Object.call", exception.backtrace.first) end end assert('Method call in rescue') do skip "backtrace isn't avaialble" unless backtrace_avaialble? line = nil begin [1].each do [2].each do line = __LINE__; raise "XXX" end end rescue => exception [3].each do end assert_equal("#{__FILE__}:#{line}:in Object.call", exception.backtrace.first) end end mruby-1.2.0+20160315+git4f20d58a/test/t/false.rb000066400000000000000000000012121267140355100202430ustar00rootroot00000000000000## # FalseClass ISO Test assert('FalseClass', '15.2.6') do assert_equal Class, FalseClass.class end assert('FalseClass false', '15.2.6.1') do assert_false false assert_equal FalseClass, false.class assert_false FalseClass.method_defined? :new end assert('FalseClass#&', '15.2.6.3.1') do assert_false false.&(true) assert_false false.&(false) end assert('FalseClass#^', '15.2.6.3.2') do assert_true false.^(true) assert_false false.^(false) end assert('FalseClass#to_s', '15.2.6.3.3') do assert_equal 'false', false.to_s end assert('FalseClass#|', '15.2.6.3.4') do assert_true false.|(true) assert_false false.|(false) end mruby-1.2.0+20160315+git4f20d58a/test/t/float.rb000066400000000000000000000102461267140355100202650ustar00rootroot00000000000000## # Float ISO Test assert('Float', '15.2.9') do assert_equal Class, Float.class end assert('Float#+', '15.2.9.3.1') do a = 3.123456788 + 0.000000001 b = 3.123456789 + 1 assert_float(3.123456789, a) assert_float(4.123456789, b) assert_raise(TypeError){ 0.0+nil } assert_raise(TypeError){ 1.0+nil } end assert('Float#-', '15.2.9.3.2') do a = 3.123456790 - 0.000000001 b = 5.123456789 - 1 assert_float(3.123456789, a) assert_float(4.123456789, b) end assert('Float#*', '15.2.9.3.3') do a = 3.125 * 3.125 b = 3.125 * 1 assert_float(9.765625, a) assert_float(3.125 , b) end assert('Float#/', '15.2.9.3.4') do a = 3.123456789 / 3.123456789 b = 3.123456789 / 1 assert_float(1.0 , a) assert_float(3.123456789, b) end assert('Float#%', '15.2.9.3.5') do a = 3.125 % 3.125 b = 3.125 % 1 assert_float(0.0 , a) assert_float(0.125, b) end assert('Float#<=>', '15.2.9.3.6') do a = 3.125 <=> 3.123 b = 3.125 <=> 3.125 c = 3.125 <=> 3.126 a2 = 3.125 <=> 3 c2 = 3.125 <=> 4 assert_equal( 1, a) assert_equal( 0, b) assert_equal(-1, c) assert_equal( 1, a2) assert_equal(-1, c2) end assert('Float#==', '15.2.9.3.7') do assert_true 3.1 == 3.1 assert_false 3.1 == 3.2 end assert('Float#ceil', '15.2.9.3.8') do a = 3.123456789.ceil b = 3.0.ceil c = -3.123456789.ceil d = -3.0.ceil assert_equal( 4, a) assert_equal( 3, b) assert_equal(-3, c) assert_equal(-3, d) end assert('Float#finite?', '15.2.9.3.9') do assert_true 3.123456789.finite? assert_false (1.0 / 0.0).finite? end assert('Float#floor', '15.2.9.3.10') do a = 3.123456789.floor b = 3.0.floor c = -3.123456789.floor d = -3.0.floor assert_equal( 3, a) assert_equal( 3, b) assert_equal(-4, c) assert_equal(-3, d) end assert('Float#infinite?', '15.2.9.3.11') do a = 3.123456789.infinite? b = (1.0 / 0.0).infinite? c = (-1.0 / 0.0).infinite? assert_nil a assert_equal( 1, b) assert_equal(-1, c) end assert('Float#round', '15.2.9.3.12') do a = 3.123456789.round b = 3.5.round c = 3.4999.round d = (-3.123456789).round e = (-3.5).round f = 12345.67.round(-1) g = 3.423456789.round(0) h = 3.423456789.round(1) i = 3.423456789.round(3) assert_equal( 3, a) assert_equal( 4, b) assert_equal( 3, c) assert_equal( -3, d) assert_equal( -4, e) assert_equal(12350, f) assert_equal( 3, g) assert_float( 3.4, h) assert_float(3.423, i) assert_equal(42.0, 42.0.round(307)) assert_equal(1.0e307, 1.0e307.round(2)) inf = 1.0/0.0 assert_raise(FloatDomainError){ inf.round } assert_raise(FloatDomainError){ inf.round(-1) } assert_equal(inf, inf.round(1)) nan = 0.0/0.0 assert_raise(FloatDomainError){ nan.round } assert_raise(FloatDomainError){ nan.round(-1) } assert_true(nan.round(1).nan?) end assert('Float#to_f', '15.2.9.3.13') do a = 3.123456789 assert_float(a, a.to_f) end assert('Float#to_i', '15.2.9.3.14') do assert_equal(3, 3.123456789.to_i) end assert('Float#truncate', '15.2.9.3.15') do assert_equal( 3, 3.123456789.truncate) assert_equal(-3, -3.1.truncate) end assert('Float#divmod') do def check_floats exp, act assert_float exp[0], act[0] assert_float exp[1], act[1] end # Note: quotients are Float because mruby does not have Bignum. check_floats [ 0, 0.0], 0.0.divmod(1) check_floats [ 0, 1.1], 1.1.divmod(3) check_floats [ 3, 0.2], 3.2.divmod(1) check_floats [ 2, 6.3], 20.3.divmod(7) check_floats [-1, 1.6], -3.4.divmod(5) check_floats [-2, -0.5], 25.5.divmod(-13) check_floats [ 1, -6.6], -13.6.divmod(-7) check_floats [ 3, 0.2], 9.8.divmod(3.2) end assert('Float#nan?') do assert_true (0.0/0.0).nan? assert_false 0.0.nan? assert_false (1.0/0.0).nan? assert_false (-1.0/0.0).nan? end assert('Float#<<') do # Left Shift by one assert_equal 46, 23.0 << 1 # Left Shift by a negative is Right Shift assert_equal 23, 46.0 << -1 end assert('Float#>>') do # Right Shift by one assert_equal 23, 46.0 >> 1 # Right Shift by a negative is Left Shift assert_equal 46, 23.0 >> -1 # Don't raise on large Right Shift assert_equal 0, 23.0 >> 128 # Don't raise on large Right Shift assert_equal(-1, -23.0 >> 128) end mruby-1.2.0+20160315+git4f20d58a/test/t/gc.rb000066400000000000000000000015011267140355100175430ustar00rootroot00000000000000# Not ISO specified assert('GC.enable') do assert_false GC.disable assert_true GC.enable assert_false GC.enable end assert('GC.disable') do begin assert_false GC.disable assert_true GC.disable ensure GC.enable end end assert('GC.interval_ratio=') do origin = GC.interval_ratio begin assert_equal 150, (GC.interval_ratio = 150) ensure GC.interval_ratio = origin end end assert('GC.step_ratio=') do origin = GC.step_ratio begin assert_equal 150, (GC.step_ratio = 150) ensure GC.step_ratio = origin end end assert('GC.generational_mode=') do origin = GC.generational_mode begin assert_false (GC.generational_mode = false) assert_true (GC.generational_mode = true) assert_true (GC.generational_mode = true) ensure GC.generational_mode = origin end end mruby-1.2.0+20160315+git4f20d58a/test/t/hash.rb000066400000000000000000000162041267140355100201030ustar00rootroot00000000000000## # Hash ISO Test assert('Hash', '15.2.13') do assert_equal Class, Hash.class end assert('Hash#==', '15.2.13.4.1') do assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' }) assert_false({ 'abc' => 'abc' } == { 'cba' => 'cba' }) assert_true({ :equal => 1 } == { :equal => 1.0 }) assert_false({ :a => 1 } == true) end assert('Hash#[]', '15.2.13.4.2') do a = { 'abc' => 'abc' } assert_equal 'abc', a['abc'] end assert('Hash#[]=', '15.2.13.4.3') do a = Hash.new a['abc'] = 'abc' assert_equal 'abc', a['abc'] end assert('Hash#clear', '15.2.13.4.4') do a = { 'abc' => 'abc' } a.clear assert_equal({ }, a) end assert('Hash#dup') do a = { 'a' => 1 } b = a.dup a['a'] = 2 assert_equal({'a' => 1}, b) end assert('Hash#default', '15.2.13.4.5') do a = Hash.new b = Hash.new('abc') c = Hash.new {|s,k| s[k] = k} assert_nil a.default assert_equal 'abc', b.default assert_nil c.default assert_equal 'abc', c.default('abc') end assert('Hash#default=', '15.2.13.4.6') do a = { 'abc' => 'abc' } a.default = 'cba' assert_equal 'abc', a['abc'] assert_equal 'cba', a['notexist'] end assert('Hash#default_proc', '15.2.13.4.7') do a = Hash.new b = Hash.new {|s,k| s[k] = k + k} c = b[2] d = b['cat'] assert_nil a.default_proc assert_equal Proc, b.default_proc.class assert_equal 4, c assert_equal 'catcat', d end assert('Hash#delete', '15.2.13.4.8') do a = { 'abc' => 'abc' } b = { 'abc' => 'abc' } b_tmp_1 = false b_tmp_2 = false a.delete('abc') b.delete('abc') do |k| b_tmp_1 = true end b.delete('abc') do |k| b_tmp_2 = true end assert_nil a.delete('cba') assert_false a.has_key?('abc') assert_false b_tmp_1 assert_true b_tmp_2 end assert('Hash#each', '15.2.13.4.9') do a = { 'abc_key' => 'abc_value' } key = nil value = nil a.each do |k,v| key = k value = v end assert_equal 'abc_key', key assert_equal 'abc_value', value end assert('Hash#each_key', '15.2.13.4.10') do a = { 'abc_key' => 'abc_value' } key = nil a.each_key do |k| key = k end assert_equal 'abc_key', key end assert('Hash#each_value', '15.2.13.4.11') do a = { 'abc_key' => 'abc_value' } value = nil a.each_value do |v| value = v end assert_equal 'abc_value', value end assert('Hash#empty?', '15.2.13.4.12') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_false a.empty? assert_true b.empty? end assert('Hash#has_key?', '15.2.13.4.13') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.has_key?('abc_key') assert_false b.has_key?('cba') end assert('Hash#has_value?', '15.2.13.4.14') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.has_value?('abc_value') assert_false b.has_value?('cba') end assert('Hash#include?', '15.2.13.4.15') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.include?('abc_key') assert_false b.include?('cba') end assert('Hash#initialize', '15.2.13.4.16') do # Testing initialize by new. h = Hash.new h2 = Hash.new(:not_found) assert_true h.is_a? Hash assert_equal({ }, h) assert_nil h["hello"] assert_equal :not_found, h2["hello"] end assert('Hash#initialize_copy', '15.2.13.4.17') do a = { 'abc_key' => 'abc_value' } b = Hash.new.initialize_copy(a) assert_equal({ 'abc_key' => 'abc_value' }, b) end assert('Hash#key?', '15.2.13.4.18') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.key?('abc_key') assert_false b.key?('cba') end assert('Hash#keys', '15.2.13.4.19') do a = { 'abc_key' => 'abc_value' } assert_equal ['abc_key'], a.keys end assert('Hash#length', '15.2.13.4.20') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_equal 1, a.length assert_equal 0, b.length end assert('Hash#member?', '15.2.13.4.21') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.member?('abc_key') assert_false b.member?('cba') end assert('Hash#merge', '15.2.13.4.22') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' } result_1 = a.merge b 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#replace', '15.2.13.4.23') do a = { 'abc_key' => 'abc_value' } b = Hash.new.replace(a) assert_equal({ 'abc_key' => 'abc_value' }, b) a = Hash.new(42) b = {} b.replace(a) assert_equal(42, b[1]) a = Hash.new{|h,x| x} b.replace(a) assert_equal(127, b[127]) assert_raise(TypeError) do { 'abc_key' => 'abc_value' }.replace "a" end end assert('Hash#shift', '15.2.13.4.24') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = a.shift assert_equal Array, b.class assert_equal 2, b.size assert_equal 1, a.size b = a.shift assert_equal Array, b.class assert_equal 2, b.size assert_equal 0, a.size end assert('Hash#size', '15.2.13.4.25') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_equal 1, a.size assert_equal 0, b.size end assert('Hash#store', '15.2.13.4.26') do a = Hash.new a.store('abc', 'abc') assert_equal 'abc', a['abc'] end assert('Hash#value?', '15.2.13.4.27') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.value?('abc_value') assert_false b.value?('cba') end assert('Hash#values', '15.2.13.4.28') do a = { 'abc_key' => 'abc_value' } assert_equal ['abc_value'], a.values end # Not ISO specified assert('Hash#eql?') do a = { 'a' => 1, 'b' => 2, 'c' => 3 } b = { 'a' => 1, 'b' => 2, 'c' => 3 } c = { 'a' => 1.0, 'b' => 2, 'c' => 3 } assert_true(a.eql?(b)) assert_false(a.eql?(c)) assert_false(a.eql?(true)) end assert('Hash#reject') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.reject do |k,v| v % 2 == 0 end assert_equal({:one => 1, :three => 3}, ret) assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h) end assert('Hash#reject!') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.reject! do |k,v| v % 2 == 0 end assert_equal({:one => 1, :three => 3}, ret) assert_equal({:one => 1, :three => 3}, h) end assert('Hash#select') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.select do |k,v| v % 2 == 0 end assert_equal({:two => 2, :four => 4}, ret) assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h) end assert('Hash#select!') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.select! do |k,v| v % 2 == 0 end assert_equal({:two => 2, :four => 4}, ret) assert_equal({:two => 2, :four => 4}, h) end # Not ISO specified assert('Hash#inspect') do h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 } ret = h.to_s assert_include ret, '"c"=>300' assert_include ret, '"a"=>100' assert_include ret, '"d"=>400' end assert('Hash#rehash') do h = {[:a] => "b"} # hash key modified h.keys[0][0] = :b # h[[:b]] => nil h.rehash assert_equal("b", h[[:b]]) end mruby-1.2.0+20160315+git4f20d58a/test/t/indexerror.rb000066400000000000000000000001501267140355100213320ustar00rootroot00000000000000## # IndexError ISO Test assert('IndexError', '15.2.33') do assert_equal Class, IndexError.class end mruby-1.2.0+20160315+git4f20d58a/test/t/integer.rb000066400000000000000000000111751267140355100206170ustar00rootroot00000000000000## # Integer ISO Test assert('Integer', '15.2.8') do assert_equal Class, Integer.class end assert('Integer#+', '15.2.8.3.1') do a = 1+1 b = 1+1.0 assert_equal 2, a assert_equal 2.0, b assert_raise(TypeError){ 0+nil } assert_raise(TypeError){ 1+nil } c = Mrbtest::FIXNUM_MAX + 1 d = Mrbtest::FIXNUM_MAX.__send__(:+, 1) e = Mrbtest::FIXNUM_MAX + 1.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end assert('Integer#-', '15.2.8.3.2') do a = 2-1 b = 2-1.0 assert_equal 1, a assert_equal 1.0, b c = Mrbtest::FIXNUM_MIN - 1 d = Mrbtest::FIXNUM_MIN.__send__(:-, 1) e = Mrbtest::FIXNUM_MIN - 1.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end assert('Integer#*', '15.2.8.3.3') do a = 1*1 b = 1*1.0 assert_equal 1, a assert_equal 1.0, b assert_raise(TypeError){ 0*nil } assert_raise(TypeError){ 1*nil } c = Mrbtest::FIXNUM_MAX * 2 d = Mrbtest::FIXNUM_MAX.__send__(:*, 2) e = Mrbtest::FIXNUM_MAX * 2.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end assert('Integer#/', '15.2.8.3.4') do a = 2/1 b = 2/1.0 assert_equal 2, a assert_equal 2.0, b end assert('Integer#%', '15.2.8.3.5') do a = 1%1 b = 1%1.0 c = 2%4 d = 2%5 e = 2%-5 f = -2%5 g = -2%-5 h = 2%-2 i = -2%2 j = -2%-2 assert_equal 0, a assert_equal 0.0, b assert_equal 2, c assert_equal 2, d assert_equal(-3, e) assert_equal 3, f assert_equal(-2, g) assert_equal 0, h assert_equal 0, i assert_equal 0, j end assert('Integer#<=>', '15.2.9.3.6') do a = 1<=>0 b = 1<=>1 c = 1<=>2 assert_equal 1, a assert_equal 0, b assert_equal(-1, c) end assert('Integer#==', '15.2.8.3.7') do a = 1==0 b = 1==1 assert_false a assert_true b end assert('Integer#~', '15.2.8.3.8') do # Complement assert_equal(-1, ~0) assert_equal(-3, ~2) end assert('Integer#&', '15.2.8.3.9') do # Bitwise AND # 0101 (5) # & 0011 (3) # = 0001 (1) assert_equal 1, 5 & 3 end assert('Integer#|', '15.2.8.3.10') do # Bitwise OR # 0101 (5) # | 0011 (3) # = 0111 (7) assert_equal 7, 5 | 3 end assert('Integer#^', '15.2.8.3.11') do # Bitwise XOR # 0101 (5) # ^ 0011 (3) # = 0110 (6) assert_equal 6, 5 ^ 3 end assert('Integer#<<', '15.2.8.3.12') do # Left Shift by one # 00010111 (23) # = 00101110 (46) assert_equal 46, 23 << 1 # Left Shift by a negative is Right Shift assert_equal 23, 46 << -1 # Left Shift by 31 is bitShift overflow to SignedInt assert_equal 2147483648, 1 << 31 # -3 Left Shift by 30 is bitShift overflow to SignedInt assert_equal(-3221225472, -3 << 30) end assert('Integer#>>', '15.2.8.3.13') do # Right Shift by one # 00101110 (46) # = 00010111 (23) assert_equal 23, 46 >> 1 # Right Shift by a negative is Left Shift assert_equal 46, 23 >> -1 # Don't raise on large Right Shift assert_equal 0, 23 >> 128 end assert('Integer#ceil', '15.2.8.3.14') do assert_equal 10, 10.ceil end assert('Integer#downto', '15.2.8.3.15') do a = 0 3.downto(1) do |i| a += i end assert_equal 6, a end assert('Integer#eql?', '15.2.8.3.16') do a = 1.eql?(1) b = 1.eql?(2) c = 1.eql?(nil) assert_true a assert_false b assert_false c end assert('Integer#floor', '15.2.8.3.17') do a = 1.floor assert_equal 1, a end assert('Integer#next', '15.2.8.3.19') do assert_equal 2, 1.next end assert('Integer#round', '15.2.8.3.20') do assert_equal 1, 1.round end assert('Integer#succ', '15.2.8.3.21') do assert_equal 2, 1.succ end assert('Integer#times', '15.2.8.3.22') do a = 0 3.times do a += 1 end assert_equal 3, a end assert('Integer#to_f', '15.2.8.3.23') do assert_equal 1.0, 1.to_f end assert('Integer#to_i', '15.2.8.3.24') do assert_equal 1, 1.to_i end assert('Integer#to_s', '15.2.8.3.25') do assert_equal '1', 1.to_s assert_equal("-1", -1.to_s) end assert('Integer#truncate', '15.2.8.3.26') do assert_equal 1, 1.truncate end assert('Integer#upto', '15.2.8.3.27') do a = 0 1.upto(3) do |i| a += i end assert_equal 6, a end assert('Integer#divmod', '15.2.8.3.30') do assert_equal [ 0, 0], 0.divmod(1) assert_equal [ 0, 1], 1.divmod(3) assert_equal [ 3, 0], 3.divmod(1) assert_equal [ 2, 6], 20.divmod(7) assert_equal [-1, 2], -3.divmod(5) assert_equal [-2, -1], 25.divmod(-13) assert_equal [ 1, -6], -13.divmod(-7) end # Not ISO specified assert('Integer#step') do a = [] b = [] 1.step(3) do |i| a << i end 1.step(6, 2) do |i| b << i end assert_equal [1, 2, 3], a assert_equal [1, 3, 5], b end mruby-1.2.0+20160315+git4f20d58a/test/t/kernel.rb000066400000000000000000000266321267140355100204460ustar00rootroot00000000000000## # Kernel ISO Test assert('Kernel', '15.3.1') do assert_equal Module, Kernel.class end assert('Kernel.block_given?', '15.3.1.2.2') do def bg_try(&b) if Kernel.block_given? yield else "no block" end end assert_false Kernel.block_given? # test without block assert_equal "no block", bg_try # test with block assert_equal "block" do bg_try { "block" } end # test with block assert_equal "block" do bg_try do "block" end end end # Kernel.eval is provided by the mruby-gem mrbgem. '15.3.1.2.3' assert('Kernel.global_variables', '15.3.1.2.4') do assert_equal Array, Kernel.global_variables.class end assert('Kernel.iterator?', '15.3.1.2.5') do assert_false Kernel.iterator? end assert('Kernel.lambda', '15.3.1.2.6') do l = Kernel.lambda do true end m = Kernel.lambda(&l) assert_true l.call assert_equal Proc, l.class assert_true m.call assert_equal Proc, m.class end # Not implemented at the moment #assert('Kernel.local_variables', '15.3.1.2.7') do # Kernel.local_variables.class == Array #end assert('Kernel.loop', '15.3.1.2.8') do i = 0 Kernel.loop do i += 1 break if i == 100 end assert_equal 100, i end assert('Kernel.p', '15.3.1.2.9') do # TODO search for a way to test p to stdio assert_true true end assert('Kernel.print', '15.3.1.2.10') do # TODO search for a way to test print to stdio assert_true true end assert('Kernel.puts', '15.3.1.2.11') do # TODO search for a way to test puts to stdio assert_true true end assert('Kernel.raise', '15.3.1.2.12') do assert_raise RuntimeError do Kernel.raise end assert_raise RuntimeError do Kernel.raise RuntimeError.new end end assert('Kernel#__id__', '15.3.1.3.3') do assert_equal Fixnum, __id__.class end assert('Kernel#__send__', '15.3.1.3.4') do # test with block l = __send__(:lambda) do true end assert_true l.call assert_equal Proc, l.class # test with argument assert_true __send__(:respond_to?, :nil?) # test without argument and without block assert_equal Array, __send__(:public_methods).class end assert('Kernel#block_given?', '15.3.1.3.6') do def bg_try(&b) if block_given? yield else "no block" end end assert_false block_given? assert_equal "no block", bg_try assert_equal "block" do bg_try { "block" } end assert_equal "block" do bg_try do "block" end end end assert('Kernel#class', '15.3.1.3.7') do assert_equal Module, Kernel.class end assert('Kernel#clone', '15.3.1.3.8') do class KernelCloneTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelCloneTest.new a.set(1) b = a.clone def a.test end a.set(2) c = a.clone immutables = [ 1, :foo, true, false, nil ] error_count = 0 immutables.each do |i| begin i.clone rescue TypeError error_count += 1 end end assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_true c.respond_to?(:test) end assert('Kernel#dup', '15.3.1.3.9') do class KernelDupTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelDupTest.new a.set(1) b = a.dup def a.test end a.set(2) c = a.dup immutables = [ 1, :foo, true, false, nil ] error_count = 0 immutables.each do |i| begin i.dup rescue TypeError error_count += 1 end end assert_equal immutables.size, error_count assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_false c.respond_to?(:test) end # Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12' assert('Kernel#extend', '15.3.1.3.13') do class Test4ExtendClass end module Test4ExtendModule def test_method; end end a = Test4ExtendClass.new a.extend(Test4ExtendModule) b = Test4ExtendClass.new assert_true a.respond_to?(:test_method) assert_false b.respond_to?(:test_method) end assert('Kernel#extend works on toplevel', '15.3.1.3.13') do module Test4ExtendModule def test_method; end end # This would crash... extend(Test4ExtendModule) assert_true respond_to?(:test_method) end assert('Kernel#global_variables', '15.3.1.3.14') do assert_equal Array, global_variables.class end assert('Kernel#hash', '15.3.1.3.15') do assert_equal hash, hash end assert('Kernel#inspect', '15.3.1.3.17') do s = inspect assert_equal String, s.class assert_equal "main", s end assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do o = Object.new o.instance_variable_set(:@a, 1) assert_true o.instance_variable_defined?("@a") assert_false o.instance_variable_defined?("@b") assert_true o.instance_variable_defined?("@a"[0,2]) assert_true o.instance_variable_defined?("@abc"[0,2]) end assert('Kernel#instance_variables', '15.3.1.3.23') do o = Object.new o.instance_eval do @a = 11 @b = 12 end ivars = o.instance_variables assert_equal Array, ivars.class, assert_equal(2, ivars.size) assert_true ivars.include?(:@a) assert_true ivars.include?(:@b) end assert('Kernel#is_a?', '15.3.1.3.24') do assert_true is_a?(Kernel) assert_false is_a?(Array) assert_raise TypeError do 42.is_a?(42) end end assert('Kernel#iterator?', '15.3.1.3.25') do assert_false iterator? end assert('Kernel#kind_of?', '15.3.1.3.26') do assert_true kind_of?(Kernel) assert_false kind_of?(Array) end assert('Kernel#lambda', '15.3.1.3.27') do l = lambda do true end m = lambda(&l) assert_true l.call assert_equal Proc, l.class assert_true m.call assert_equal Proc, m.class end # Not implemented yet #assert('Kernel#local_variables', '15.3.1.3.28') do # local_variables.class == Array #end assert('Kernel#loop', '15.3.1.3.29') do i = 0 loop do i += 1 break if i == 100 end assert_equal i, 100 end assert('Kernel#method_missing', '15.3.1.3.30') do class MMTestClass def method_missing(sym) "A call to #{sym}" end end mm_test = MMTestClass.new assert_equal 'A call to no_method_named_this', mm_test.no_method_named_this a = String.new begin a.no_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_method_named_this' for \"\"", e.message end class ShortInspectClass def inspect 'An inspect string' end end b = ShortInspectClass.new begin b.no_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_method_named_this' for An inspect string", e.message end class LongInspectClass def inspect "A" * 70 end end c = LongInspectClass.new begin c.no_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_method_named_this' for #{c.to_s}", e.message end class NoInspectClass undef inspect end d = NoInspectClass.new begin d.no_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_method_named_this' for #{d.to_s}", e.message end end assert('Kernel#methods', '15.3.1.3.31') do assert_equal Array, methods.class end assert('Kernel#nil?', '15.3.1.3.32') do assert_false nil? end assert('Kernel#object_id', '15.3.1.3.33') do a = "" b = "" assert_not_equal a.object_id, b.object_id assert_kind_of Numeric, object_id assert_kind_of Numeric, "".object_id assert_kind_of Numeric, true.object_id assert_kind_of Numeric, false.object_id assert_kind_of Numeric, nil.object_id assert_kind_of Numeric, :no.object_id assert_kind_of Numeric, 1.object_id assert_kind_of Numeric, 1.0.object_id end # Kernel#p is defined in mruby-print mrbgem. '15.3.1.3.34' # Kernel#print is defined in mruby-print mrbgem. '15.3.1.3.35' assert('Kernel#private_methods', '15.3.1.3.36') do assert_equal Array, private_methods.class end assert('Kernel#protected_methods', '15.3.1.3.37') do assert_equal Array, protected_methods.class end assert('Kernel#public_methods', '15.3.1.3.38') do assert_equal Array, public_methods.class end # Kernel#puts is defined in mruby-print mrbgem. '15.3.1.3.39' assert('Kernel#raise', '15.3.1.3.40') do assert_raise RuntimeError do raise end assert_raise RuntimeError do raise RuntimeError.new end end assert('Kernel#remove_instance_variable', '15.3.1.3.41') do class Test4RemoveInstanceVar attr_reader :var def initialize @var = 99 end def remove remove_instance_variable(:@var) end end tri = Test4RemoveInstanceVar.new assert_equal 99, tri.var tri.remove assert_equal nil, tri.var assert_raise NameError do tri.remove end end # Kernel#require is defined in mruby-require. '15.3.1.3.42' assert('Kernel#respond_to?', '15.3.1.3.43') do class Test4RespondTo def valid_method; end def test_method; end undef test_method end assert_raise TypeError do Test4RespondTo.new.respond_to?(1) end assert_raise ArgumentError do Test4RespondTo.new.respond_to? end assert_raise ArgumentError do Test4RespondTo.new.respond_to? :a, true, :aa end assert_true respond_to?(:nil?) assert_true Test4RespondTo.new.respond_to?(:valid_method) assert_true Test4RespondTo.new.respond_to?('valid_method') assert_false Test4RespondTo.new.respond_to?(:test_method) end assert('Kernel#send', '15.3.1.3.44') do # test with block l = send(:lambda) do true end assert_true l.call assert_equal l.class, Proc # test with argument assert_true send(:respond_to?, :nil?) # test without argument and without block assert_equal send(:public_methods).class, Array end assert('Kernel#singleton_methods', '15.3.1.3.45') do assert_equal singleton_methods.class, Array end assert('Kernel#to_s', '15.3.1.3.46') do assert_equal to_s.class, String end assert('Kernel.local_variables', '15.3.1.2.7') do a, b = 0, 1 a += b vars = Kernel.local_variables.sort assert_equal [:a, :b, :vars], vars Proc.new { c = 2 vars = Kernel.local_variables.sort assert_equal [:a, :b, :c, :vars], vars }.call end assert('Kernel#!=') do str1 = "hello" str2 = str1 str3 = "world" assert_false (str1[1] != 'e') assert_true (str1 != str3) assert_false (str2 != str1) end # operator "!~" is defined in ISO Ruby 11.4.4. assert('Kernel#!~') do x = "x" def x.=~(other) other == "x" end assert_false x !~ "x" assert_true x !~ "z" y = "y" def y.=~(other) other == "y" end def y.!~(other) other == "not y" end assert_false y !~ "y" assert_false y !~ "z" assert_true y !~ "not y" end assert('Kernel#respond_to_missing?') do class Test4RespondToMissing def respond_to_missing?(method_name, include_private = false) method_name == :a_method end end assert_true Test4RespondToMissing.new.respond_to?(:a_method) assert_false Test4RespondToMissing.new.respond_to?(:no_method) end assert('Kernel#global_variables') do variables = global_variables 1.upto(9) do |i| assert_equal variables.include?(:"$#{i}"), true end end assert('Kernel#define_singleton_method') do o = Object.new ret = o.define_singleton_method(:test_method) do :singleton_method_ok end assert_equal :test_method, ret assert_equal :singleton_method_ok, o.test_method end assert('stack extend') do def recurse(count, stop) return count if count > stop recurse(count+1, stop) end assert_equal 6, recurse(0, 5) end mruby-1.2.0+20160315+git4f20d58a/test/t/literals.rb000066400000000000000000000136301267140355100207770ustar00rootroot00000000000000## # Literals ISO Test assert('Literals Numerical', '8.7.6.2') do # signed and unsigned integer assert_equal 1, 1 assert_equal(-1, -1) assert_equal(+1, +1) # signed and unsigned float assert_equal 1.0, 1.0 assert_equal(-1.0, -1.0) # binary assert_equal 128, 0b10000000 assert_equal 128, 0B10000000 # octal assert_equal 8, 0o10 assert_equal 8, 0O10 assert_equal 8, 0_10 # hex assert_equal 255, 0xff assert_equal 255, 0Xff # decimal assert_equal 999, 0d999 assert_equal 999, 0D999 # decimal seperator assert_equal 10000000, 10_000_000 assert_equal 10, 1_0 # integer with exponent assert_equal 10.0, 1e1, assert_equal(0.1, 1e-1) assert_equal 10.0, 1e+1 # float with exponent assert_equal 10.0, 1.0e1 assert_equal(0.1, 1.0e-1) assert_equal 10.0, 1.0e+1 end assert('Literals Strings Single Quoted', '8.7.6.3.2') do assert_equal 'abc', 'abc' assert_equal '\'', '\'' assert_equal '\\', '\\' end assert('Literals Strings Double Quoted', '8.7.6.3.3') do a = "abc" assert_equal "abc", "abc" assert_equal "\"", "\"" assert_equal "\\", "\\" assert_equal "abc", "#{a}" end assert('Literals Strings Quoted Non-Expanded', '8.7.6.3.4') do a = %q{abc} b = %q(abc) c = %q[abc] d = %q e = %q/abc/ f = %q/ab\/c/ g = %q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal '#{a}', g end assert('Literals Strings Quoted Expanded', '8.7.6.3.5') do a = %Q{abc} b = %Q(abc) c = %Q[abc] d = %Q e = %Q/abc/ f = %Q/ab\/c/ g = %Q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal 'abc', g end assert('Literals Strings Here documents', '8.7.6.3.6') do a = <\"mm3\\n\"}y\nmm1\n", "mm2\n"], m assert_equal ({:x=>"mm3\n"}), m2 assert_equal [1, "nn1\n", 3, 4], n assert_equal "a $ q\n $ c $ d", q1 assert_equal "l $ mqq\nn $ o", q2 assert_equal ["1", "www\n", "3", "4", "5"], w assert_equal [1, "foo 222 333\n 444\n5\n bar\n6\n", 9], x assert_equal "", z end assert('Literals Array', '8.7.6.4') do a = %W{abc#{1+2}def \}g} b = %W(abc #{2+3} def \(g) c = %W[#{3+4}] d = %W< #{4+5} > e = %W// f = %W[[ab cd][ef]] g = %W{ ab #{-1}1 2#{2} } h = %W(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc3def', '}g'], a assert_equal ['abc', '5', 'def', '(g'], b assert_equal ['7'],c assert_equal ['9'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '-11', '22'], g assert_equal ["a\nb", 'test abc', "c\nd", "xy", "x\\y", "x\\y"], h a = %w{abc#{1+2}def \}g} b = %w(abc #{2+3} def \(g) c = %w[#{3+4}] d = %w< #{4+5} > e = %w// f = %w[[ab cd][ef]] g = %w{ ab #{-1}1 2#{2} } h = %w(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc#{1+2}def', '}g'], a assert_equal ['abc', '#{2+3}', 'def', '(g'], b assert_equal ['#{3+4}'], c assert_equal ['#{4+5}'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '#{-1}1', '2#{2}'], g assert_equal ["a\\nb", "test abc", "c\nd", "x\\y", "x\\y", "x\\\\y"], h end assert('Literals Array of symbols') do a = %I{abc#{1+2}def \}g} b = %I(abc #{2+3} def \(g) c = %I[#{3+4}] d = %I< #{4+5} > e = %I// f = %I[[ab cd][ef]] g = %I{ ab #{-1}1 2#{2} } assert_equal [:'abc3def', :'}g'], a assert_equal [:'abc', :'5', :'def', :'(g'], b assert_equal [:'7'],c assert_equal [:'9'], d assert_equal [], e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'-11', :'22'], g a = %i{abc#{1+2}def \}g} b = %i(abc #{2+3} def \(g) c = %i[#{3+4}] d = %i< #{4+5} > e = %i// f = %i[[ab cd][ef]] g = %i{ ab #{-1}1 2#{2} } assert_equal [:'abc#{1+2}def', :'}g'], a assert_equal [:'abc', :'#{2+3}', :'def', :'(g'], b assert_equal [:'#{3+4}'], c assert_equal [:'#{4+5}'], d assert_equal [] ,e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'#{-1}1', :'2#{2}'], g end assert('Literals Symbol', '8.7.6.6') do # do not compile error :$asd :@asd :@@asd :asd= :asd! :asd? :+ :+@ :if :BEGIN a = :"asd qwe" b = :'foo bar' c = :"a#{1+2}b" d = %s(asd) e = %s( foo \)) f = %s[asd \[ qwe] g = %s/foo#{1+2}bar/ h = %s{{foo bar}} assert_equal :'asd qwe', a assert_equal :"foo bar", b assert_equal :a3b, c assert_equal :asd, d assert_equal :' foo )', e assert_equal :"asd [\nqwe", f assert_equal :'foo#{1+2}bar', g assert_equal :'{foo bar}', h end # Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do mruby-1.2.0+20160315+git4f20d58a/test/t/localjumperror.rb000066400000000000000000000004751267140355100222230ustar00rootroot00000000000000## # LocalJumpError ISO Test assert('LocalJumpError', '15.2.25') do assert_equal Class, LocalJumpError.class # assert_raise LocalJumpError do # # this will cause an exception due to the wrong location # retry # end end # TODO 15.2.25.2.1 LocalJumpError#exit_value # TODO 15.2.25.2.2 LocalJumpError#reason mruby-1.2.0+20160315+git4f20d58a/test/t/methods.rb000066400000000000000000000054241267140355100206250ustar00rootroot00000000000000## # Chapter 13.3 "Methods" ISO Test assert('The alias statement', '13.3.6 a) 4)') do # check aliasing in all possible ways def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement (overwrite original)', '13.3.6 a) 4)') do # check that an aliased method can be overwritten # without side effect def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) def alias_test_method_original; false; end assert_false(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement', '13.3.6 a) 5)') do # check that alias is raising NameError if # non-existing method should be undefined assert_raise(NameError) do alias new_name_a non_existing_method end assert_raise(NameError) do alias :new_name_b :non_existing_method end end assert('The undef statement', '13.3.7 a) 4)') do # check that undef is undefining method # based on the method name def existing_method_a; true; end def existing_method_b; true; end def existing_method_c; true; end def existing_method_d; true; end def existing_method_e; true; end def existing_method_f; true; end # check that methods are defined assert_true(existing_method_a, 'Method should be defined') assert_true(existing_method_b, 'Method should be defined') assert_true(existing_method_c, 'Method should be defined') assert_true(existing_method_d, 'Method should be defined') assert_true(existing_method_e, 'Method should be defined') assert_true(existing_method_f, 'Method should be defined') # undefine in all possible ways and check that method # is undefined undef existing_method_a assert_raise(NoMethodError) do existing_method_a end undef :existing_method_b assert_raise(NoMethodError) do existing_method_b end undef existing_method_c, existing_method_d assert_raise(NoMethodError) do existing_method_c end assert_raise(NoMethodError) do existing_method_d end undef :existing_method_e, :existing_method_f assert_raise(NoMethodError) do existing_method_e end assert_raise(NoMethodError) do existing_method_f end end assert('The undef statement (method undefined)', '13.3.7 a) 5)') do # check that undef is raising NameError if # non-existing method should be undefined assert_raise(NameError) do undef non_existing_method end assert_raise(NameError) do undef :non_existing_method end end mruby-1.2.0+20160315+git4f20d58a/test/t/module.rb000066400000000000000000000510351267140355100204460ustar00rootroot00000000000000## # Module ISO Test def labeled_module(name, &block) Module.new do singleton_class.class_eval do define_method(:to_s) { name } alias_method :inspect, :to_s end class_eval(&block) if block end end def labeled_class(name, supklass = Object, &block) Class.new(supklass) do singleton_class.class_eval do define_method(:to_s) { name } alias_method :inspect, :to_s end class_eval(&block) if block end end assert('Module', '15.2.2') do assert_equal Class, Module.class end # TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do # TODO not implemented ATM assert('Module.nesting', '15.2.2.3.2') do assert('Module#ancestors', '15.2.2.4.9') do class Test4ModuleAncestors end sc = Test4ModuleAncestors.singleton_class r = String.ancestors assert_equal Array, r.class assert_true r.include?(String) assert_true r.include?(Object) end assert('Module#append_features', '15.2.2.4.10') do module Test4AppendFeatures def self.append_features(mod) Test4AppendFeatures2.const_set(:Const4AppendFeatures2, mod) end end module Test4AppendFeatures2 include Test4AppendFeatures end assert_equal Test4AppendFeatures2, Test4AppendFeatures2.const_get(:Const4AppendFeatures2) end assert('Module#attr NameError') do %w[ foo? @foo @@foo $foo ].each do |name| module NameTest; end assert_raise(NameError) do NameTest.module_eval { attr_reader name.to_sym } end assert_raise(NameError) do NameTest.module_eval { attr_writer name.to_sym } end assert_raise(NameError) do NameTest.module_eval { attr name.to_sym } end assert_raise(NameError) do NameTest.module_eval { attr_accessor name.to_sym } end end end assert('Module#attr', '15.2.2.4.11') do class AttrTest class << self attr :cattr def cattr_val=(val) @cattr = val end end attr :iattr def iattr_val=(val) @iattr = val end end test = AttrTest.new assert_true AttrTest.respond_to?(:cattr) assert_true test.respond_to?(:iattr) assert_false AttrTest.respond_to?(:cattr=) assert_false test.respond_to?(:iattr=) test.iattr_val = 'test' assert_equal 'test', test.iattr AttrTest.cattr_val = 'test' assert_equal 'test', AttrTest.cattr end assert('Module#attr_accessor', '15.2.2.4.12') do class AttrTestAccessor class << self attr_accessor :cattr end attr_accessor :iattr, 'iattr2' end attr_instance = AttrTestAccessor.new assert_true AttrTestAccessor.respond_to?(:cattr=) assert_true attr_instance.respond_to?(:iattr=) assert_true attr_instance.respond_to?(:iattr2=) assert_true AttrTestAccessor.respond_to?(:cattr) assert_true attr_instance.respond_to?(:iattr) assert_true attr_instance.respond_to?(:iattr2) attr_instance.iattr = 'test' assert_equal 'test', attr_instance.iattr AttrTestAccessor.cattr = 'test' assert_equal 'test', AttrTestAccessor.cattr end assert('Module#attr_reader', '15.2.2.4.13') do class AttrTestReader class << self attr_reader :cattr def cattr_val=(val) @cattr = val end end attr_reader :iattr, 'iattr2' def iattr_val=(val) @iattr = val end end attr_instance = AttrTestReader.new assert_true AttrTestReader.respond_to?(:cattr) assert_true attr_instance.respond_to?(:iattr) assert_true attr_instance.respond_to?(:iattr2) assert_false AttrTestReader.respond_to?(:cattr=) assert_false attr_instance.respond_to?(:iattr=) assert_false attr_instance.respond_to?(:iattr2=) attr_instance.iattr_val = 'test' assert_equal 'test', attr_instance.iattr AttrTestReader.cattr_val = 'test' assert_equal 'test', AttrTestReader.cattr end assert('Module#attr_writer', '15.2.2.4.14') do class AttrTestWriter class << self attr_writer :cattr def cattr_val @cattr end end attr_writer :iattr, 'iattr2' def iattr_val @iattr end end attr_instance = AttrTestWriter.new assert_true AttrTestWriter.respond_to?(:cattr=) assert_true attr_instance.respond_to?(:iattr=) assert_true attr_instance.respond_to?(:iattr2=) assert_false AttrTestWriter.respond_to?(:cattr) assert_false attr_instance.respond_to?(:iattr) assert_false attr_instance.respond_to?(:iattr2) attr_instance.iattr = 'test' assert_equal 'test', attr_instance.iattr_val AttrTestWriter.cattr = 'test' assert_equal 'test', AttrTestWriter.cattr_val end assert('Module#class_eval', '15.2.2.4.15') do class Test4ClassEval @a = 11 @b = 12 end Test4ClassEval.class_eval do def method1 end end r = Test4ClassEval.instance_methods assert_equal 11, Test4ClassEval.class_eval{ @a } assert_equal 12, Test4ClassEval.class_eval{ @b } assert_equal Array, r.class assert_true r.include?(:method1) end assert('Module#class_variable_defined?', '15.2.2.4.16') do class Test4ClassVariableDefined @@cv = 99 end assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv) assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting) end assert('Module#class_variable_get', '15.2.2.4.17') do class Test4ClassVariableGet @@cv = 99 end assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv) end assert('Module#class_variable_set', '15.2.2.4.18') do class Test4ClassVariableSet @@foo = 100 def foo @@foo end end assert_true Test4ClassVariableSet.class_variable_set(:@@cv, 99) assert_true Test4ClassVariableSet.class_variable_set(:@@foo, 101) assert_true Test4ClassVariableSet.class_variables.include? :@@cv assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv) assert_equal 101, Test4ClassVariableSet.new.foo end assert('Module#class_variables', '15.2.2.4.19') do class Test4ClassVariables1 @@var1 = 1 end class Test4ClassVariables2 < Test4ClassVariables1 @@var2 = 2 end assert_equal [:@@var1], Test4ClassVariables1.class_variables assert_equal [:@@var2, :@@var1], Test4ClassVariables2.class_variables end assert('Module#const_defined?', '15.2.2.4.20') do module Test4ConstDefined Const4Test4ConstDefined = true end assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined) assert_false Test4ConstDefined.const_defined?(:NotExisting) end assert('Module#const_get', '15.2.2.4.21') do module Test4ConstGet Const4Test4ConstGet = 42 end assert_equal 42, Test4ConstGet.const_get(:Const4Test4ConstGet) end assert('Module#const_missing', '15.2.2.4.22') do module Test4ConstMissing def self.const_missing(sym) 42 # the answer to everything end end assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist) end assert('Module#const_get', '15.2.2.4.23') do module Test4ConstSet Const4Test4ConstSet = 42 end assert_true Test4ConstSet.const_set(:Const4Test4ConstSet, 23) assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet) end assert('Module#constants', '15.2.2.4.24') do $n = [] module TestA C = 1 end class TestB include TestA C2 = 1 $n = constants.sort end assert_equal [ :C ], TestA.constants assert_equal [ :C, :C2 ], $n end assert('Module#include', '15.2.2.4.27') do module Test4Include Const4Include = 42 end module Test4Include2 include Test4Include end assert_equal 42, Test4Include2.const_get(:Const4Include) end assert('Module#include?', '15.2.2.4.28') do module Test4IncludeP end class Test4IncludeP2 include Test4IncludeP end class Test4IncludeP3 < Test4IncludeP2 end assert_true Test4IncludeP2.include?(Test4IncludeP) assert_true Test4IncludeP3.include?(Test4IncludeP) assert_false Test4IncludeP.include?(Test4IncludeP) end assert('Module#included', '15.2.2.4.29') do module Test4Included Const4Included = 42 def self.included mod Test4Included.const_set(:Const4Included2, mod) end end module Test4Included2 include Test4Included end assert_equal 42, Test4Included2.const_get(:Const4Included) assert_equal Test4Included2, Test4Included2.const_get(:Const4Included2) end assert('Module#included_modules', '15.2.2.4.30') do module Test4includedModules end module Test4includedModules2 include Test4includedModules end r = Test4includedModules2.included_modules assert_equal Array, r.class assert_true r.include?(Test4includedModules) end assert('Module#initialize', '15.2.2.4.31') do assert_kind_of Module, Module.new mod = Module.new { def hello; "hello"; end } assert_equal [:hello], mod.instance_methods a = nil mod = Module.new { |m| a = m } assert_equal mod, a end assert('Module#instance_methods', '15.2.2.4.33') do module Test4InstanceMethodsA def method1() end end class Test4InstanceMethodsB def method2() end end class Test4InstanceMethodsC < Test4InstanceMethodsB def method3() end end r = Test4InstanceMethodsC.instance_methods(true) assert_equal [:method1], Test4InstanceMethodsA.instance_methods assert_equal [:method2], Test4InstanceMethodsB.instance_methods(false) assert_equal [:method3], Test4InstanceMethodsC.instance_methods(false) assert_equal Array, r.class assert_true r.include?(:method3) assert_true r.include?(:method2) end assert('Module#method_defined?', '15.2.2.4.34') do module Test4MethodDefined module A def method1() end end class B def method2() end end class C < B include A def method3() end end end assert_true Test4MethodDefined::A.method_defined? :method1 assert_true Test4MethodDefined::C.method_defined? :method1 assert_true Test4MethodDefined::C.method_defined? "method2" assert_true Test4MethodDefined::C.method_defined? "method3" assert_false Test4MethodDefined::C.method_defined? "method4" end assert('Module#module_eval', '15.2.2.4.35') do module Test4ModuleEval @a = 11 @b = 12 end assert_equal 11, Test4ModuleEval.module_eval{ @a } assert_equal 12, Test4ModuleEval.module_eval{ @b } end assert('Module#remove_class_variable', '15.2.2.4.39') do class Test4RemoveClassVariable @@cv = 99 end assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv) assert_false Test4RemoveClassVariable.class_variables.include? :@@cv end assert('Module#remove_const', '15.2.2.4.40') do module Test4RemoveConst ExistingConst = 23 end result = Test4RemoveConst.module_eval { remove_const :ExistingConst } name_error = false begin Test4RemoveConst.module_eval { remove_const :NonExistingConst } rescue NameError name_error = true end # Constant removed from Module assert_false Test4RemoveConst.const_defined? :ExistingConst # Return value of binding assert_equal 23, result # Name Error raised when Constant doesn't exist assert_true name_error end assert('Module#remove_method', '15.2.2.4.41') do module Test4RemoveMethod class Parent def hello end end class Child < Parent def hello end end end assert_true Test4RemoveMethod::Child.class_eval{ remove_method :hello } assert_true Test4RemoveMethod::Child.instance_methods.include? :hello assert_false Test4RemoveMethod::Child.instance_methods(false).include? :hello end assert('Module#undef_method', '15.2.2.4.42') do module Test4UndefMethod class Parent def hello end end class Child < Parent def hello end end class GrandChild < Child end end Test4UndefMethod::Child.class_eval{ undef_method :hello } assert_true Test4UndefMethod::Parent.new.respond_to?(:hello) assert_false Test4UndefMethod::Child.new.respond_to?(:hello) assert_false Test4UndefMethod::GrandChild.new.respond_to?(:hello) assert_false Test4UndefMethod::Child.instance_methods(false).include? :hello end # Not ISO specified # @!group prepend assert('Module#prepend') do module M0 def m1; [:M0] end end module M1 def m1; [:M1, super, :M1] end end module M2 def m1; [:M2, super, :M2] end end M3 = Module.new do def m1; [:M3, super, :M3] end end module M4 def m1; [:M4, super, :M4] end end class P0 include M0 prepend M1 def m1; [:C0, super, :C0] end end class P1 < P0 prepend M2, M3 include M4 def m1; [:C1, super, :C1] end end obj = P1.new expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] assert_equal(expected, obj.m1) end # mruby shouldn't be affected by this since there is # no visibility control (yet) assert('Module#prepend public') do assert_nothing_raised('ruby/ruby #8846') do Class.new.prepend(Module.new) end end assert('Module#prepend inheritance') do bug6654 = '[ruby-core:45914]' a = labeled_module('a') b = labeled_module('b') { include a } c = labeled_module('c') { prepend b } #assert bug6654 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) #end bug8357 = '[ruby-core:54736] [Bug #8357]' b = labeled_module('b') { prepend a } c = labeled_class('c') { include b } #assert bug8357 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) #end bug8357 = '[ruby-core:54742] [Bug #8357]' assert_kind_of(b, c.new, bug8357) end assert('Moduler#prepend + #instance_methods') do bug6655 = '[ruby-core:45915]' assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655) end assert 'Module#prepend + #singleton_methods' do o = Object.new o.singleton_class.class_eval {prepend Module.new} assert_equal([], o.singleton_methods) end assert 'Module#prepend + #remove_method' do c = Class.new do prepend Module.new { def foo; end } end assert_raise(NameError) do c.class_eval do remove_method(:foo) end end c.class_eval do def foo; end end removed = nil c.singleton_class.class_eval do define_method(:method_removed) {|id| removed = id} end assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do c.class_eval do remove_method(:foo) end end assert_equal(:foo, removed) end assert 'Module#prepend + Class#ancestors' do bug6658 = '[ruby-core:45919]' m = labeled_module("m") c = labeled_class("c") {prepend m} assert_equal([m, c], c.ancestors[0, 2], bug6658) bug6662 = '[ruby-dev:45868]' c2 = labeled_class("c2", c) anc = c2.ancestors assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662) end assert 'Module#prepend + Module#ancestors' do bug6659 = '[ruby-dev:45861]' m0 = labeled_module("m0") { def x; [:m0, *super] end } m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } c0 = labeled_class("c0") { def x; [:c0] end } c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } # assert_equal([m0, m1], m1.ancestors, bug6659) # bug6662 = '[ruby-dev:45868]' assert_equal([m0, m1, m2], m2.ancestors, bug6662) assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) assert_equal([:m0, :m1, :m2, :c1], c1.new.x) assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) # m3 = labeled_module("m3") { include m1; prepend m1 } assert_equal([m3, m0, m1], m3.ancestors) m3 = labeled_module("m3") { prepend m1; include m1 } assert_equal([m0, m1, m3], m3.ancestors) m3 = labeled_module("m3") { prepend m1; prepend m1 } assert_equal([m0, m1, m3], m3.ancestors) m3 = labeled_module("m3") { include m1; include m1 } assert_equal([m3, m0, m1], m3.ancestors) end assert 'Module#prepend #instance_methods(false)' do bug6660 = '[ruby-dev:45863]' assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660) assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660) end assert 'cyclic Module#prepend' do bug7841 = '[ruby-core:52205] [Bug #7841]' m1 = Module.new m2 = Module.new m1.instance_eval { prepend(m2) } assert_raise(ArgumentError, bug7841) do m2.instance_eval { prepend(m1) } end end # these assertions will not run without a #assert_seperately method #assert 'test_prepend_optmethod' do # bug7983 = '[ruby-dev:47124] [Bug #7983]' # assert_separately [], %{ # module M # def /(other) # to_f / other # end # end # Fixnum.send(:prepend, M) # assert_equal(0.5, 1 / 2, "#{bug7983}") # } # assert_equal(0, 1 / 2) #end # mruby has no visibility control assert 'Module#prepend visibility' do bug8005 = '[ruby-core:53106] [Bug #8005]' c = Class.new do prepend Module.new {} def foo() end protected :foo end a = c.new assert_true a.respond_to?(:foo), bug8005 assert_nothing_raised(NoMethodError, bug8005) {a.send :foo} end # mruby has no visibility control assert 'Module#prepend inherited visibility' do bug8238 = '[ruby-core:54105] [Bug #8238]' module Test4PrependVisibilityInherited class A def foo() A; end private :foo end class B < A public :foo prepend Module.new end end assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}") end assert 'Module#prepend + #included_modules' do bug8025 = '[ruby-core:53158] [Bug #8025]' mixin = labeled_module("mixin") c = labeled_module("c") {prepend mixin} im = c.included_modules assert_not_include(im, c, bug8025) assert_include(im, mixin, bug8025) c1 = labeled_class("c1") {prepend mixin} c2 = labeled_class("c2", c1) im = c2.included_modules assert_not_include(im, c1, bug8025) assert_not_include(im, c2, bug8025) assert_include(im, mixin, bug8025) end assert 'Module#prepend super in alias' do skip "super does not currently work in aliased methods" bug7842 = '[Bug #7842]' p = labeled_module("P") do def m; "P"+super; end end a = labeled_class("A") do def m; "A"; end end b = labeled_class("B", a) do def m; "B"+super; end alias m2 m prepend p alias m3 m end assert_nothing_raised do assert_equal("BA", b.new.m2, bug7842) end assert_nothing_raised do assert_equal("PBA", b.new.m3, bug7842) end end assert 'Module#prepend each class' do m = labeled_module("M") c1 = labeled_class("C1") {prepend m} c2 = labeled_class("C2", c1) {prepend m} assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class") end assert 'Module#prepend no duplication' do m = labeled_module("M") c = labeled_class("C") {prepend m; prepend m} assert_equal([m, c], c.ancestors[0, 2], "should never duplicate") end assert 'Module#prepend in superclass' do m = labeled_module("M") c1 = labeled_class("C1") c2 = labeled_class("C2", c1) {prepend m} c1.class_eval {prepend m} assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass") end # requires #assert_seperately #assert 'Module#prepend call super' do # assert_separately([], <<-'end;') #do # bug10847 = '[ruby-core:68093] [Bug #10847]' # module M; end # Float.prepend M # assert_nothing_raised(SystemStackError, bug10847) do # 0.3.numerator # end # end; #end # @!endgroup prepend assert('Module#to_s') do module Test4to_sModules end assert_equal 'Test4to_sModules', Test4to_sModules.to_s end assert('Module#inspect') do module Test4to_sModules end assert_equal 'Test4to_sModules', Test4to_sModules.inspect end assert('Issue 1467') do module M1 def initialize() super() end end class C1 include M1 def initialize() super() end end class C2 include M1 end C1.new C2.new end assert('clone Module') do module M1 def foo true end end class B include M1.clone end B.new.foo end assert('Module#module_function') do module M def modfunc; end module_function :modfunc end assert_true M.respond_to?(:modfunc) end assert('module with non-class/module outer raises TypeError') do assert_raise(TypeError) { module 0::M1 end } assert_raise(TypeError) { module []::M2 end } end mruby-1.2.0+20160315+git4f20d58a/test/t/nameerror.rb000066400000000000000000000011211267140355100211420ustar00rootroot00000000000000## # NameError ISO Test assert('NameError', '15.2.31') do assert_equal Class, NameError.class end assert('NameError#name', '15.2.31.2.1') do # This check is not duplicate with 15.2.31.2.2 check. # Because the NameError in this test is generated in # C API. class TestDummy alias foo bar rescue NameError => e $test_dummy_result = e.name end assert_equal :bar, $test_dummy_result end assert('NameError#initialize', '15.2.31.2.2') do e = NameError.new('a', :foo) assert_equal NameError, e.class assert_equal 'a', e.message assert_equal :foo, e.name end mruby-1.2.0+20160315+git4f20d58a/test/t/nil.rb000066400000000000000000000012111267140355100177320ustar00rootroot00000000000000## # NilClass ISO Test assert('NilClass', '15.2.4') do assert_equal Class, NilClass.class end assert('NilClass', '15.2.4.1') do assert_equal NilClass, nil.class assert_false NilClass.method_defined? :new end assert('NilClass#&', '15.2.4.3.1') do assert_false nil.&(true) assert_false nil.&(nil) end assert('NilClass#^', '15.2.4.3.2') do assert_true nil.^(true) assert_false nil.^(false) end assert('NilClass#|', '15.2.4.3.3') do assert_true nil.|(true) assert_false nil.|(false) end assert('NilClass#nil?', '15.2.4.3.4') do assert_true nil.nil? end assert('NilClass#to_s', '15.2.4.3.5') do assert_equal '', nil.to_s end mruby-1.2.0+20160315+git4f20d58a/test/t/nomethoderror.rb000066400000000000000000000007431267140355100220500ustar00rootroot00000000000000## # NoMethodError ISO Test assert('NoMethodError', '15.2.32') do NoMethodError.class == Class assert_raise NoMethodError do doesNotExistAsAMethodNameForVerySure("") end end assert('NoMethodError#args', '15.2.32.2.1') do a = NoMethodError.new 'test', :test, [1, 2] assert_equal [1, 2], a.args assert_nothing_raised do begin doesNotExistAsAMethodNameForVerySure 3, 1, 4 rescue NoMethodError => e assert_equal [3, 1, 4], e.args end end end mruby-1.2.0+20160315+git4f20d58a/test/t/numeric.rb000066400000000000000000000014511267140355100206200ustar00rootroot00000000000000## # Numeric ISO Test assert('Numeric', '15.2.7') do assert_equal Class, Numeric.class end assert('Numeric#+@', '15.2.7.4.1') do assert_equal(+1, +1) end assert('Numeric#-@', '15.2.7.4.2') do assert_equal(-1, -1) end assert('Numeric#abs', '15.2.7.4.3') do assert_equal(1, 1.abs) assert_equal(1.0, -1.abs) end assert('Numeric#pow') do assert_equal(8, 2 ** 3) assert_equal(-8, -2 ** 3) assert_equal(1, 2 ** 0) assert_equal(1, 2.2 ** 0) assert_equal(0.5, 2 ** -1) end assert('Numeric#/', '15.2.8.3.4') do n = Class.new(Numeric){ def /(x); 15.1;end }.new assert_equal(2, 10/5) assert_equal(0.0625, 1/16) assert_equal(15.1, n/10) assert_raise(TypeError){ 1/n } assert_raise(TypeError){ 1/nil } end # Not ISO specified assert('Numeric#**') do assert_equal 8.0, 2.0**3 end mruby-1.2.0+20160315+git4f20d58a/test/t/object.rb000066400000000000000000000002721267140355100204240ustar00rootroot00000000000000## # Object ISO Test assert('Object', '15.2.1') do assert_equal Class, Object.class end assert('Object superclass', '15.2.1.2') do assert_equal BasicObject, Object.superclass end mruby-1.2.0+20160315+git4f20d58a/test/t/proc.rb000066400000000000000000000072561267140355100201320ustar00rootroot00000000000000## # Proc ISO Test assert('Proc', '15.2.17') do assert_equal Class, Proc.class end assert('Proc.new', '15.2.17.3.1') do assert_raise ArgumentError do Proc.new end assert_equal (Proc.new {}).class, Proc end assert('Proc#[]', '15.2.17.4.1') do a = 0 b = Proc.new { a += 1 } b.[] a2 = 0 b2 = Proc.new { |i| a2 += i } b2.[](5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#arity', '15.2.17.4.2') do a = Proc.new {|x, y|}.arity b = Proc.new {|x, *y, z|}.arity c = Proc.new {|x=0, y|}.arity d = Proc.new {|(x, y), z=0|}.arity assert_equal 2, a assert_equal(-3, b) assert_equal 1, c assert_equal 1, d e = ->(x=0, y){}.arity f = ->((x, y), z=0){}.arity g = ->(x=0){}.arity assert_equal(-2, e) assert_equal(-2, f) assert_equal(-1, g) end assert('Proc#call', '15.2.17.4.3') do a = 0 b = Proc.new { a += 1 } b.call a2 = 0 b2 = Proc.new { |i| a2 += i } b2.call(5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#call proc args pos block') do pr = Proc.new {|a,b,&c| [a, b, c.class, c&&c.call(:x)] } assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc }) assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc }) assert_equal [nil, nil, Proc, :x], (pr.call(){|x| x}) assert_equal [1, nil, Proc, :x], (pr.call(1){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x}) end assert('Proc#call proc args pos rest post') do pr = Proc.new {|a,b,*c,d,e| [a,b,c,d,e] } assert_equal [nil, nil, [], nil, nil], pr.call() assert_equal [1, nil, [], nil, nil], pr.call(1) assert_equal [1, 2, [], nil, nil], pr.call(1,2) assert_equal [1, 2, [], 3, nil], pr.call(1,2,3) assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4) assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5) assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7) assert_equal [nil, nil, [], nil, nil], pr.call([]) assert_equal [1, nil, [], nil, nil], pr.call([1]) assert_equal [1, 2, [], nil, nil], pr.call([1,2]) assert_equal [1, 2, [], 3, nil], pr.call([1,2,3]) assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4]) assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5]) assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6]) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7]) end assert('Proc#return_does_not_break_self') do class TestClass attr_accessor :block def initialize end def return_array @block = Proc.new { self } return [] end def return_instance_variable @block = Proc.new { self } return @block end def return_const_fixnum @block = Proc.new { self } return 123 end def return_nil @block = Proc.new { self } return nil end end c = TestClass.new assert_equal [], c.return_array assert_equal c, c.block.call c.return_instance_variable assert_equal c, c.block.call assert_equal 123, c.return_const_fixnum assert_equal c, c.block.call assert_equal nil, c.return_nil assert_equal c, c.block.call end assert('&obj call to_proc if defined') do pr = Proc.new{} def mock(&b) b end assert_equal pr.object_id, mock(&pr).object_id assert_equal pr, mock(&pr) obj = Object.new def obj.to_proc Proc.new{ :from_to_proc } end assert_equal :from_to_proc, mock(&obj).call assert_raise(TypeError){ mock(&(Object.new)) } end mruby-1.2.0+20160315+git4f20d58a/test/t/range.rb000066400000000000000000000036701267140355100202570ustar00rootroot00000000000000## # Range ISO Test assert('Range', '15.2.14') do assert_equal Class, Range.class end assert('Range#==', '15.2.14.4.1') do assert_true (1..10) == (1..10) assert_false (1..10) == (1..100) assert_true (1..10) == Range.new(1.0, 10.0) end assert('Range#===', '15.2.14.4.2') do a = (1..10) assert_true a === 5 assert_false a === 20 end assert('Range#begin', '15.2.14.4.3') do assert_equal 1, (1..10).begin end assert('Range#each', '15.2.14.4.4') do a = (1..3) b = 0 a.each {|i| b += i} assert_equal 6, b end assert('Range#end', '15.2.14.4.5') do assert_equal 10, (1..10).end end assert('Range#exclude_end?', '15.2.14.4.6') do assert_true (1...10).exclude_end? assert_false (1..10).exclude_end? end assert('Range#first', '15.2.14.4.7') do assert_equal 1, (1..10).first end assert('Range#include?', '15.2.14.4.8') do a = (1..10) assert_true a.include?(5) assert_false a.include?(20) end assert('Range#initialize', '15.2.14.4.9') do a = Range.new(1, 10, true) b = Range.new(1, 10, false) assert_equal (1...10), a assert_true a.exclude_end? assert_equal (1..10), b assert_false b.exclude_end? end assert('Range#last', '15.2.14.4.10') do assert_equal 10, (1..10).last end assert('Range#member?', '15.2.14.4.11') do a = (1..10) assert_true a.member?(5) assert_false a.member?(20) end assert('Range#to_s', '15.2.14.4.12') do assert_equal "0..1", (0..1).to_s assert_equal "0...1", (0...1).to_s assert_equal "a..b", ("a".."b").to_s assert_equal "a...b", ("a"..."b").to_s end assert('Range#inspect', '15.2.14.4.13') do assert_equal "0..1", (0..1).inspect assert_equal "0...1", (0...1).inspect assert_equal "\"a\"..\"b\"", ("a".."b").inspect assert_equal "\"a\"...\"b\"", ("a"..."b").inspect end assert('Range#eql?', '15.2.14.4.14') do assert_true (1..10).eql? (1..10) assert_false (1..10).eql? (1..100) assert_false (1..10).eql? (Range.new(1.0, 10.0)) assert_false (1..10).eql? "1..10" end mruby-1.2.0+20160315+git4f20d58a/test/t/rangeerror.rb000066400000000000000000000001501267140355100213170ustar00rootroot00000000000000## # RangeError ISO Test assert('RangeError', '15.2.26') do assert_equal Class, RangeError.class end mruby-1.2.0+20160315+git4f20d58a/test/t/regexperror.rb000066400000000000000000000001211267140355100215130ustar00rootroot00000000000000## # RegexpError ISO Test # TODO broken ATM assert('RegexpError', '15.2.27') do mruby-1.2.0+20160315+git4f20d58a/test/t/runtimeerror.rb000066400000000000000000000001561267140355100217140ustar00rootroot00000000000000## # RuntimeError ISO Test assert('RuntimeError', '15.2.28') do assert_equal Class, RuntimeError.class end mruby-1.2.0+20160315+git4f20d58a/test/t/standarderror.rb000066400000000000000000000001611267140355100220250ustar00rootroot00000000000000## # StandardError ISO Test assert('StandardError', '15.2.23') do assert_equal Class, StandardError.class end mruby-1.2.0+20160315+git4f20d58a/test/t/string.rb000066400000000000000000000340321267140355100204650ustar00rootroot00000000000000# coding: utf-8 ## # String ISO Test UTF8STRING = ("\343\201\202".size == 1) assert('String', '15.2.10') do assert_equal Class, String.class end assert('String#<=>', '15.2.10.5.1') do a = '' <=> '' b = '' <=> 'not empty' c = 'not empty' <=> '' d = 'abc' <=> 'cba' e = 'cba' <=> 'abc' assert_equal 0, a assert_equal(-1, b) assert_equal 1, c assert_equal(-1, d) assert_equal 1, e end assert('String#==', '15.2.10.5.2') do assert_equal 'abc', 'abc' assert_not_equal 'abc', 'cba' end # 'String#=~', '15.2.10.5.3' will be tested in mrbgems. assert('String#+', '15.2.10.5.4') do assert_equal 'ab', 'a' + 'b' end assert('String#*', '15.2.10.5.5') do assert_equal 'aaaaa', 'a' * 5 assert_equal '', 'a' * 0 assert_raise(ArgumentError) do 'a' * -1 end end assert('String#[]', '15.2.10.5.6') do # length of args is 1 a = 'abc'[0] b = 'abc'[-1] c = 'abc'[10] d = 'abc'[-10] e = 'abc'[1.1] # length of args is 2 a1 = 'abc'[0, -1] b1 = 'abc'[10, 0] c1 = 'abc'[-10, 0] d1 = 'abc'[0, 0] e1 = 'abc'[1, 2] # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc'['bc'] b3 = 'abc'['XX'] assert_equal 'a', 'a' # assert_equal 'c', b # assert_nil c # assert_nil d # assert_equal 'b', e # assert_nil a1 # assert_nil b1 # assert_nil c1 # assert_equal '', d1 # assert_equal 'bc', e1 # assert_equal 'bc', a3 # assert_nil b3 # assert_raise(TypeError) do # a[nil] # end end assert('String#[](UTF-8)', '15.2.10.5.6') do assert_equal "ち", "こんにちは世界"[3] assert_equal nil, "こんにちは世界"[20] assert_equal "世", "こんにちは世界"[-2] assert_equal "世界", "こんにちは世界"[-2..-1] assert_equal "んに", "こんにちは世界"[1,2] assert_equal "世", "こんにちは世界"["世"] end if UTF8STRING assert('String#[] with Range') do a1 = 'abc'[1..0] b1 = 'abc'[1..1] c1 = 'abc'[1..2] d1 = 'abc'[1..3] e1 = 'abc'[1..4] f1 = 'abc'[0..-2] g1 = 'abc'[-2..3] h1 = 'abc'[3..4] i1 = 'abc'[4..5] j1 = 'abcdefghijklmnopqrstuvwxyz'[1..3] a2 = 'abc'[1...0] b2 = 'abc'[1...1] c2 = 'abc'[1...2] d2 = 'abc'[1...3] e2 = 'abc'[1...4] f2 = 'abc'[0...-2] g2 = 'abc'[-2...3] h2 = 'abc'[3...4] i2 = 'abc'[4...5] j2 = 'abcdefghijklmnopqrstuvwxyz'[1...3] assert_equal '', a1 assert_equal 'b', b1 assert_equal 'bc', c1 assert_equal 'bc', d1 assert_equal 'bc', e1 assert_equal 'ab', f1 assert_equal 'bc', g1 assert_equal '', h1 assert_nil i2 assert_equal 'bcd', j1 assert_equal '', a2 assert_equal '', b2 assert_equal 'b', c2 assert_equal 'bc', d2 assert_equal 'bc', e2 assert_equal 'a', f2 assert_equal 'bc', g2 assert_equal '', h2 assert_nil i2 assert_equal 'bc', j2 end assert('String#[]=') do # length of args is 1 a = 'abc' a[0] = 'X' assert_equal 'Xbc', a b = 'abc' b[-1] = 'X' assert_equal 'abX', b c = 'abc' assert_raise(IndexError) do c[10] = 'X' end d = 'abc' assert_raise(IndexError) do d[-10] = 'X' end e = 'abc' e[1.1] = 'X' assert_equal 'aXc', e # length of args is 2 a1 = 'abc' assert_raise(IndexError) do a1[0, -1] = 'X' end b1 = 'abc' assert_raise(IndexError) do b1[10, 0] = 'X' end c1 = 'abc' assert_raise(IndexError) do c1[-10, 0] = 'X' end d1 = 'abc' d1[0, 0] = 'X' assert_equal 'Xabc', d1 e1 = 'abc' e1[1, 3] = 'X' assert_equal 'aX', e1 # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc' a3['bc'] = 'X' assert_equal a3, 'aX' b3 = 'abc' assert_raise(IndexError) do b3['XX'] = 'Y' end end assert('String#capitalize', '15.2.10.5.7') do a = 'abc' a.capitalize assert_equal 'abc', a assert_equal 'Abc', 'abc'.capitalize end assert('String#capitalize!', '15.2.10.5.8') do a = 'abc' a.capitalize! assert_equal 'Abc', a assert_equal nil, 'Abc'.capitalize! end assert('String#chomp', '15.2.10.5.9') do a = 'abc'.chomp b = ''.chomp c = "abc\n".chomp d = "abc\n\n".chomp e = "abc\t".chomp("\t") f = "abc\n" f.chomp assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e assert_equal "abc\n", f end assert('String#chomp!', '15.2.10.5.10') do a = 'abc' b = '' c = "abc\n" d = "abc\n\n" e = "abc\t" a.chomp! b.chomp! c.chomp! d.chomp! e.chomp!("\t") assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e end assert('String#chop', '15.2.10.5.11') do a = ''.chop b = 'abc'.chop c = 'abc' c.chop assert_equal '', a assert_equal 'ab', b assert_equal 'abc', c end assert('String#chop(UTF-8)', '15.2.10.5.11') do a = ''.chop b = 'あいう'.chop c = "あ\nい".chop.chop assert_equal '', a assert_equal 'あい', b assert_equal 'あ', c end if UTF8STRING assert('String#chop!', '15.2.10.5.12') do a = '' b = 'abc' a.chop! b.chop! assert_equal a, '' assert_equal b, 'ab' end assert('String#chop!(UTF-8)', '15.2.10.5.12') do a = '' b = "あいうえ\n" c = "あいうえ\n" a.chop! b.chop! c.chop! c.chop! assert_equal a, '' assert_equal b, 'あいうえ' assert_equal c, 'あいう' end if UTF8STRING assert('String#downcase', '15.2.10.5.13') do a = 'ABC'.downcase b = 'ABC' b.downcase assert_equal 'abc', a assert_equal 'ABC', b end assert('String#downcase!', '15.2.10.5.14') do a = 'ABC' a.downcase! assert_equal 'abc', a assert_equal nil, 'abc'.downcase! end assert('String#each_line', '15.2.10.5.15') do a = "first line\nsecond line\nthird line" list = ["first line\n", "second line\n", "third line"] n_list = [] a.each_line do |line| n_list << line end assert_equal list, n_list end assert('String#empty?', '15.2.10.5.16') do a = '' b = 'not empty' assert_true a.empty? assert_false b.empty? end assert('String#eql?', '15.2.10.5.17') do assert_true 'abc'.eql?('abc') assert_false 'abc'.eql?('cba') end assert('String#gsub', '15.2.10.5.18') do assert_equal('aBcaBc', 'abcabc'.gsub('b', 'B'), 'gsub without block') assert_equal('aBcaBc', 'abcabc'.gsub('b'){|w| w.capitalize }, 'gsub with block') assert_equal('$a$a$', '#a#a#'.gsub('#', '$'), 'mruby/mruby#847') assert_equal('$a$a$', '#a#a#'.gsub('#'){|w| '$' }, 'mruby/mruby#847 with block') assert_equal('$$a$$', '##a##'.gsub('##', '$$'), 'mruby/mruby#847 another case') assert_equal('$$a$$', '##a##'.gsub('##'){|w| '$$' }, 'mruby/mruby#847 another case with block') assert_equal('A', 'a'.gsub('a', 'A')) assert_equal('A', 'a'.gsub('a'){|w| w.capitalize }) end assert('String#gsub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cd<\\>ef', s.gsub('X', '<\\\\>') assert_equal 'abcdef', s.gsub('X', '<\\&>') assert_equal 'abcdef', s.gsub('X', '<\\0>') assert_equal 'abcdef', s.gsub('X', '<\\`>') assert_equal 'abcdef', s.gsub('X', '<\\\'>') end assert('String#gsub!', '15.2.10.5.19') do a = 'abcabc' a.gsub!('b', 'B') b = 'abcabc' b.gsub!('b') { |w| w.capitalize } assert_equal 'aBcaBc', a assert_equal 'aBcaBc', b end assert('String#hash', '15.2.10.5.20') do a = 'abc' assert_equal 'abc'.hash, a.hash end assert('String#include?', '15.2.10.5.21') do assert_true 'abc'.include?(97) assert_false 'abc'.include?(100) assert_true 'abc'.include?('a') assert_false 'abc'.include?('d') end assert('String#index', '15.2.10.5.22') do assert_equal 0, 'abc'.index('a') assert_nil 'abc'.index('d') assert_equal 3, 'abcabc'.index('a', 1) end assert('String#initialize', '15.2.10.5.23') do a = '' a.initialize('abc') assert_equal 'abc', a a.initialize('abcdefghijklmnopqrstuvwxyz') assert_equal 'abcdefghijklmnopqrstuvwxyz', a end assert('String#initialize_copy', '15.2.10.5.24') do a = '' a.initialize_copy('abc') assert_equal 'abc', a end assert('String#intern', '15.2.10.5.25') do assert_equal :abc, 'abc'.intern end assert('String#length', '15.2.10.5.26') do assert_equal 3, 'abc'.length end # 'String#match', '15.2.10.5.27' will be tested in mrbgems. assert('String#replace', '15.2.10.5.28') do a = '' a.replace('abc') assert_equal 'abc', a assert_equal 'abc', 'cba'.replace(a) b = 'abc' * 10 c = ('cba' * 10).dup b.replace(c); c.replace(b); assert_equal c, b # shared string s = "foo" * 100 a = s[10, 90] # create shared string assert_equal("", s.replace("")) # clear assert_equal("", s) # s is cleared assert_not_equal("", a) # a should not be affected end assert('String#reverse', '15.2.10.5.29') do a = 'abc' a.reverse assert_equal 'abc', a assert_equal 'cba', 'abc'.reverse end assert('String#reverse(UTF-8)', '15.2.10.5.29') do assert_equal "ち", "こんにちは世界"[3] assert_equal nil, "こんにちは世界"[20] assert_equal "世", "こんにちは世界"[-2] assert_equal "世界", "こんにちは世界"[-2..-1] assert_equal "んに", "こんにちは世界"[1,2] assert_equal "世", "こんにちは世界"["世"] end if UTF8STRING assert('String#reverse!', '15.2.10.5.30') do a = 'abc' a.reverse! assert_equal 'cba', a assert_equal 'cba', 'abc'.reverse! end assert('String#reverse!(UTF-8)', '15.2.10.5.30') do a = 'こんにちは世界!' a.reverse! assert_equal '!界世はちにんこ', a assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse! end if UTF8STRING assert('String#rindex', '15.2.10.5.31') do assert_equal 0, 'abc'.rindex('a') assert_nil 'abc'.rindex('d') assert_equal 0, 'abcabc'.rindex('a', 1) assert_equal 3, 'abcabc'.rindex('a', 4) end assert('String#rindex(UTF-8)', '15.2.10.5.31') do str = "こんにちは世界!\nこんにちは世界!" assert_nil str.index('さ') assert_equal 3, str.index('ち') assert_equal 12, str.index('ち', 10) assert_equal nil, str.index("さ") end if UTF8STRING # 'String#scan', '15.2.10.5.32' will be tested in mrbgems. assert('String#size', '15.2.10.5.33') do assert_equal 3, 'abc'.size end assert('String#size(UTF-8)', '15.2.10.5.33') do str = 'こんにちは世界!' assert_equal 8, str.size assert_not_equal str.bytesize, str.size assert_equal 2, str[1, 2].size end if UTF8STRING assert('String#slice', '15.2.10.5.34') do # length of args is 1 a = 'abc'.slice(0) b = 'abc'.slice(-1) c = 'abc'.slice(10) d = 'abc'.slice(-10) # length of args is 2 a1 = 'abc'.slice(0, -1) b1 = 'abc'.slice(10, 0) c1 = 'abc'.slice(-10, 0) d1 = 'abc'.slice(0, 0) e1 = 'abc'.slice(1, 2) # slice of shared string e11 = e1.slice(0) # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc'.slice('bc') b3 = 'abc'.slice('XX') assert_equal 'a', a assert_equal 'c', b assert_nil c assert_nil d assert_nil a1 assert_nil b1 assert_nil c1 assert_equal '', d1 assert_equal 'bc', e1 assert_equal 'b', e11 assert_equal 'bc', a3 assert_nil b3 end # TODO Broken ATM assert('String#split', '15.2.10.5.35') do # without RegExp behavior is actually unspecified assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split assert_equal ["a", "b", "c", "", "d"], 'a,b,c,,d'.split(',') assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split(nil) assert_equal ['a', 'b', 'c'], 'abc'.split("") end assert('String#split(UTF-8)', '15.2.10.5.35') do got = "こんにちは世界!".split('') assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got got = "こんにちは世界!".split('に') assert_equal ['こん', 'ちは世界!'], got end if UTF8STRING assert('String#sub', '15.2.10.5.36') do assert_equal 'aBcabc', 'abcabc'.sub('b', 'B') assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize } assert_equal 'aa$', 'aa#'.sub('#', '$') end assert('String#sub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cdXef', s.sub('X', '<\\\\>') assert_equal 'abcdXef', s.sub('X', '<\\&>') assert_equal 'abcdXef', s.sub('X', '<\\0>') assert_equal 'abcdXef', s.sub('X', '<\\`>') assert_equal 'abcdXef', s.sub('X', '<\\\'>') end assert('String#sub!', '15.2.10.5.37') do a = 'abcabc' a.sub!('b', 'B') b = 'abcabc' b.sub!('b') { |w| w.capitalize } assert_equal 'aBcabc', a assert_equal 'aBcabc', b end assert('String#to_f', '15.2.10.5.38') do a = ''.to_f b = '123456789'.to_f c = '12345.6789'.to_f assert_float(0.0, a) assert_float(123456789.0, b) assert_float(12345.6789, c) end assert('String#to_i', '15.2.10.5.39') do a = ''.to_i b = '32143'.to_i c = 'a'.to_i(16) d = '100'.to_i(2) e = '1_000'.to_i assert_equal 0, a assert_equal 32143, b assert_equal 10, c assert_equal 4, d assert_equal 1_000, e end assert('String#to_s', '15.2.10.5.40') do assert_equal 'abc', 'abc'.to_s end assert('String#to_sym', '15.2.10.5.41') do assert_equal :abc, 'abc'.to_sym end assert('String#upcase', '15.2.10.5.42') do a = 'abc'.upcase b = 'abc' b.upcase assert_equal 'ABC', a assert_equal 'abc', b end assert('String#upcase!', '15.2.10.5.43') do a = 'abc' a.upcase! assert_equal 'ABC', a assert_equal nil, 'ABC'.upcase! a = 'abcdefghijklmnopqrstuvwxyz' b = a.dup a.upcase! b.upcase! assert_equal 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', b end assert('String#inspect', '15.2.10.5.46') do # should not raise an exception - regress #1210 assert_nothing_raised do ("\1" * 100).inspect end assert_equal "\"\\000\"", "\0".inspect end # Not ISO specified assert('String interpolation (mrb_str_concat for shared strings)') do a = "A" * 32 assert_equal "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:", "#{a}:" end assert('Check the usage of a NUL character') do "qqq\0ppp" end assert('String#bytes') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] str2 = "\xFF" bytes2 = [0xFF] assert_equal bytes1, str1.bytes assert_equal bytes2, str2.bytes end assert('String#each_byte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] bytes2 = [] str1.each_byte {|b| bytes2 << b } assert_equal bytes1, bytes2 end assert('String#freeze') do str = "hello" str.freeze assert_raise(RuntimeError) { str.upcase! } end mruby-1.2.0+20160315+git4f20d58a/test/t/superclass.rb000066400000000000000000000035231267140355100213440ustar00rootroot00000000000000[ # [:Object, :implementation_defined_value, '15.2.2.1'], [:Module, :Object, '15.2.2.2'], [:Class, :Module, '15.2.3.2'], [:NilClass, :Object, '15.2.4.2'], [:TrueClass, :Object, '15.2.5.2'], [:FalseClass, :Object, '15.2.6.2'], [:Numeric, :Object, '15.2.7.2'], [:Integer, :Numeric, '15.2.8.2'], [:Float, :Numeric, '15.2.9.2'], [:String, :Object, '15.2.10.2'], [:Symbol, :Object, '15.2.11.2'], [:Array, :Object, '15.2.12.2'], [:Hash, :Object, '15.2.13.2'], [:Range, :Object, '15.2.14.2'], # [:Regexp, :Object, '15.2.15.2'], #No Regexp in mruby core # [:MatchData, :Object, '15.2.16.2'], [:Proc, :Object, '15.2.17.2'], # [:Struct, :Object, '15.2.18.2'], # [:Time, :Object, '15.2.19.2'], # [:IO, :Object, '15.2.20.2'], # [:File, :IO, '15.2.21.2'], [:Exception, :Object, '15.2.22.2'], [:StandardError, :Exception, '15.2.23.2'], [:ArgumentError, :StandardError, '15.2.24.2'], [:LocalJumpError, :StandardError, '15.2.25.2'], [:RangeError, :StandardError, '12.2.26.2'], [:RegexpError, :StandardError, '12.2.27.2'], [:RuntimeError, :StandardError, '12.2.28.2'], [:TypeError, :StandardError, '12.2.29.2'], # [:ZeroDivisionError, :StandardError, '12.2.30.2'], # No ZeroDivisionError in mruby [:NameError, :StandardError, '15.2.31.2'], [:NoMethodError, :NameError, '15.2.32.2'], [:IndexError, :StandardError, '15.2.33.2'], # [:IOError, :StandardError, '12.2.34.2'], # [:EOFError, :IOError, '12.2.35.2'], # [:SystemCallError, :StandardError, '15.2.36.2'], [:ScriptError, :Exception, '12.2.37.2'], [:SyntaxError, :ScriptError, '12.2.38.2'], # [:LoadError, :ScriptError, '12.2.39,2'], ].each do |cls, super_cls, iso| assert "Direct superclass of #{cls}", iso do skip "#{cls} isn't defined" unless Object.const_defined? cls assert_equal Object.const_get(super_cls), Object.const_get(cls).superclass end end mruby-1.2.0+20160315+git4f20d58a/test/t/symbol.rb000066400000000000000000000006601267140355100204640ustar00rootroot00000000000000## # Symbol ISO Test assert('Symbol', '15.2.11') do assert_equal Class, Symbol.class end assert('Symbol#===', '15.2.11.3.1') do assert_true :abc == :abc assert_false :abc == :cba end assert('Symbol#id2name', '15.2.11.3.2') do assert_equal 'abc', :abc.id2name end assert('Symbol#to_s', '15.2.11.3.3') do assert_equal 'abc', :abc.to_s end assert('Symbol#to_sym', '15.2.11.3.4') do assert_equal :abc, :abc.to_sym end mruby-1.2.0+20160315+git4f20d58a/test/t/syntax.rb000066400000000000000000000156541267140355100205160ustar00rootroot00000000000000assert('__FILE__') do file = __FILE__[-9, 9] assert_equal 'syntax.rb', file end assert('__LINE__') do assert_equal 7, __LINE__ end assert('super', '11.3.4') do assert_raise NoMethodError do super end class SuperFoo def foo true end def bar(*a) a end end class SuperBar < SuperFoo def foo super end def bar(*a) super(*a) end end bar = SuperBar.new assert_true bar.foo assert_equal [1,2,3], bar.bar(1,2,3) end assert('yield', '11.3.5') do assert_raise LocalJumpError do yield end end assert('Abbreviated variable assignment', '11.4.2.3.2') do a ||= 1 b &&= 1 c = 1 c += 2 assert_equal 1, a assert_nil b assert_equal 3, c end assert('case expression', '11.5.2.2.4') do # case-expression-with-expression, one when-clause x = 0 case "a" when "a" x = 1 end assert_equal 1, x # case-expression-with-expression, multiple when-clauses x = 0 case "b" when "a" x = 1 when "b" x = 2 end assert_equal 2, x # no matching when-clause x = 0 case "c" when "a" x = 1 when "b" x = 2 end assert_equal 0, x # case-expression-with-expression, one when-clause and one else-clause a = 0 case "c" when "a" x = 1 else x = 3 end assert_equal 3, x # case-expression-without-expression, one when-clause x = 0 case when true x = 1 end assert_equal 1, x # case-expression-without-expression, multiple when-clauses x = 0 case when 0 == 1 x = 1 when 1 == 1 x = 2 end assert_equal 2, x # case-expression-without-expression, one when-clause and one else-clause x = 0 case when 0 == 1 x = 1 else x = 3 end assert_equal 3, x # multiple when-arguments x = 0 case 4 when 1, 3, 5 x = 1 when 2, 4, 6 x = 2 end assert_equal 2, x # when-argument with splatting argument x = :integer odds = [ 1, 3, 5, 7, 9 ] evens = [ 2, 4, 6, 8 ] case 5 when *odds x = :odd when *evens x = :even end assert_equal :odd, x true end assert('Nested const reference') do module Syntax4Const CONST1 = "hello world" class Const2 def const1 CONST1 end end end assert_equal "hello world", Syntax4Const::CONST1 assert_equal "hello world", Syntax4Const::Const2.new.const1 end assert('Abbreviated variable assignment as returns') do module Syntax4AbbrVarAsgnAsReturns class A def b @c ||= 1 end end end assert_equal 1, Syntax4AbbrVarAsgnAsReturns::A.new.b end assert('Splat and multiple assignment') do *a = *[1,2,3] b, *c = *[7,8,9] assert_equal [1,2,3], a assert_equal 7, b assert_equal [8,9], c (a, b), c = [1,2],3 assert_equal [1,2,3], [a,b,c] (a, b), c = 1,2,3 assert_equal [1,nil,2], [a,b,c] end assert('Splat and multiple assignment from variable') do a = [1, 2, 3] b, *c = a assert_equal 1, b assert_equal [2, 3], c end assert('Splat and multiple assignment from variables') do a = [1, 2, 3] b = [4, 5, 6, 7] c, d, *e, f, g = *a, *b assert_equal 1, c assert_equal 2, d assert_equal [3, 4, 5], e assert_equal 6, f assert_equal 7, g end assert('Splat and multiple assignment in for') do a = [1, 2, 3, 4, 5, 6, 7] for b, c, *d, e, f in [a] do end assert_equal 1, b assert_equal 2, c assert_equal [3, 4, 5], d assert_equal 6, e assert_equal 7, f end assert('Splat without assignment') do * = [0] a, * = [1, 2] assert_equal 1, a end assert('multiple assignment (rest)') do *a = 0 assert_equal [0], a end assert('multiple assignment (rest+post)') do *a, b = 0, 1, 2 *c, d = 3 assert_equal [0, 1], a assert_equal 2, b assert_equal [], c assert_equal 3, d end assert('multiple assignment (nosplat array rhs)') do a, *b = [] *c, d = [0] e, *f, g = [1, 2] assert_nil a assert_equal [], b assert_equal [], c assert_equal 0, d assert_equal 1, e assert_equal [], f assert_equal 2, g end assert('Return values of case statements') do a = [] << case 1 when 3 then 2 when 2 then 2 when 1 then 2 end b = [] << case 1 when 2 then 2 else end def fb n = 0 Proc.new do n += 1 case when n % 15 == 0 else n end end end assert_equal [2], a assert_equal [nil], b assert_equal 1, fb.call end assert('Return values of if and case statements') do true_clause_value = if true 1 else case 2 when 3 end 4 end assert_equal 1, true_clause_value end assert('Return values of no expression case statement') do when_value = case when true 1 end assert_equal 1, when_value end assert('splat in case statement') do values = [3,5,1,7,8] testa = [1,2,7] testb = [5,6] resulta = [] resultb = [] resultc = [] values.each do |value| case value when *testa resulta << value when *testb resultb << value else resultc << value end end assert_equal [1,7], resulta assert_equal [5], resultb assert_equal [3,8], resultc end assert('External command execution.') do class << Kernel sym = '`'.to_sym alias_method :old_cmd, sym results = [] define_method(sym) do |str| results.push str str end `test` # NOVAL NODE_XSTR `test dynamic #{sym}` # NOVAL NODE_DXSTR assert_equal ['test', 'test dynamic `'], results t = `test` # VAL NODE_XSTR assert_equal 'test', t assert_equal ['test', 'test dynamic `', 'test'], results t = `test dynamic #{sym}` # VAL NODE_DXSTR assert_equal 'test dynamic `', t assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results alias_method sym, :old_cmd end true end assert('parenthesed do-block in cmdarg') do class ParenDoBlockCmdArg def test(block) block.call end end x = ParenDoBlockCmdArg.new result = x.test (Proc.new do :ok; end) assert_equal :ok, result end assert('method definition in cmdarg') do if false bar def foo; self.each do end end end true end assert('optional argument in the rhs default expressions') do class OptArgInRHS def foo "method called" end def t(foo = foo) foo end def t2(foo = foo()) foo end end o = OptArgInRHS.new assert_nil(o.t) assert_equal("method called", o.t2) end assert('optional block argument in the rhs default expressions') do assert_nil(Proc.new {|foo = foo| foo}.call) end assert('multiline comments work correctly') do =begin this is a comment with nothing after begin and end =end =begin this is a comment this is a comment with extra after =begin =end =begin this is a comment that has =end with spaces after it =end =begin this is a comment this is a comment that has extra after =begin and =end with spaces after it =end line = __LINE__ =begin this is a comment this is a comment that has extra after =begin and =end with tabs after it =end xxxxxxxxxxxxxxxxxxxxxxxxxx assert_equal(line + 4, __LINE__) end mruby-1.2.0+20160315+git4f20d58a/test/t/true.rb000066400000000000000000000011621267140355100201340ustar00rootroot00000000000000## # TrueClass ISO Test assert('TrueClass', '15.2.5') do assert_equal Class, TrueClass.class end assert('TrueClass true', '15.2.5.1') do assert_true true assert_equal TrueClass, true.class assert_false TrueClass.method_defined? :new end assert('TrueClass#&', '15.2.5.3.1') do assert_true true.&(true) assert_false true.&(false) end assert('TrueClass#^', '15.2.5.3.2') do assert_false true.^(true) assert_true true.^(false) end assert('TrueClass#to_s', '15.2.5.3.3') do assert_equal 'true', true.to_s end assert('TrueClass#|', '15.2.5.3.4') do assert_true true.|(true) assert_true true.|(false) end mruby-1.2.0+20160315+git4f20d58a/test/t/typeerror.rb000066400000000000000000000001451267140355100212100ustar00rootroot00000000000000## # TypeError ISO Test assert('TypeError', '15.2.29') do assert_equal Class, TypeError.class end mruby-1.2.0+20160315+git4f20d58a/test/t/unicode.rb000066400000000000000000000020061267140355100206010ustar00rootroot00000000000000# Test of the \u notation assert('bare \u notation test') do # Mininum and maximum one byte characters assert_equal("\u0000", "\x00") assert_equal("\u007F", "\x7F") # Mininum and maximum two byte characters assert_equal("\u0080", "\xC2\x80") assert_equal("\u07FF", "\xDF\xBF") # Mininum and maximum three byte characters assert_equal("\u0800", "\xE0\xA0\x80") assert_equal("\uFFFF", "\xEF\xBF\xBF") # Four byte characters require the \U notation end assert('braced \u notation test') do # Mininum and maximum one byte characters assert_equal("\u{0000}", "\x00") assert_equal("\u{007F}", "\x7F") # Mininum and maximum two byte characters assert_equal("\u{0080}", "\xC2\x80") assert_equal("\u{07FF}", "\xDF\xBF") # Mininum and maximum three byte characters assert_equal("\u{0800}", "\xE0\xA0\x80") assert_equal("\u{FFFF}", "\xEF\xBF\xBF") # Mininum and maximum four byte characters assert_equal("\u{10000}", "\xF0\x90\x80\x80") assert_equal("\u{10FFFF}", "\xF4\x8F\xBF\xBF") end mruby-1.2.0+20160315+git4f20d58a/travis_config.rb000066400000000000000000000020511267140355100205660ustar00rootroot00000000000000MRuby::Build.new('debug') do |conf| toolchain :gcc enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement) conf.compilers.each do |c| c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA) end build_mrbc_exec end MRuby::Build.new('full-debug') do |conf| toolchain :gcc enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK) conf.enable_test end MRuby::Build.new do |conf| toolchain :gcc # include all core GEMs conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement) conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test end MRuby::Build.new('cxx_abi') do |conf| toolchain :gcc conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement) conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test enable_cxx_abi build_mrbc_exec end