pax_global_header00006660000000000000000000000064122516330740014515gustar00rootroot0000000000000052 comment=882afdea20f344c2a3ed4842a6269fe2b8922493 mruby-0.0.0~20131214+git882afdea/000077500000000000000000000000001225163307400156665ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/.gitignore000066400000000000000000000001741225163307400176600ustar00rootroot00000000000000# / *.bak *.d *.o *.orig *.rej *.sav *.swp *.tmp *~ .DS_Store .ccmalloc .svn /.git cscope.out /src/y.tab.c /bin /build /lib mruby-0.0.0~20131214+git882afdea/.travis.yml000066400000000000000000000002021225163307400177710ustar00rootroot00000000000000# no installation... install: - sudo apt-get -q install gperf env: MRUBY_CONFIG=travis_config.rb script: "./minirake all test" mruby-0.0.0~20131214+git882afdea/AUTHORS000066400000000000000000000007041225163307400167370ustar00rootroot00000000000000Original Authors "mruby developers" are: Yukihiro Matsumoto FUKUOKA CSK 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 mruby-0.0.0~20131214+git882afdea/CONTRIBUTING.md000066400000000000000000000042531225163307400201230ustar00rootroot00000000000000# 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) ## 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 prefered 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-0.0.0~20131214+git882afdea/ChangeLog000066400000000000000000000006311225163307400174400ustar00rootroot00000000000000Thu 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-0.0.0~20131214+git882afdea/INSTALL000066400000000000000000000013151225163307400167170ustar00rootroot00000000000000* Prerequisites 1. Make sure you have bison (http://www.gnu.org/software/bison/) installed in your system. 2. Make sure you have ruby installed in your system (required to build). * Compilation and Installation 1. Run make in the top directory. This command will create the following directories and store libraries and binaries files into them. * bin * lib * include You can directory invoke 'minirake' as the following. $ ruby ./minirake If an error occurs when compiling mruby, it will be helpful for others if you send a detailed report to the developers that includes the error log, machine, and OS type. * Porting to other platforms That's all. mruby-0.0.0~20131214+git882afdea/LEGAL000066400000000000000000000002601225163307400164330ustar00rootroot00000000000000LEGAL NOTICE INFORMATION ------------------------ All the files in this distribution are covered under the MIT license (see the file MITL) except some files mentioned below: mruby-0.0.0~20131214+git882afdea/MITL000066400000000000000000000020611225163307400163550ustar00rootroot00000000000000Copyright (c) 2013 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-0.0.0~20131214+git882afdea/Makefile000066400000000000000000000004271225163307400173310ustar00rootroot00000000000000# 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 .PHONY : all all : $(RAKE) .PHONY : test test : all $(RAKE) test .PHONY : clean clean : $(RAKE) clean mruby-0.0.0~20131214+git882afdea/NEWS000066400000000000000000000005271225163307400163710ustar00rootroot00000000000000* 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-0.0.0~20131214+git882afdea/README.md000066400000000000000000000107371225163307400171550ustar00rootroot00000000000000# !!Notice!! This is a preliminary release for internal team review. The URLs and addresses described below are not available yet. The official release will be announced later. Any suggestions for modification are welcome. Delays in replies are to be expected. Sorry in advance. [![Build Status](https://travis-ci.org/mruby/mruby.png?branch=master)](https://travis-ci.org/mruby/mruby) ## What's mruby mruby is the lightweight implementation of the Ruby language complying to (part of) the [ISO standard](http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579). 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 "bin" directory. The "mrbc" is also able to generate compiled byte code in a C source file. You can check the "mrbtest" program under the "test" directory. 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 mruby distribution files can be found in the following site: 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 There are some other branches under development. Try the following command and see the list of branches: $ git branch -r ## mruby home-page mruby's website is not launched yet but we are actively working on it. The URL of the mruby home-page will be: http://www.mruby.org/ ## Mailing list To subscribe to the mruby mailing list....[T.B.D.] ## How to compile and install See the INSTALL file. ## Running Tests To run the tests, execute the following from the project's root directory. $ make test Or $ ruby ./minirake test ## Customization mruby contains a package manager called *mrbgems*. To create extensions in C and/or Ruby you should create a *GEM*. You will find a complete documentation with examples under *examples/mrbgems*. ## License Copyright (c) 2013 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. ## 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](https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md) 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. mruby-0.0.0~20131214+git882afdea/Rakefile000066400000000000000000000072201225163307400173340ustar00rootroot00000000000000# 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') # 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}/tools/mrbc/mrbc.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" load "#{MRUBY_ROOT}/tasks/mrbgems_test.rake" load "#{MRUBY_ROOT}/test/mrbtest.rake" ############################## # generic build targets, rules task :default => :all depfiles = MRuby.targets['host'].bins.map do |bin| install_path = MRuby.targets['host'].exefile("#{MRUBY_ROOT}/bin/#{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 = "#{build_dir}/#{relative_from_root}" gem.bins.each do |bin| exec = exefile("#{build_dir}/bin/#{bin}") objs = Dir.glob("#{current_dir}/tools/#{bin}/*.{c,cpp,cxx}").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 end if target == MRuby.targets['host'] install_path = MRuby.targets['host'].exefile("#{MRUBY_ROOT}/bin/#{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 ] 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 => MRuby.targets.values.map { |t| t.build_mrbtest_lib_only? ? t.libfile("#{t.build_dir}/test/mrbtest") : t.exefile("#{t.build_dir}/test/mrbtest") } do MRuby.each_target do run_test unless build_mrbtest_lib_only? 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 build folder" end mruby-0.0.0~20131214+git882afdea/TODO000066400000000000000000000005061225163307400163570ustar00rootroot00000000000000Things to do (Things that are not done yet) * Special variables ($1,$2..) * super in aliased methods Things to improve (Done but things to fix) * Hash (Reduce size. Use khash or save the order) * stringEx (Delete encoding、delete CODERANGE、delete everything except UTF-8 or ASCII) * Make additions as they are noticed. mruby-0.0.0~20131214+git882afdea/benchmark/000077500000000000000000000000001225163307400176205ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/benchmark/ao-render.rb000066400000000000000000000153521225163307400220270ustar00rootroot00000000000000# AO render benchmark # Original program (C) Syoyo Fujita in Javascript (and other languages) # http://lucille.atso-net.jp/blog/?p=642 # http://lucille.atso-net.jp/blog/?p=711 # Ruby(yarv2llvm) version by Hideki Miura # mruby version by Hideki Miura # IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 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-0.0.0~20131214+git882afdea/benchmark/bm_so_lists.rb000066400000000000000000000017471225163307400224730ustar00rootroot00000000000000#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', :branch => 'master' # 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 = '/' 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-0.0.0~20131214+git882afdea/doc/000077500000000000000000000000001225163307400164335ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/doc/.gitkeep000066400000000000000000000000001225163307400200520ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/doc/compile/000077500000000000000000000000001225163307400200635ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/doc/compile/README.md000066400000000000000000000254701225163307400213520ustar00rootroot00000000000000# Compile mruby is using 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) * 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: 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. toolchain :gcc #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. 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. toolchain :androideabi 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: 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```. conf.file_separator = '/' ### C Compiler Configuration of the C compiler binary, flags and include paths. conf.cc do |cc| cc.command = ... cc.flags = ... cc.include_paths = ... cc.defines = ... cc.option_include_path = ... cc.option_define = ... cc.compile_options = ... end ### Linker Configuration of the Linker binary, flags and library paths. 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. conf.archiver do |archiver| archiver.command = ... archiver.archive_options = ... end ### Parser Generator Configuration of the Parser Generator binary and flags. conf.yacc do |yacc| yacc.command = ... yacc.compile_options = ... end ### GPerf Configuration of the GPerf binary and flags. conf.gperf do |gperf| gperf.command = ... gperf.compile_options = ... end ### File Extensions conf.exts do |exts exts.object = ... exts.executable = ... exts.library = ... end ### Mrbgems Integrate GEMs in the build process. # 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``` conf.build_mrbtest_lib_only ## 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: 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```. ## 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 direcotry. 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. MRuby::CrossBuild.new('Minimal') do |conf| toolchain :gcc conf.cc.defines = %w(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-0.0.0~20131214+git882afdea/doc/mrbgems/000077500000000000000000000000001225163307400200675ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/doc/mrbgems/README.md000066400000000000000000000200231225163307400213430ustar00rootroot00000000000000# 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: conf.gem '/path/to/your/gem/dir' You can also use a relative path which would be relative from the mruby root: conf.gem 'examples/mrbgems/ruby_extension_example' A remote GIT repository location for a GEM is also supported: 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 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*: 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: 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 absolute path like below. 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 | +- 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 files to extend mruby. The folder *test* contains 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: MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' 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` (Short summary) * `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)` like: MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # add GEM dependency mruby-parser. # Version has to be between 1.0.0 and 1.5.2 spec.add_dependency('mruby-parser', '> 1.0.0', '< 1.5.2') end The usage of versions is optional. __ATTENTION:__ The dependency system is currently (May 2013) under development and doesn't check or resolve dependencies! In case your GEM has more complex build requirements you can use the following options additionally inside of your GEM specification: * `spec.cflags` (C compiler flags) * `spec.mruby_cflags` (global C compiler flags for everything) * `spec.mruby_ldflags` (global linker flags for everything) * `spec.mruby_libs` (global libraries for everything) * `spec.mruby_includes` (global includes for everything) * `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) ## 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: 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: 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. ### 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-0.0.0~20131214+git882afdea/examples/000077500000000000000000000000001225163307400175045ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/000077500000000000000000000000001225163307400211405ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/000077500000000000000000000000001225163307400270545ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/README.md000066400000000000000000000001501225163307400303270ustar00rootroot00000000000000C and Ruby Extension Example ========= This is an example gem which implements a C and Ruby extension. mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake000066400000000000000000000015121225163307400311700ustar00rootroot00000000000000MRuby::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-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/mrblib/000077500000000000000000000000001225163307400303235ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb000066400000000000000000000001351225163307400323020ustar00rootroot00000000000000module CRubyExtension def CRubyExtension.ruby_method puts "A Ruby Extension" end end mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/src/000077500000000000000000000000001225163307400276435ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/src/example.c000066400000000000000000000007241225163307400314450ustar00rootroot00000000000000#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-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/test/000077500000000000000000000000001225163307400300335ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_and_ruby_extension_example/test/example.rb000066400000000000000000000002621225163307400320130ustar00rootroot00000000000000assert('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-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/000077500000000000000000000000001225163307400251715ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/README.md000066400000000000000000000001261225163307400264470ustar00rootroot00000000000000C Extension Example ========= This is an example gem which implements a C extension. mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/mrbgem.rake000066400000000000000000000015031225163307400273050ustar00rootroot00000000000000MRuby::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-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/src/000077500000000000000000000000001225163307400257605ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/src/example.c000066400000000000000000000006761225163307400275700ustar00rootroot00000000000000#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-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/test/000077500000000000000000000000001225163307400261505ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/test/example.c000066400000000000000000000001541225163307400277470ustar00rootroot00000000000000#include void mrb_c_extension_example_gem_test(mrb_state *mrb) { /* test initializer in C */ } mruby-0.0.0~20131214+git882afdea/examples/mrbgems/c_extension_example/test/example.rb000066400000000000000000000001101225163307400301200ustar00rootroot00000000000000assert('C Extension Example') do CExtension.respond_to? :c_method end mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/000077500000000000000000000000001225163307400257305ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/README.md000066400000000000000000000001461225163307400272100ustar00rootroot00000000000000Pure Ruby Extension Example ========= This is an example gem which implements a pure Ruby extension. mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/mrbgem.rake000066400000000000000000000015041225163307400300450ustar00rootroot00000000000000MRuby::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' # 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-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/mrblib/000077500000000000000000000000001225163307400271775ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/mrblib/example.rb000066400000000000000000000001321225163307400311530ustar00rootroot00000000000000class RubyExtension def RubyExtension.ruby_method puts "A Ruby Extension" end end mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/test/000077500000000000000000000000001225163307400267075ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/mrbgems/ruby_extension_example/test/example.rb000066400000000000000000000001211225163307400306610ustar00rootroot00000000000000assert('Ruby Extension Example') do RubyExtension.respond_to? :ruby_method end mruby-0.0.0~20131214+git882afdea/examples/targets/000077500000000000000000000000001225163307400211555ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/examples/targets/ArduinoDue.rb000066400000000000000000000044661225163307400235530ustar00rootroot00000000000000# Cross Compiling configuration for Arduino Due # http://arduino.cc/en/Main/ArduinoBoardDue # # Requires Arduino IDE >= 1.5 MRuby::CrossBuild.new("Arduino Due") do |conf| toolchain :gcc # Mac OS X # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java' # GNU Linux ARDUINO_PATH = '/opt/arduino' BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/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=152 -D__SAM3X8E__ -mthumb -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON) 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(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 = "#{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 #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-0.0.0~20131214+git882afdea/examples/targets/chipKitMax32.rb000066400000000000000000000044521225163307400237150ustar00rootroot00000000000000# 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(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 #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-0.0.0~20131214+git882afdea/include/000077500000000000000000000000001225163307400173115ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/include/mrbconf.h000066400000000000000000000041271225163307400211140ustar00rootroot00000000000000/* ** 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 /* 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 /* initial size for IREP array */ //#define MRB_IREP_ARRAY_INIT_SIZE (256u) /* 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 /* -DDISABLE_XXXX to drop following features */ //#define DISABLE_STDIO /* use of stdio */ /* -DENABLE_XXXX to enable following features */ //#define ENABLE_DEBUG /* hooks for debugger */ /* end of configuration */ /* define ENABLE_XXXX from DISABLE_XXX */ #ifndef DISABLE_STDIO #define ENABLE_STDIO #endif #ifndef ENABLE_DEBUG #define DISABLE_DEBUG #endif #ifdef ENABLE_STDIO # include #endif #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #endif /* MRUBYCONF_H */ mruby-0.0.0~20131214+git882afdea/include/mruby.h000066400000000000000000000362141225163307400206260ustar00rootroot00000000000000/* ** mruby - An embeddable Ruby implementation ** ** Copyright (c) mruby developers 2010-2013 ** ** 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 #if defined(__cplusplus) extern "C" { #endif #include #include #include "mrbconf.h" #include "mruby/value.h" typedef uint32_t mrb_code; typedef uint32_t mrb_aspec; struct mrb_irep; struct mrb_state; typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud); #ifndef MRB_GC_ARENA_SIZE #define MRB_GC_ARENA_SIZE 100 #endif typedef struct { mrb_sym mid; struct RProc *proc; int stackidx; int nregs; int argc; mrb_code *pc; /* return address */ mrb_code *err; /* error position */ int acc; struct RClass *target_class; int ridx; int eidx; struct REnv *env; } mrb_callinfo; enum mrb_fiber_state { MRB_FIBER_CREATED = 0, MRB_FIBER_RUNNING, MRB_FIBER_RESUMED, 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; struct RFiber *fib; }; enum gc_state { GC_STATE_NONE = 0, GC_STATE_MARK, GC_STATE_SWEEP }; typedef struct mrb_state { void *jmp; mrb_allocf allocf; /* memory allocation function */ struct mrb_context *c; struct mrb_context *root_c; struct RObject *exc; /* exception */ 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 heap_page *heaps; /* heaps for GC */ struct heap_page *sweeps; struct 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; enum gc_state gc_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 gc_live_after_mark; size_t gc_threshold; int gc_interval_ratio; int gc_step_ratio; mrb_bool gc_disabled:1; mrb_bool gc_full:1; mrb_bool is_generational_gc_mode:1; mrb_bool out_of_memory:1; size_t majorgc_old_threshold; struct alloca_header *mems; mrb_sym symidx; struct kh_n2s *name2sym; /* symbol table */ #ifdef ENABLE_DEBUG void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs); #endif struct RClass *eException_class; struct RClass *eStandardError_class; void *ud; /* auxiliary data */ } mrb_state; typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value); struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass*); struct RClass *mrb_define_module(mrb_state *, const char*); mrb_value mrb_singleton_class(mrb_state*, mrb_value); void mrb_include_module(mrb_state*, struct RClass*, struct RClass*); void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec); void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value); void mrb_undef_method(mrb_state*, struct RClass*, const char*); void mrb_undef_class_method(mrb_state*, struct RClass*, const char*); mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, int argc, mrb_value *argv); #define mrb_class_new_instance(mrb,argc,argv,c) mrb_obj_new(mrb,c,argc,argv) mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv); struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super); struct RClass * mrb_module_new(mrb_state *mrb); mrb_bool mrb_class_defined(mrb_state *mrb, const char *name); struct RClass * mrb_class_get(mrb_state *mrb, const char *name); struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name); mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj); mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method); mrb_bool mrb_obj_respond_to(struct RClass* c, mrb_sym mid); struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super); struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name); /* required arguments */ #define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18) /* optional arguments */ #define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13) /* mandatory and optinal 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))) /* block argument */ #define MRB_ARGS_BLOCK() ((mrb_aspec)1) /* accept any number of arguments */ #define MRB_ARGS_ANY() ARGS_REST() /* accept no arguments */ #define MRB_ARGS_NONE() ((mrb_aspec)0) /* compatibility macros; will be removed */ #define ARGS_REQ(n) MRB_ARGS_REQ(n) #define ARGS_OPT(n) MRB_ARGS_OPT(n) #define ARGS_REST() MRB_ARGS_REST() #define ARGS_POST(n) MRB_ARGS_POST() #define ARGS_KEY(n1,n2) MRB_ARGS_KEY(n1,n2) #define ARGS_BLOCK() MRB_ARGS_BLOCK() #define ARGS_ANY() MRB_ARGS_ANY() #define ARGS_NONE() MRB_ARGS_NONE() int mrb_get_args(mrb_state *mrb, const char *format, ...); mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, int,...); mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, int, mrb_value*); mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, int, mrb_value*, mrb_value); mrb_sym mrb_intern_cstr(mrb_state*,const char*); mrb_sym mrb_intern(mrb_state*,const char*,size_t); mrb_sym mrb_intern_str(mrb_state*,mrb_value); mrb_value mrb_check_intern_cstr(mrb_state*,const char*); mrb_value mrb_check_intern(mrb_state*,const char*,size_t); mrb_value mrb_check_intern_str(mrb_state*,mrb_value); const char *mrb_sym2name(mrb_state*,mrb_sym); const char *mrb_sym2name_len(mrb_state*,mrb_sym,size_t*); mrb_value mrb_sym2str(mrb_state*,mrb_sym); mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value); #define mrb_intern_lit(mrb, lit) mrb_intern(mrb, (lit), sizeof(lit) - 1) void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */ void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */ void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */ void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */ void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */ struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*); void mrb_free(mrb_state*, void*); mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len); mrb_value mrb_str_new_cstr(mrb_state*, const char*); mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len); mrb_state* mrb_open(void); mrb_state* mrb_open_allocf(mrb_allocf, void *ud); void mrb_close(mrb_state*); mrb_value mrb_top_self(mrb_state *); mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value); mrb_value mrb_context_run(mrb_state*, struct RProc*, mrb_value, unsigned int); void mrb_p(mrb_state*, mrb_value); mrb_int mrb_obj_id(mrb_value obj); mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name); mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value); mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value); mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2); mrb_value mrb_Integer(mrb_state *mrb, mrb_value val); mrb_value mrb_Float(mrb_state *mrb, mrb_value val); mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj); mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2); void mrb_garbage_collect(mrb_state*); void mrb_full_gc(mrb_state*); void mrb_incremental_gc(mrb_state *); int mrb_gc_arena_save(mrb_state*); void mrb_gc_arena_restore(mrb_state*,int); void mrb_gc_mark(mrb_state*,struct RBasic*); #define mrb_gc_mark_value(mrb,val) do {\ if (mrb_type(val) >= MRB_TT_HAS_BASIC) mrb_gc_mark((mrb), mrb_basic_ptr(val));\ } while (0) void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*); #define mrb_field_write_barrier_value(mrb, obj, val) do{\ if ((val.tt >= MRB_TT_HAS_BASIC)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val));\ } while (0) void mrb_write_barrier(mrb_state *, struct RBasic*); mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj); const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj); struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj); mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c); mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c); mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self); mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); /* need to include to use these macros */ #ifndef ISPRINT //#define ISASCII(c) isascii((int)(unsigned char)(c)) #define ISASCII(c) 1 #define ISPRINT(c) (ISASCII(c) && isprint((int)(unsigned char)(c))) #define ISSPACE(c) (ISASCII(c) && isspace((int)(unsigned char)(c))) #define ISUPPER(c) (ISASCII(c) && isupper((int)(unsigned char)(c))) #define ISLOWER(c) (ISASCII(c) && islower((int)(unsigned char)(c))) #define ISALNUM(c) (ISASCII(c) && isalnum((int)(unsigned char)(c))) #define ISALPHA(c) (ISASCII(c) && isalpha((int)(unsigned char)(c))) #define ISDIGIT(c) (ISASCII(c) && isdigit((int)(unsigned char)(c))) #define ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c))) #define TOUPPER(c) (ISASCII(c) ? toupper((int)(unsigned char)(c)) : (c)) #define TOLOWER(c) (ISASCII(c) ? tolower((int)(unsigned char)(c)) : (c)) #endif mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len); void mrb_exc_raise(mrb_state *mrb, mrb_value exc); void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg); void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...); void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...); void mrb_warn(mrb_state *mrb, const char *fmt, ...); void mrb_bug(mrb_state *mrb, const char *fmt, ...); void mrb_print_backtrace(mrb_state *mrb); 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_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_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv); void mrb_gc_protect(mrb_state *mrb, mrb_value obj); mrb_value mrb_to_int(mrb_state *mrb, mrb_value val); 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; void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2); const char *mrb_class_name(mrb_state *mrb, struct RClass* klass); void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val); mrb_value mrb_block_proc(void); mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id); mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid); mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c); /* memory pool implementation */ typedef struct mrb_pool mrb_pool; struct mrb_pool* mrb_pool_open(mrb_state*); void mrb_pool_close(struct mrb_pool*); void* mrb_pool_alloc(struct mrb_pool*, size_t); void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen); mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t); void* mrb_alloca(mrb_state *mrb, size_t); #ifdef MRB_DEBUG #include #define mrb_assert(p) assert(p) #else #define mrb_assert(p) ((void)0) #endif #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/000077500000000000000000000000001225163307400204475ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/include/mruby/array.h000066400000000000000000000037361225163307400217470ustar00rootroot00000000000000/* ** mruby/array.h - Array class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ARRAY_H #define MRUBY_ARRAY_H #if defined(__cplusplus) extern "C" { #endif typedef struct mrb_shared_array { int refcnt; mrb_value *ptr; mrb_int len; } 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) (RARRAY(a)->ptr) #define MRB_ARY_SHARED 256 void mrb_ary_modify(mrb_state*, struct RArray*); void mrb_ary_decref(mrb_state*, mrb_shared_array*); mrb_value mrb_ary_new_capa(mrb_state*, mrb_int); mrb_value mrb_ary_new(mrb_state *mrb); mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals); void mrb_ary_concat(mrb_state*, mrb_value, mrb_value); mrb_value mrb_ary_splat(mrb_state*, mrb_value); void mrb_ary_push(mrb_state*, mrb_value, mrb_value); mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary); mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self); mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n); void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val); mrb_int mrb_ary_len(mrb_state *mrb, mrb_value ary); void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b); mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self); mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item); mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr); mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset); mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self); mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self); mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_ARRAY_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/class.h000066400000000000000000000043611225163307400217310ustar00rootroot00000000000000/* ** mruby/class.h - Class class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_CLASS_H #define MRUBY_CLASS_H #if defined(__cplusplus) extern "C" { #endif 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 (v.value.i) 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; default: return mrb_obj_ptr(v)->c; } } #define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt) #define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff) struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym); struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym); void mrb_define_method_vm(mrb_state*, struct RClass*, mrb_sym, mrb_value); void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *); void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec); void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b); struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *); struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym); struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym); 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*); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_CLASS_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/compile.h000066400000000000000000000125621225163307400222560ustar00rootroot00000000000000/* ** mruby/compile.h - mruby parser ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILE_H #define MRUBY_COMPILE_H 1 #if defined(__cplusplus) extern "C" { #endif #include "mruby.h" #include 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; } mrbc_context; mrbc_context* mrbc_context_new(mrb_state *mrb); void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt); const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s); 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; #ifdef ENABLE_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, cmd_start; 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; int capture_errors; 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; jmp_buf jmp; }; struct mrb_parser_state* mrb_parser_new(mrb_state*); void mrb_parser_free(struct mrb_parser_state*); void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*); void mrb_parser_set_filename(struct mrb_parser_state*, char const*); char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx); /* utility functions */ #ifdef ENABLE_STDIO struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*); #endif struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*); struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*); struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*); /* program load functions */ #ifdef ENABLE_STDIO mrb_value mrb_load_file(mrb_state*,FILE*); #endif mrb_value mrb_load_string(mrb_state *mrb, const char *s); mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len); #ifdef ENABLE_STDIO mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt); #endif mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt); mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_COMPILE_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/data.h000066400000000000000000000034621225163307400215360ustar00rootroot00000000000000/* ** mruby/data.h - Data class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DATA_H #define MRUBY_DATA_H 1 #if defined(__cplusplus) extern "C" { #endif typedef struct mrb_data_type { const char *struct_name; 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; }; 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) void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*); 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) 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) #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_DATA_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/debug.h000066400000000000000000000027541225163307400217160ustar00rootroot00000000000000/* ** mruby/debug.h - mruby debug info ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DEBUG_H #define MRUBY_DEBUG_H #if defined(__cplusplus) extern "C" { #endif 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 *line_ptr; mrb_irep_debug_info_line *line_flat_map; uint16_t *line_ary; }; } 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 */ 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 */ int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc); 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 *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep); void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_DEBUG_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/dump.h000066400000000000000000000065711225163307400215760ustar00rootroot00000000000000/* ** mruby/dump.h - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DUMP_H #define MRUBY_DUMP_H #if defined(__cplusplus) extern "C" { #endif #include "mruby.h" #include "mruby/irep.h" #ifdef ENABLE_STDIO int mrb_dump_irep_binary(mrb_state*, mrb_irep*, int, FILE*); int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, int, FILE *f, const char *initname); mrb_irep *mrb_read_irep_file(mrb_state*, FILE*); mrb_value mrb_load_irep_file(mrb_state*,FILE*); mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*); #endif 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_IDENTIFIER "RITE" #define RITE_BINARY_FORMAT_VER "0002" #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_IDENTIFIER "IREP" #define RITE_SECTION_LINENO_IDENTIFIER "LINE" #define RITE_SECTION_DEBUG_IDENTIFIER "DBG\0" #define MRB_DUMP_DEFAULT_STR_LEN 128 // binary header struct rite_binary_header { uint8_t binary_identify[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_identify[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_binary_footer { RITE_SECTION_HEADER; }; static inline int uint8_to_bin(uint8_t s, uint8_t *bin) { *bin = s; return sizeof(uint8_t); } static inline int uint16_to_bin(uint16_t s, uint8_t *bin) { *bin++ = (s >> 8) & 0xff; *bin = s & 0xff; return sizeof(uint16_t); } static inline int 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 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 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]; } #if defined(__cplusplus) } /* extern "C" { */ #endif /* crc.c */ uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc); #endif /* MRUBY_DUMP_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/gc.h000066400000000000000000000006601225163307400212130ustar00rootroot00000000000000/* ** gc.h - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_GC_H #define MRUBY_GC_H #include "mruby.h" #include "mruby/value.h" typedef void (each_object_callback)(mrb_state *mrb, struct RBasic* obj, void *data); void mrb_objspace_each_objects(mrb_state *mrb, each_object_callback* callback, void *data); void mrb_free_context(mrb_state *mrb, struct mrb_context *c); #endif /* MRUBY_GC_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/hash.h000066400000000000000000000032571225163307400215520ustar00rootroot00000000000000/* ** mruby/hash.h - Hash class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_HASH_H #define MRUBY_HASH_H #if defined(__cplusplus) extern "C" { #endif 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_value mrb_hash_new_capa(mrb_state*, int); mrb_value mrb_hash_new(mrb_state *mrb); void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val); mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key); mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def); mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* 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) struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash); #define MRB_HASH_PROC_DEFAULT 256 #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*); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_HASH_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/irep.h000066400000000000000000000021711225163307400215600ustar00rootroot00000000000000/* ** mruby/irep.h - mrb_irep structure ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_IREP_H #define MRUBY_IREP_H #if defined(__cplusplus) extern "C" { #endif #include "mruby/compile.h" enum irep_pool_type { IREP_TT_STRING, IREP_TT_FIXNUM, IREP_TT_FLOAT, }; /* 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; /* 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_irep *mrb_add_irep(mrb_state *mrb); mrb_value mrb_load_irep(mrb_state*, const uint8_t*); 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*); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_IREP_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/khash.h000066400000000000000000000321031225163307400217150ustar00rootroot00000000000000/* ** mruby/khash.c - Hash for mruby ** ** See Copyright Notice in mruby.h */ #ifndef KHASH_H #define KHASH_H #if defined(__cplusplus) extern "C" { #endif #include "mruby.h" #include 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[8] = {0x02, 0x08, 0x20, 0x80}; static const uint8_t __m_del[8] = {0x01, 0x04, 0x10, 0x40}; static const uint8_t __m_either[8] = {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) /* 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; \ khint_t upper_bound; \ uint8_t *ed_flags; \ khkey_t *keys; \ khval_t *vals; \ khint_t mask; \ khint_t inc; \ mrb_state *mrb; \ } kh_##name##_t; \ void kh_alloc_##name(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(kh_##name##_t *h); \ void kh_clear_##name(kh_##name##_t *h); \ khint_t kh_get_##name(kh_##name##_t *h, khkey_t key); \ khint_t kh_put_##name(kh_##name##_t *h, khkey_t key); \ void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ void kh_del_##name(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(kh_##name##_t *h) \ { \ khint_t sz = h->n_buckets; \ int len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0); \ uint8_t *p = mrb_malloc(h->mrb, sizeof(uint8_t)*sz/4+len*sz); \ h->size = h->n_occupied = 0; \ h->upper_bound = UPPER_BOUND(sz); \ 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); \ h->mask = sz-1; \ h->inc = sz/2-1; \ } \ 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; \ h->mrb = mrb; \ kh_alloc_##name(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(kh_##name##_t *h) \ { \ if (h) { \ mrb_free(h->mrb, h->keys); \ mrb_free(h->mrb, h); \ } \ } \ void kh_clear_##name(kh_##name##_t *h) \ { \ 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(kh_##name##_t *h, khkey_t key) \ { \ khint_t k = __hash_func(h->mrb,key) & (h->mask); \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(h->mrb,h->keys[k], key)) return k; \ } \ k = (k+h->inc) & (h->mask); \ } \ return h->n_buckets; \ } \ void kh_resize_##name(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); \ { \ 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; \ h->n_buckets = new_n_buckets; \ kh_alloc_##name(h); \ /* relocate */ \ for (i=0 ; imrb, old_keys); \ } \ } \ khint_t kh_put_##name(kh_##name##_t *h, khkey_t key) \ { \ khint_t k; \ if (h->n_occupied >= h->upper_bound) { \ kh_resize_##name(h, h->n_buckets*2); \ } \ k = __hash_func(h->mrb,key) & (h->mask); \ while (!__ac_iseither(h->ed_flags, k)) { \ if (__hash_equal(h->mrb,h->keys[k], key)) break; \ k = (k+h->inc) & (h->mask); \ } \ if (__ac_isempty(h->ed_flags, k)) { \ /* put at empty */ \ h->keys[k] = key; \ h->ed_flags[k/4] &= ~__m_empty[k%4]; \ h->size++; \ h->n_occupied++; \ } else if (__ac_isdel(h->ed_flags, k)) { \ /* put at del */ \ h->keys[k] = key; \ h->ed_flags[k/4] &= ~__m_del[k%4]; \ h->size++; \ } \ return k; \ } \ void kh_del_##name(kh_##name##_t *h, khint_t 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(h2, kh_key(h, k)); \ 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, h) kh_destroy_##name(h) #define kh_clear(name, h) kh_clear_##name(h) #define kh_resize(name, h, s) kh_resize_##name(h, s) #define kh_put(name, h, k) kh_put_##name(h, k) #define kh_get(name, h, k) kh_get_##name(h, k) #define kh_del(name, h, k) kh_del_##name(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; #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* KHASH_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/numeric.h000066400000000000000000000016021225163307400222610ustar00rootroot00000000000000/* ** mruby/numeric.h - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_NUMERIC_H #define MRUBY_NUMERIC_H #if defined(__cplusplus) extern "C" { #endif #define POSFIXABLE(f) ((f) <= MRB_INT_MAX) #define NEGFIXABLE(f) ((f) >= MRB_INT_MIN) #define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val); mrb_value mrb_flo_to_str(mrb_state *mrb, mrb_value flo, int max_digit); mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base); 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); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_NUMERIC_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/proc.h000066400000000000000000000027441225163307400215720ustar00rootroot00000000000000/* ** mruby/proc.h - Proc class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PROC_H #define MRUBY_PROC_H #include "mruby/irep.h" #if defined(__cplusplus) extern "C" { #endif struct REnv { MRB_OBJECT_HEADER; mrb_value *stack; mrb_sym mid; int cioff; }; 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) & (1<<12)) #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_proc_new_cfunc(mrb_state*, mrb_func_t); struct RProc *mrb_closure_new(mrb_state*, mrb_irep*); 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); #include "mruby/khash.h" KHASH_DECLARE(mt, mrb_sym, struct RProc*, 1) #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_PROC_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/range.h000066400000000000000000000012721225163307400217160ustar00rootroot00000000000000/* ** mruby/range.h - Range class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RANGE_H #define MRUBY_RANGE_H #if defined(__cplusplus) extern "C" { #endif typedef struct mrb_range_edges { mrb_value beg; mrb_value end; } mrb_range_edges; struct RRange { MRB_OBJECT_HEADER; mrb_range_edges *edges; int excl; }; #define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v))) #define mrb_range_value(p) mrb_obj_value((void*)(p)) mrb_value mrb_range_new(mrb_state*, mrb_value, mrb_value, int); mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_RANGE_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/string.h000066400000000000000000000056701225163307400221360ustar00rootroot00000000000000/* ** mruby/string.h - String class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_STRING_H #define MRUBY_STRING_H #if defined(__cplusplus) extern "C" { #endif #define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) extern const char mrb_digitmap[]; struct RString { MRB_OBJECT_HEADER; mrb_int len; union { mrb_int capa; struct mrb_shared_string *shared; } aux; char *ptr; }; #define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING_PTR(s) (RSTRING(s)->ptr) #define RSTRING_LEN(s) (RSTRING(s)->len) #define RSTRING_CAPA(s) (RSTRING(s)->aux.capa) #define RSTRING_END(s) (RSTRING(s)->ptr + RSTRING(s)->len) void mrb_gc_free_str(mrb_state*, struct RString*); void mrb_str_modify(mrb_state*, struct RString*); mrb_value mrb_str_literal(mrb_state*, mrb_value); void mrb_str_concat(mrb_state*, mrb_value, mrb_value); mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value); mrb_value mrb_ptr_to_str(mrb_state *, void*); mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj); mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len); mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); mrb_value mrb_string_type(mrb_state *mrb, mrb_value str); mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str); mrb_value mrb_str_buf_new(mrb_state *mrb, mrb_int capa); mrb_value mrb_str_buf_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr); char *mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr); int mrb_str_offset(mrb_state *mrb, mrb_value str, int pos); mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self); mrb_value mrb_str_cat_cstr(mrb_state *, mrb_value, const char *); mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, int base, int badcheck); double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, int badcheck); mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str); mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str); mrb_value mrb_str_buf_append(mrb_state *mrb, mrb_value str, mrb_value str2); mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2); mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2); char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str); mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str); /* For backward compatibility */ static inline mrb_value mrb_str_cat2(mrb_state *mrb, mrb_value str, const char *ptr) { return mrb_str_cat_cstr(mrb, str, ptr); } #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_STRING_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/value.h000066400000000000000000000277301225163307400217450ustar00rootroot00000000000000/* ** mruby/value.h - mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VALUE_H #define MRUBY_VALUE_H #ifdef MRB_USE_FLOAT typedef float mrb_float; # define mrb_float_to_str(buf, i) sprintf(buf, "%.7e", i) # define str_to_mrb_float(buf) strtof(buf, NULL) #else typedef double mrb_float; # define mrb_float_to_str(buf, i) sprintf(buf, "%.16e", i) # define str_to_mrb_float(buf) strtod(buf, NULL) #endif #if defined(MRB_INT16) && defined(MRB_INT64) # error "You can't define MRB_INT16 and MRB_INT64 at the same time." #endif #if defined(MRB_INT64) # ifdef MRB_NAN_BOXING # error Cannot use NaN boxing when mrb_int is 64bit # else typedef int64_t mrb_int; # define MRB_INT_MIN INT64_MIN # define MRB_INT_MAX INT64_MAX # define PRIdMRB_INT PRId64 # define PRIiMRB_INT PRIi64 # define PRIoMRB_INT PRIo64 # define PRIxMRB_INT PRIx64 # define PRIXMRB_INT PRIX64 # endif #elif defined(MRB_INT16) typedef int16_t mrb_int; # define MRB_INT_MIN INT16_MIN # define MRB_INT_MAX INT16_MAX #else typedef int32_t mrb_int; # define MRB_INT_MIN INT32_MIN # define MRB_INT_MAX INT32_MAX # define PRIdMRB_INT PRId32 # define PRIiMRB_INT PRIi32 # define PRIoMRB_INT PRIo32 # define PRIxMRB_INT PRIx32 # define PRIXMRB_INT PRIX32 #endif typedef short mrb_sym; #ifdef _MSC_VER # ifndef __cplusplus # define inline __inline # endif # define snprintf _snprintf # if _MSC_VER < 1800 # include # define isnan _isnan # define isinf(n) (!_finite(n) && !_isnan(n)) # define strtoll _strtoi64 # define strtof (float)strtod # define PRId32 "I32d" # define PRIi32 "I32i" # define PRIo32 "I32o" # define PRIx32 "I32x" # define PRIX32 "I32X" # define PRId64 "I64d" # define PRIi64 "I64i" # define PRIo64 "I64o" # define PRIx64 "I64x" # define PRIX64 "I64X" # else # include # endif #else # include #endif typedef uint8_t mrb_bool; struct mrb_state; #if defined(MRB_NAN_BOXING) #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 enum mrb_vtype { MRB_TT_FALSE = 1, /* 1 */ MRB_TT_FREE, /* 2 */ MRB_TT_TRUE, /* 3 */ MRB_TT_FIXNUM, /* 4 */ MRB_TT_SYMBOL, /* 5 */ MRB_TT_UNDEF, /* 6 */ MRB_TT_FLOAT, /* 7 */ MRB_TT_CPTR, /* 8 */ MRB_TT_OBJECT, /* 9 */ MRB_TT_CLASS, /* 10 */ MRB_TT_MODULE, /* 11 */ MRB_TT_ICLASS, /* 12 */ MRB_TT_SCLASS, /* 13 */ MRB_TT_PROC, /* 14 */ MRB_TT_ARRAY, /* 15 */ MRB_TT_HASH, /* 16 */ MRB_TT_STRING, /* 17 */ MRB_TT_RANGE, /* 18 */ MRB_TT_EXCEPTION, /* 19 */ MRB_TT_FILE, /* 20 */ MRB_TT_ENV, /* 21 */ MRB_TT_DATA, /* 22 */ MRB_TT_FIBER, /* 23 */ MRB_TT_MAXDEFINE /* 24 */ }; #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 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; /* 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. */ #define mrb_tt(o) (((o).value.ttt & 0xfc000)>>14) #define mrb_mktt(tt) (0xfff00000|((tt)<<14)) #define mrb_type(o) ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT) #define mrb_ptr(o) ((void*)((((intptr_t)0x3fffffffffff)&((intptr_t)((o).value.p)))<<2)) #define mrb_float(o) (o).f #define MRB_SET_VALUE(o, tt, attr, v) do {\ (o).value.ttt = mrb_mktt(tt);\ 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*)((intptr_t)(o).value.p | (((intptr_t)(v))>>2)); break;\ }\ } while (0) static inline mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; if (f != f) { v.value.ttt = 0x7ff80000; v.value.i = 0; } else { v.f = f; } return v; } #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #else 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 */ }; #if defined(MRB_WORD_BOXING) #include #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_FIXNUM_SHIFT 1 #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 : (sizeof(mrb_int) * CHAR_BIT - MRB_FIXNUM_SHIFT); }; struct { unsigned int sym_flag : MRB_SPECIAL_SHIFT; int sym : (sizeof(mrb_sym) * CHAR_BIT); }; struct RBasic *bp; struct RFloat *fp; struct RCptr *vp; } value; unsigned long w; } mrb_value; #define mrb_ptr(o) (o).value.p #define mrb_float(o) (o).value.fp->f #define MRB_SET_VALUE(o, ttt, attr, v) do {\ (o).w = 0;\ (o).attr = (v);\ 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; break;\ case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; break;\ default: if ((o).value.bp) (o).value.bp->tt = ttt; break;\ }\ } while (0) mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f); mrb_value mrb_float_pool(struct mrb_state *mrb, mrb_float f); #else /* No MRB_xxx_BOXING */ #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_type(o) (o).tt #define mrb_ptr(o) (o).value.p #define mrb_float(o) (o).value.f #define MRB_SET_VALUE(o, ttt, attr, v) do {\ (o).tt = ttt;\ (o).attr = v;\ } while (0) static inline mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; (void) mrb; MRB_SET_VALUE(v, MRB_TT_FLOAT, value.f, f); return v; } #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #endif /* no boxing */ #endif #ifdef MRB_WORD_BOXING #define mrb_cptr(o) (o).value.vp->p #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 mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse) #else #define mrb_cptr(o) mrb_ptr(o) #define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM) #define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF) #define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !(o).value.i) #define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE) #endif /* no boxing */ #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym #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_test(o) mrb_bool(o) #define MRB_OBJECT_HEADER \ enum mrb_vtype tt:8;\ uint32_t color:3;\ uint32_t flags:21;\ struct RClass *c;\ struct RBasic *gcnext /* white: 011, black: 100, gray: 000 */ #define MRB_GC_GRAY 0 #define MRB_GC_WHITE_A 1 #define MRB_GC_WHITE_B (1 << 1) #define MRB_GC_BLACK (1 << 2) #define MRB_GC_WHITES (MRB_GC_WHITE_A | MRB_GC_WHITE_B) #define MRB_GC_COLOR_MASK 7 #define paint_gray(o) ((o)->color = MRB_GC_GRAY) #define paint_black(o) ((o)->color = MRB_GC_BLACK) #define paint_white(o) ((o)->color = MRB_GC_WHITES) #define paint_partial_white(s, o) ((o)->color = (s)->current_white_part) #define is_gray(o) ((o)->color == MRB_GC_GRAY) #define is_white(o) ((o)->color & MRB_GC_WHITES) #define is_black(o) ((o)->color & MRB_GC_BLACK) #define is_dead(s, o) (((o)->color & other_white_part(s) & MRB_GC_WHITES) || (o)->tt == MRB_TT_FREE) #define flip_white_part(s) ((s)->current_white_part = other_white_part(s)) #define other_white_part(s) ((s)->current_white_part ^ MRB_GC_WHITES) struct RBasic { MRB_OBJECT_HEADER; }; #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v))) /* obsolete macro mrb_basic; will be removed soon */ #define mrb_basic(v) mrb_basic_ptr(v) struct RObject { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v))) /* obsolete macro mrb_object; will be removed soon */ #define mrb_object(o) mrb_obj_ptr(o) #define mrb_immediate_p(x) (mrb_type(x) <= MRB_TT_CPTR) #define mrb_special_const_p(x) mrb_immediate_p(x) struct RFiber { MRB_OBJECT_HEADER; struct mrb_context *cxt; }; #ifdef MRB_WORD_BOXING struct RFloat { MRB_OBJECT_HEADER; mrb_float f; }; struct RCptr { MRB_OBJECT_HEADER; void *p; }; 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; } #endif /* MRB_WORD_BOXING */ static inline mrb_value mrb_fixnum_value(mrb_int i) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_FIXNUM, value.i, i); return v; } static inline mrb_value mrb_symbol_value(mrb_sym i) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_SYMBOL, value.sym, i); return v; } static inline mrb_value mrb_obj_value(void *p) { mrb_value v; struct RBasic *b = (struct RBasic*)p; MRB_SET_VALUE(v, b->tt, value.p, p); return v; } #ifdef MRB_WORD_BOXING mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p); #else static inline mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p) { mrb_value v; (void) mrb; MRB_SET_VALUE(v, MRB_TT_CPTR, value.p, p); return v; } #endif /* obsolete macros; will be removed */ #define MRB_TT_VOIDP MRB_TT_CPTR #define mrb_voidp_value(m,p) mrb_cptr_value((m),(p)) #define mrb_voidp(o) mrb_cptr(o) #define mrb_voidp_p(o) mrb_cptr_p(o) static inline mrb_value mrb_false_value(void) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_FALSE, value.i, 1); return v; } static inline mrb_value mrb_nil_value(void) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_FALSE, value.i, 0); return v; } static inline mrb_value mrb_true_value(void) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_TRUE, value.i, 1); return v; } static inline mrb_value mrb_undef_value(void) { mrb_value v; MRB_SET_VALUE(v, MRB_TT_UNDEF, value.i, 0); return v; } static inline mrb_value mrb_bool_value(mrb_bool boolean) { mrb_value v; MRB_SET_VALUE(v, boolean ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1); return v; } #endif /* MRUBY_OBJECT_H */ mruby-0.0.0~20131214+git882afdea/include/mruby/variable.h000066400000000000000000000061421225163307400224100ustar00rootroot00000000000000/* ** mruby/variable.h - mruby variables ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VARIABLE_H #define MRUBY_VARIABLE_H #if defined(__cplusplus) extern "C" { #endif 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_value mrb_const_get(mrb_state*, mrb_value, mrb_sym); void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value); mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym); void mrb_const_remove(mrb_state*, mrb_value, mrb_sym); mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym); void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym); void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym); void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v); mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym); mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym); void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src); int mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id); 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_gv_get(mrb_state *mrb, mrb_sym sym); void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val); void mrb_gv_remove(mrb_state *mrb, mrb_sym sym); mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value); mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*); mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer); 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_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym); void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v); void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v); mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym); mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym); /* 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*); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_VARIABLE_H */ mruby-0.0.0~20131214+git882afdea/minirake000077500000000000000000000314401225163307400174150ustar00rootroot00000000000000#!/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 File::stat(name.to_s).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(dir) path = [] Sys.split_all(dir).each do |p| path << p FileTask.define_task(File.join(path)) do |t| Sys.makedirs(t.name) 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."], ['--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 (default)."], ] # 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 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-0.0.0~20131214+git882afdea/mrbgems/000077500000000000000000000000001225163307400173225ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/default.gembox000066400000000000000000000026041225163307400221530ustar00rootroot00000000000000MRuby::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 extensional Enumerable module conf.gem :core => "mruby-enum-ext" # Use extensional String class conf.gem :core => "mruby-string-ext" # Use extensional Numeric class conf.gem :core => "mruby-numeric-ext" # Use extensional Array class conf.gem :core => "mruby-array-ext" # Use extensional Hash class conf.gem :core => "mruby-hash-ext" # Use extensional Range class conf.gem :core => "mruby-range-ext" # Use extensional Proc class conf.gem :core => "mruby-proc-ext" # Use extensional Symbol class conf.gem :core => "mruby-symbol-ext" # Use Random class conf.gem :core => "mruby-random" # Use extensional Object class conf.gem :core => "mruby-object-ext" # Use ObjectSpace class conf.gem :core => "mruby-objectspace" # Use Fiber class conf.gem :core => "mruby-fiber" # Use extended toplevel object (main) methods conf.gem :core => "mruby-toplevel-ext" # Generate mirb command conf.gem :core => "mruby-bin-mirb" # Generate mruby command conf.gem :core => "mruby-bin-mruby" end mruby-0.0.0~20131214+git882afdea/mrbgems/full-core.gembox000066400000000000000000000003611225163307400224150ustar00rootroot00000000000000MRuby::GemBox.new do |conf| conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" Dir.glob("#{root}/mrbgems/mruby-*") do |x| g = File.basename(x) conf.gem :core => g unless g =~ /^mruby-(print|sprintf)$/ end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/000077500000000000000000000000001225163307400223725ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/mrbgem.rake000066400000000000000000000001721225163307400245070ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/mrblib/000077500000000000000000000000001225163307400236415ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/mrblib/array.rb000066400000000000000000000114551225163307400253120ustar00rootroot00000000000000class Array ## # call-seq: # ary.uniq! -> 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 # def uniq! ary = self.dup result = [] while ary.size > 0 result << ary.shift ary.delete(result.last) end if result.size == self.size nil else self.replace(result) end end ## # call-seq: # ary.uniq -> new_ary # # Returns a new array by removing duplicate values in +self+. # # a = [ "a", "a", "b", "b", "c" ] # a.uniq #=> ["a", "b", "c"] # def uniq ary = self.dup ary.uniq! 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 end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/src/000077500000000000000000000000001225163307400231615ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/src/array.c000066400000000000000000000067111225163307400244500ustar00rootroot00000000000000#include "mruby.h" #include "mruby/value.h" #include "mruby/array.h" /* * call-seq: * Array.try_convert(obj) -> array or nil * * Try to convert obj into an array, using +to_ary+ method. * Returns 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 * */ static mrb_value mrb_ary_s_try_convert(mrb_state *mrb, mrb_value self) { mrb_value ary; mrb_get_args(mrb, "o", &ary); return mrb_check_array_type(mrb, ary); } /* * 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); } void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { struct RClass * a = mrb->array_class; mrb_define_class_method(mrb, a, "try_convert", mrb_ary_s_try_convert, MRB_ARGS_REQ(1)); 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)); } void mrb_mruby_array_ext_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/test/000077500000000000000000000000001225163307400233515ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-array-ext/test/array.rb000066400000000000000000000044351225163307400250220ustar00rootroot00000000000000## # Array(Ext) Test assert("Array::try_convert") do Array.try_convert([1]) == [1] and Array.try_convert("1").nil? end assert("Array#assoc") do s1 = [ "colors", "red", "blue", "green" ] s2 = [ "letters", "a", "b", "c" ] s3 = "foo" a = [ s1, s2, s3 ] a.assoc("letters") == [ "letters", "a", "b", "c" ] and a.assoc("foo").nil? end assert("Array#at") do a = [ "a", "b", "c", "d", "e" ] a.at(0) == "a" and a.at(-1) == "e" end assert("Array#rassoc") do a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] a.rassoc("two") == [2, "two"] and a.rassoc("four").nil? end assert("Array#uniq!") do a = [1, 2, 3, 1] a.uniq! a == [1, 2, 3] end assert("Array#uniq") do a = [1, 2, 3, 1] a.uniq == [1, 2, 3] && a == [1, 2, 3, 1] end assert("Array#-") do a = [1, 2, 3, 1] b = [1] c = 1 e1 = nil begin a - c rescue => e1 end (a - b) == [2, 3] and e1.class == TypeError and a == [1, 2, 3, 1] end assert("Array#|") do a = [1, 2, 3, 1] b = [1, 4] c = 1 e1 = nil begin a | c rescue => e1 end (a | b) == [1, 2, 3, 4] and e1.class == TypeError and a == [1, 2, 3, 1] end assert("Array#&") do a = [1, 2, 3, 1] b = [1, 4] c = 1 e1 = nil begin a & c rescue => e1 end (a & b) == [1] and e1.class == TypeError and a == [1, 2, 3, 1] end assert("Array#flatten") do [1, 2, "3", {4=>5}, :'6'] == [1, 2, "3", {4=>5}, :'6'].flatten and [1, 2, 3, 4, 5, 6] == [1, 2, [3, 4, 5], 6].flatten and [1, 2, 3, 4, 5, 6] == [1, 2, [3, [4, 5], 6]].flatten and [1, [2, [3, [4, [5, [6]]]]]] == [1, [2, [3, [4, [5, [6]]]]]].flatten(0) and [1, 2, [3, [4, [5, [6]]]]] == [1, [2, [3, [4, [5, [6]]]]]].flatten(1) and [1, 2, 3, [4, [5, [6]]]] == [1, [2, [3, [4, [5, [6]]]]]].flatten(2) and [1, 2, 3, 4, [5, [6]]] == [1, [2, [3, [4, [5, [6]]]]]].flatten(3) and [1, 2, 3, 4, 5, [6]] == [1, [2, [3, [4, [5, [6]]]]]].flatten(4) and [1, 2, 3, 4, 5, 6] == [1, [2, [3, [4, [5, [6]]]]]].flatten(5) end assert("Array#flatten!") do [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] a.compact == [1, "2", :t, false] && a == [1, nil, "2", nil, :t, false, nil] end assert("Array#compact!") do a = [1, nil, "2", nil, :t, false, nil] a.compact! a == [1, "2", :t, false] end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mirb/000077500000000000000000000000001225163307400221555ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mirb/mrbgem.rake000066400000000000000000000003461225163307400242750ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.linker.libraries << 'readline' if spec.cc.defines.include? "ENABLE_READLINE" spec.bins = %w(mirb) end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mirb/tools/000077500000000000000000000000001225163307400233155ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mirb/tools/mirb/000077500000000000000000000000001225163307400242465ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c000066400000000000000000000221431225163307400253450ustar00rootroot00000000000000/* ** 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 "mruby/array.h" #include #include #include #ifdef ENABLE_READLINE #include #include #include #endif #include #ifdef ENABLE_READLINE static const char *history_file_name = ".mirb_history"; char history_path[PATH_MAX]; #endif static void p(mrb_state *mrb, mrb_value obj, int prompt) { obj = mrb_funcall(mrb, obj, "inspect", 0); if (prompt) { if (!mrb->exc) { fputs(" => ", stdout); } else { obj = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); } } fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); putc('\n', stdout); } /* Guess if the user might want to enter more * or if he wants an evaluation of his code now */ mrb_bool is_code_block_open(struct mrb_parser_state *parser) { int 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, strlen(unexpected_end)) == 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: /* an expression was just started, */ /* we can't end it like this */ code_block_open = TRUE; 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; } void mrb_show_version(mrb_state *); void mrb_show_copyright(mrb_state *); struct _args { 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 = 1; break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "verbose") == 0) { args->verbose = 1; break; } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { mrb_close(mrb); } /* Print a short remark for the user */ static void print_hint(void) { printf("mirb - Embeddable Interactive Ruby Shell\n"); printf("\nThis is a very early version, please test and report errors.\n"); printf("Thanks :)\n\n"); } /* Print the command line prompt of the REPL */ void print_cmdline(int code_block_open) { if (code_block_open) { printf("* "); } else { printf("> "); } } void codedump_all(mrb_state*, struct RProc*); int main(int argc, char **argv) { char ruby_code[1024] = { 0 }; char last_code_line[1024] = { 0 }; #ifndef ENABLE_READLINE int last_char; int char_index; #else char *home = NULL; #endif mrbc_context *cxt; struct mrb_parser_state *parser; mrb_state *mrb; mrb_value result; struct _args args; int n; int code_block_open = FALSE; int ai; int first_command = 1; unsigned int nregs; /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fputs("Invalid mrb interpreter, exiting mirb\n", stderr); return EXIT_FAILURE; } mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0)); n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE) { cleanup(mrb, &args); usage(argv[0]); return n; } print_hint(); cxt = mrbc_context_new(mrb); cxt->capture_errors = 1; cxt->lineno = 1; mrbc_filename(mrb, cxt, "(mirb)"); if (args.verbose) cxt->dump_result = 1; ai = mrb_gc_arena_save(mrb); #ifdef ENABLE_READLINE using_history(); home = getenv("HOME"); #ifdef _WIN32 if (!home) home = getenv("USERPROFILE"); #endif if (home) { strcpy(history_path, home); strcat(history_path, "/"); strcat(history_path, history_file_name); read_history(history_path); } #endif while (TRUE) { #ifndef ENABLE_READLINE print_cmdline(code_block_open); char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; last_code_line[char_index++] = last_char; } if (last_char == EOF) { fputs("\n", stdout); break; } last_code_line[char_index] = '\0'; #else char* line = readline(code_block_open ? "* " : "> "); if (line == NULL) { printf("\n"); break; } strncpy(last_code_line, line, sizeof(last_code_line)-1); add_history(line); free(line); #endif if ((strcmp(last_code_line, "quit") == 0) || (strcmp(last_code_line, "exit") == 0)) { if (!code_block_open) { break; } else{ /* count the quit/exit commands as strings if in a quote block */ strcat(ruby_code, "\n"); strcat(ruby_code, last_code_line); } } else { if (code_block_open) { strcat(ruby_code, "\n"); strcat(ruby_code, last_code_line); } else { strcpy(ruby_code, last_code_line); } } /* parse code */ parser = mrb_parser_new(mrb); parser->s = ruby_code; parser->send = ruby_code + strlen(ruby_code); parser->lineno = cxt->lineno; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); 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 (args.verbose) { codedump_all(mrb, proc); } /* pass a proc for evaulation */ nregs = first_command ? 0: proc->body.irep->nregs; /* evaluate the bytecode */ result = mrb_context_run(mrb, proc, mrb_top_self(mrb), nregs); /* 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++; first_command = 0; } mrbc_context_free(mrb, cxt); mrb_close(mrb); #ifdef ENABLE_READLINE write_history(history_path); #endif return 0; } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mruby/000077500000000000000000000000001225163307400223625ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mruby/mrbgem.rake000066400000000000000000000002221225163307400244730ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.bins = %w(mruby) end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mruby/tools/000077500000000000000000000000001225163307400235225ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mruby/tools/mruby/000077500000000000000000000000001225163307400246605ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c000066400000000000000000000130501225163307400261610ustar00rootroot00000000000000#include "mruby.h" #include "mruby/proc.h" #include "mruby/array.h" #include "mruby/string.h" #include "mruby/compile.h" #include "mruby/dump.h" #include "mruby/variable.h" #include #include #include #ifndef ENABLE_STDIO static void p(mrb_state *mrb, mrb_value obj) { obj = mrb_funcall(mrb, obj, "inspect", 0); fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); putc('\n', stdout); } #else #define p(mrb,obj) mrb_p(mrb,obj) #endif void mrb_show_version(mrb_state *); void mrb_show_copyright(mrb_state *); 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 = 1; break; case 'c': args->check_syntax = 1; 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 = 1; break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "verbose") == 0) { args->verbose = 1; 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 0; } args->fname = 1; 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->cmdline && !args->fname) mrb_free(mrb, args->cmdline); if (args->argv) 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; 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++) { mrb_ary_push(mrb, ARGV, mrb_str_new(mrb, args.argv[i], strlen(args.argv[i]))); } mrb_define_global_const(mrb, "ARGV", ARGV); c = mrbc_context_new(mrb); if (args.verbose) c->dump_result = 1; if (args.check_syntax) c->no_exec = 1; if (args.mrbfile) { v = mrb_load_irep_file_cxt(mrb, args.rfp, c); } else { mrb_sym zero_sym = mrb_intern_lit(mrb, "$0"); if (args.rfp) { char *cmdline; cmdline = args.cmdline ? args.cmdline : "-"; mrbc_filename(mrb, c, cmdline); mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); v = mrb_load_file_cxt(mrb, args.rfp, c); } else { mrbc_filename(mrb, c, "-e"); mrb_gv_set(mrb, zero_sym, mrb_str_new(mrb, "-e", 2)); v = mrb_load_string_cxt(mrb, args.cmdline, c); } } 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-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/000077500000000000000000000000001225163307400222205ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/mrbgem.rake000066400000000000000000000001711225163307400243340ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/mrblib/000077500000000000000000000000001225163307400234675ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/mrblib/enum.rb000066400000000000000000000070651225163307400247700ustar00rootroot00000000000000## # 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, "expected Integer for 1st argument" unless n.kind_of? Integer raise ArgumentError, "attempt to drop negative size" if n < 0 ary = [] self.each {|e| n == 0 ? ary << e : n -= 1 } ary end ## # call-seq: # enum.drop_while {|arr| block } -> array # # 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. # # a = [1, 2, 3, 4, 5, 0] # a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0] def drop_while(&block) ary, state = [], false self.each do |e| state = true if !state and !block.call(e) ary << e 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, "expected Integer for 1st argument" unless n.kind_of? Integer raise ArgumentError, "attempt to take negative size" if n < 0 ary = [] self.each do |e| break if ary.size >= n ary << e end ary end ## # call-seq: # enum.take_while {|arr| block } -> array # # Passes elements to the block until the block returns +nil+ or +false+, # then stops iterating and returns an array of all prior elements. # # # a = [1, 2, 3, 4, 5, 0] # a.take_while {|i| i < 3 } #=> [1, 2] def take_while(&block) ary = [] self.each do |e| return ary unless block.call(e) ary << e end ary end ## # call-seq: # enum.each_cons(n) {...} -> nil # # Iterates the given block for each array of consecutive # elements. # # e.g.: # (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, "expected Integer for 1st argument" unless n.kind_of? Integer raise ArgumentError, "invalid size" if n <= 0 ary = [] self.each do |e| ary.shift if ary.size == n ary << e block.call(ary.dup) if ary.size == n end end ## # call-seq: # enum.each_slice(n) {...} -> nil # # Iterates the given block for each slice of elements. # # e.g.: # (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, "expected Integer for 1st argument" unless n.kind_of? Integer raise ArgumentError, "invalid slice size" if n <= 0 ary = [] self.each do |e| ary << e 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 # # 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) h = {} self.each do |e| key = block.call(e) h.key?(key) ? (h[key] << e) : (h[key] = [e]) end h end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/test/000077500000000000000000000000001225163307400231775ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-enum-ext/test/enum.rb000066400000000000000000000016211225163307400244700ustar00rootroot00000000000000## # 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 mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-eval/000077500000000000000000000000001225163307400214055ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-eval/mrbgem.rake000066400000000000000000000001651225163307400235240ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-eval') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-eval/src/000077500000000000000000000000001225163307400221745ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-eval/src/eval.c000066400000000000000000000006101225163307400232640ustar00rootroot00000000000000#include "mruby.h" #include "mruby/compile.h" static mrb_value f_eval(mrb_state *mrb, mrb_value self) { char *s; int len; mrb_get_args(mrb, "s", &s, &len); return mrb_load_nstring(mrb, s, len); } void mrb_mruby_eval_gem_init(mrb_state* mrb) { mrb_define_class_method(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_REQ(1)); } void mrb_mruby_eval_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-exit/000077500000000000000000000000001225163307400214275ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-exit/mrbgem.rake000066400000000000000000000001651225163307400235460ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-exit') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-exit/src/000077500000000000000000000000001225163307400222165ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-exit/src/mruby-exit.c000066400000000000000000000006151225163307400244710ustar00rootroot00000000000000#include #include "mruby.h" static mrb_value f_exit(mrb_state *mrb, mrb_value self) { mrb_int i = EXIT_SUCCESS; mrb_get_args(mrb, "|i", &i); exit(i); /* not reached */ return mrb_nil_value(); } void mrb_mruby_exit_gem_init(mrb_state* mrb) { mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit, MRB_ARGS_REQ(1)); } void mrb_mruby_exit_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/000077500000000000000000000000001225163307400215455ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/mrbgem.rake000066400000000000000000000001661225163307400236650ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-fiber') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/src/000077500000000000000000000000001225163307400223345ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/src/fiber.c000066400000000000000000000166051225163307400235770ustar00rootroot00000000000000#include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/proc.h" #define FIBER_STACK_INIT_SIZE 64 #define FIBER_CI_INIT_SIZE 8 /* * 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 (RuntimeError) * * 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 (RuntimeError) * */ static mrb_value fiber_init(mrb_state *mrb, mrb_value self) { static const struct mrb_context mrb_context_zero = { 0 }; struct RFiber *f = (struct RFiber*)mrb_ptr(self); struct mrb_context *c; struct RProc *p; mrb_callinfo *ci; mrb_value blk; 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_ARGUMENT_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 */ c->stbase = (mrb_value *)mrb_calloc(mrb, FIBER_STACK_INIT_SIZE, sizeof(mrb_value)); c->stend = c->stbase + FIBER_STACK_INIT_SIZE; c->stack = c->stbase; /* 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; /* 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 = (struct RFiber*)mrb_ptr(fib); if (!f->cxt) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized Fiber"); } return f->cxt; } static mrb_value fiber_result(mrb_state *mrb, mrb_value *a, 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 /* * 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) { struct mrb_context *c = fiber_check(mrb, self); mrb_value *a; int len; if (c->status == MRB_FIBER_RESUMED) { mrb_raise(mrb, E_RUNTIME_ERROR, "double resume"); } if (c->status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_RUNTIME_ERROR, "resuming dead fiber"); } mrb_get_args(mrb, "*", &a, &len); mrb->c->status = MRB_FIBER_RESUMED; if (c->status == MRB_FIBER_CREATED) { mrb_value *b = c->stack+1; mrb_value *e = b + len; while (bcibase->argc = len; c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); c->status = MRB_FIBER_RUNNING; mrb->c = c; MARK_CONTEXT_MODIFY(c); return c->ci->proc->env->stack[0]; } MARK_CONTEXT_MODIFY(c); c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); c->status = MRB_FIBER_RUNNING; mrb->c = c; return fiber_result(mrb, a, len); } /* * 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); } /* * 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 * value that this Fiber.yield expression evaluates to. */ static mrb_value fiber_yield(mrb_state *mrb, mrb_value self) { struct mrb_context *c = mrb->c; mrb_value *a; int len; if (!c->prev) { mrb_raise(mrb, E_ARGUMENT_ERROR, "can't yield from root fiber"); } mrb_get_args(mrb, "*", &a, &len); c->prev->status = MRB_FIBER_RUNNING; mrb->c = c->prev; c->prev = NULL; MARK_CONTEXT_MODIFY(mrb->c); return fiber_result(mrb, a, len); } /* * call-seq: * Fiber.current() -> fiber * * Returns the current fiber. You need to require 'fiber' * before using this method. 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, "alive?", fiber_alive_p, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY()); mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE()); } void mrb_mruby_fiber_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/test/000077500000000000000000000000001225163307400225245ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-fiber/test/fiber.rb000066400000000000000000000017151225163307400241440ustar00rootroot00000000000000assert('Fiber.new') { f = Fiber.new{} f.class == Fiber } assert('Fiber#resume') { f = Fiber.new{|x| x == 2} f.resume(2) } assert('Fiber#alive?') { f = Fiber.new{ Fiber.yield } f.resume r1 = f.alive? f.resume r2 = f.alive? r1 == true and r2 == false } assert('Fiber.yield') { f = Fiber.new{|x| Fiber.yield(x == 3)} f.resume(3) } assert('Fiber iteration') { 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 } a == [1,9,2,8,3,7] } assert('Fiber with splat in the block argument list') { Fiber.new{|*x|x}.resume(1) == [1] } assert('Fiber raises on resume when dead') { r1 = true begin f = Fiber.new{} f.resume r1 = f.alive? f.resume false rescue => e1 true end } assert('Yield raises when called on root fiber') { begin Fiber.yield false rescue => e1 true end } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/000077500000000000000000000000001225163307400221775ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/mrbgem.rake000066400000000000000000000001711225163307400243130ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-hash-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/mrblib/000077500000000000000000000000001225163307400234465ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/mrblib/hash.rb000066400000000000000000000005071225163307400247200ustar00rootroot00000000000000class Hash def merge!(other, &block) raise "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 end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/src/000077500000000000000000000000001225163307400227665ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/src/hash-ext.c000066400000000000000000000023621225163307400246560ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/hash.h" #include "mruby/khash.h" #include "mruby/string.h" #include "mruby/variable.h" /* * 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"] */ mrb_value mrb_hash_values_at(mrb_state *mrb, int argc, mrb_value *argv, mrb_value hash) { mrb_value result = mrb_ary_new_capa(mrb, argc); long i; for (i=0; ihash_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-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/test/000077500000000000000000000000001225163307400231565ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-hash-ext/test/hash.rb000066400000000000000000000013231225163307400244250ustar00rootroot00000000000000## # Hash(Ext) Test 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) end assert('Hash#values_at') do h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } assert_equal ["bovine", "feline"], h.values_at("cow", "cat") end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-math/000077500000000000000000000000001225163307400214075ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-math/mrbgem.rake000066400000000000000000000001651225163307400235260ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-math') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-math/src/000077500000000000000000000000001225163307400221765ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-math/src/math.c000066400000000000000000000335571225163307400233100ustar00rootroot00000000000000/* ** math.c - Math module ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include #define domain_error(msg) \ mrb_raise(mrb, E_RANGE_ERROR, "Numerical argument is out of domain - " #msg) /* math functions not provided by Microsoft Visual C++ 2012 or older */ #if defined _MSC_VER && _MSC_VER < 1800 #define MATH_TOLERANCE 1E-12 #define asinh(x) log(x + sqrt(pow(x,2.0) + 1)) #define acosh(x) log(x + sqrt(pow(x,2.0) - 1)) #define atanh(x) (log(1+x) - log(1-x))/2.0 #define cbrt(x) pow(x,1.0/3.0) /* 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. Returns -{PI/2} .. {PI/2}. */ static mrb_value math_asin(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); 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); 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); 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); x = atanh(x); return mrb_float_value(mrb, x); } /* EXPONENTIALS AND LOGARITHMS */ #if defined __CYGWIN__ # include # if CYGWIN_VERSION_DLL_MAJOR < 1005 # define nan(x) nan() # endif # define log(x) ((x) < 0.0 ? nan("") : log(x)) # define log10(x) ((x) < 0.0 ? nan("") : log10(x)) #endif /* * 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); x = log(x); if (argc == 2) { 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); 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); 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); 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"); #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-0.0.0~20131214+git882afdea/mrbgems/mruby-math/test/000077500000000000000000000000001225163307400223665ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-math/test/math.rb000066400000000000000000000050441225163307400236470ustar00rootroot00000000000000## # 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('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-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/000077500000000000000000000000001225163307400227165ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/mrbgem.rake000066400000000000000000000001741225163307400250350ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/src/000077500000000000000000000000001225163307400235055ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/src/numeric_ext.c000066400000000000000000000010631225163307400261730ustar00rootroot00000000000000#include #include "mruby.h" #include "mruby/numeric.h" static mrb_value mrb_int_chr(mrb_state *mrb, mrb_value x) { mrb_int chr; char c; chr = mrb_fixnum(x); if (chr >= (1 << CHAR_BIT)) { mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x); } c = (char)chr; return mrb_str_new(mrb, &c, 1); } void mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) { struct RClass *i = mrb_class_get(mrb, "Integer"); mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE()); } void mrb_mruby_numeric_ext_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/test/000077500000000000000000000000001225163307400236755ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-numeric-ext/test/numeric.rb000066400000000000000000000003021225163307400256570ustar00rootroot00000000000000## # 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) { 12345.chr } end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/000077500000000000000000000000001225163307400225225ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/mrbgem.rake000066400000000000000000000001731225163307400246400ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-object-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/src/000077500000000000000000000000001225163307400233115ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/src/object.c000066400000000000000000000043741225163307400247330ustar00rootroot00000000000000#include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" /* * call-seq: * nil.to_a -> [] * * Always returns an empty array. */ static mrb_value nil_to_a(mrb_state *mrb, mrb_value obj) { return mrb_ary_new(mrb); } /* * call-seq: * nil.to_f -> 0.0 * * Always returns zero. */ static mrb_value nil_to_f(mrb_state *mrb, mrb_value obj) { return mrb_float_value(mrb, 0.0); } /* * call-seq: * nil.to_i -> 0 * * Always returns zero. */ static mrb_value nil_to_i(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(0); } /* * call-seq: * obj.instance_exec(arg...) {|var...| block } -> obj * * Executes the given block within the context of the receiver * (_obj_). In order to set the context, the variable +self+ is set * to _obj_ while the code is executing, giving the code access to * _obj_'s instance variables. Arguments are passed as block parameters. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_exec(5) {|x| @secret+x } #=> 104 */ mrb_value mrb_yield_internal(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv, mrb_value self, struct RClass *c); static mrb_value mrb_obj_instance_exec(mrb_state *mrb, mrb_value self) { mrb_value *argv; 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_internal(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-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/test/000077500000000000000000000000001225163307400235015ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/test/nil.rb000066400000000000000000000002631225163307400246110ustar00rootroot00000000000000assert('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-0.0.0~20131214+git882afdea/mrbgems/mruby-object-ext/test/object.rb000066400000000000000000000003041225163307400252710ustar00rootroot00000000000000assert('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 mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/000077500000000000000000000000001225163307400227405ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/mrbgem.rake000066400000000000000000000001741225163307400250570ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-objectspace') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/src/000077500000000000000000000000001225163307400235275ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/src/mruby_objectspace.c000066400000000000000000000055601225163307400274010ustar00rootroot00000000000000#include #include #include #include struct os_count_struct { size_t total; size_t freed; size_t counts[MRB_TT_MAXDEFINE+1]; }; 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); if (is_dead(mrb, obj)) { obj_count->freed++; } else { obj_count->counts[obj->tt]++; obj_count->total++; } } /* * call-seq: * ObjectSpace.count_objects([result_hash]) -> hash * * Counts objects for each type. * * It returns a hash, such as: * { * :TOTAL=>10000, * :FREE=>3011, * :MRB_TT_OBJECT=>6, * :MRB_TT_CLASS=>404, * # ... * } * * If the optional argument +result_hash+ is given, * it is overwritten and returned. This is intended to avoid probe effect. * */ mrb_value os_count_objects(mrb_state *mrb, mrb_value self) { struct os_count_struct obj_count; size_t 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); } for (i = 0; i <= MRB_TT_MAXDEFINE; i++) { obj_count.counts[i] = 0; } obj_count.total = 0; obj_count.freed = 0; mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count); mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_cstr(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total)); mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_cstr(mrb, "FREE")), mrb_fixnum_value(obj_count.freed)); for (i = 0; i < MRB_TT_MAXDEFINE; i++) { mrb_value type; switch (i) { #define COUNT_TYPE(t) case (t): type = mrb_symbol_value(mrb_intern_cstr(mrb, #t)); break; COUNT_TYPE(MRB_TT_FALSE); COUNT_TYPE(MRB_TT_FREE); COUNT_TYPE(MRB_TT_TRUE); COUNT_TYPE(MRB_TT_FIXNUM); COUNT_TYPE(MRB_TT_SYMBOL); COUNT_TYPE(MRB_TT_UNDEF); COUNT_TYPE(MRB_TT_FLOAT); COUNT_TYPE(MRB_TT_CPTR); COUNT_TYPE(MRB_TT_OBJECT); COUNT_TYPE(MRB_TT_CLASS); COUNT_TYPE(MRB_TT_MODULE); COUNT_TYPE(MRB_TT_ICLASS); COUNT_TYPE(MRB_TT_SCLASS); COUNT_TYPE(MRB_TT_PROC); COUNT_TYPE(MRB_TT_ARRAY); COUNT_TYPE(MRB_TT_HASH); COUNT_TYPE(MRB_TT_STRING); COUNT_TYPE(MRB_TT_RANGE); COUNT_TYPE(MRB_TT_EXCEPTION); COUNT_TYPE(MRB_TT_FILE); COUNT_TYPE(MRB_TT_ENV); COUNT_TYPE(MRB_TT_DATA); #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; } 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_ANY()); } void mrb_mruby_objectspace_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/test/000077500000000000000000000000001225163307400237175ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-objectspace/test/objectspace.rb000066400000000000000000000017251225163307400265330ustar00rootroot00000000000000assert('ObjectSpace.count_objects') do h = {} 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)) 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 = {:MRB_TT_FOO=>1000} h = ObjectSpace.count_objects(h0) assert_false(h0.has_key?(:MRB_TT_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[:MRB_TT_HASH], h_before[:MRB_TT_HASH] + 1000) assert_equal(h_after[:MRB_TT_HASH], h_before[:MRB_TT_HASH]) end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-print/000077500000000000000000000000001225163307400216125ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-print/mrbgem.rake000066400000000000000000000001661225163307400237320ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-print') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-print/mrblib/000077500000000000000000000000001225163307400230615ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-print/mrblib/print.rb000066400000000000000000000021241225163307400245410ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/mrbgems/mruby-print/src/000077500000000000000000000000001225163307400224015ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-print/src/print.c000066400000000000000000000013111225163307400236750ustar00rootroot00000000000000#include "mruby.h" #include "mruby/string.h" #include static void printstr(mrb_state *mrb, mrb_value obj) { struct RString *str; char *s; int len; if (mrb_string_p(obj)) { str = mrb_str_ptr(obj); s = str->ptr; len = str->len; fwrite(s, len, 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-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/000077500000000000000000000000001225163307400222175ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/mrbgem.rake000066400000000000000000000001711225163307400243330ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-proc-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/mrblib/000077500000000000000000000000001225163307400234665ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/mrblib/proc.rb000066400000000000000000000013731225163307400247620ustar00rootroot00000000000000class Proc def ===(*args) call(*args) end def yield(*args) call(*args) end def to_proc self end def curry(arity=self.arity) abs = lambda {|a| a < 0 ? -a - 1 : a} arity = abs[arity] if 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=[]| proc 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-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/src/000077500000000000000000000000001225163307400230065ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/src/proc.c000066400000000000000000000047011225163307400241170ustar00rootroot00000000000000#include "mruby.h" #include "mruby/proc.h" #include "mruby/array.h" #include "mruby/string.h" static mrb_value mrb_proc_lambda(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); return mrb_bool_value(MRB_PROC_STRICT_P(p)); } static mrb_value mrb_proc_source_location(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); if (MRB_PROC_CFUNC_P(p)) { return mrb_nil_value(); } else { mrb_irep *irep = p->body.irep; mrb_value filename = mrb_nil_value(); mrb_value lines = mrb_nil_value(); if (irep->filename) filename = mrb_str_new_cstr(mrb, irep->filename); if (irep->lines) lines = mrb_fixnum_value(*irep->lines); return mrb_assoc_new(mrb, filename, lines); } } 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_cstr(mrb, "#body.irep; mrb_str_cat_cstr(mrb, str, "@"); if (irep->filename) { mrb_str_cat_cstr(mrb, str, irep->filename); } else { mrb_str_cat_cstr(mrb, str, "-"); } mrb_str_cat_cstr(mrb, str, ":"); if (irep->lines) { mrb_str_append(mrb, str, mrb_fixnum_value(*irep->lines)); } else { mrb_str_cat_cstr(mrb, str, "-"); } } if (MRB_PROC_STRICT_P(p)) { mrb_str_cat_cstr(mrb, str, " (lambda)"); } mrb_str_cat_cstr(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; } 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_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-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/test/000077500000000000000000000000001225163307400231765ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-proc-ext/test/proc.rb000066400000000000000000000016661225163307400244770ustar00rootroot00000000000000## # Proc(Ext) Test 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) } 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 mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/000077500000000000000000000000001225163307400217365ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/mrbgem.rake000066400000000000000000000001671225163307400240570ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-random') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/src/000077500000000000000000000000001225163307400225255ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/src/mt19937ar.c000066400000000000000000000127021225163307400242530ustar00rootroot00000000000000/* ** 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 */ 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 */ 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 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_dbl = t->gen_int*(1.0/4294967295.0); return t->gen_dbl; /* divided by 2^32-1 */ } /* 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 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 */ mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/src/mt19937ar.h000066400000000000000000000023701225163307400242600ustar00rootroot00000000000000/* ** 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 gen_int; double gen_dbl; }; } 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-0.0.0~20131214+git882afdea/mrbgems/mruby-random/src/random.c000066400000000000000000000163731225163307400241630ustar00rootroot00000000000000/* ** random.c - Random module ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/variable.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/array.h" #include "mt19937ar.h" #include #define GLOBAL_RAND_SEED_KEY "$mrb_g_rand_seed" #define GLOBAL_RAND_SEED_KEY_CSTR_LEN 16 #define INSTANCE_RAND_SEED_KEY "$mrb_i_rand_seed" #define INSTANCE_RAND_SEED_KEY_CSTR_LEN 16 #define MT_STATE_KEY "$mrb_i_mt_state" static const struct mrb_data_type mt_state_type = { MT_STATE_KEY, mrb_free, }; static void mt_g_srand(unsigned long seed) { init_genrand(seed); } static unsigned long mt_g_rand() { return genrand_int32(); } static double mt_g_rand_real() { return genrand_real1(); } static mrb_value mrb_random_mt_g_srand(mrb_state *mrb, mrb_value seed) { if (mrb_nil_p(seed)) { seed = mrb_fixnum_value(time(NULL) + mt_g_rand()); if (mrb_fixnum(seed) < 0) { seed = mrb_fixnum_value( 0 - mrb_fixnum(seed)); } } mt_g_srand((unsigned) mrb_fixnum(seed)); return seed; } static mrb_value mrb_random_mt_g_rand(mrb_state *mrb, mrb_value max) { mrb_value value; if (mrb_fixnum(max) == 0) { value = mrb_float_value(mrb, mt_g_rand_real()); } else { value = mrb_fixnum_value(mt_g_rand() % mrb_fixnum(max)); } return value; } 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(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_fixnum_value(0); mrb_get_args(mrb, "|o", &arg); if (!mrb_nil_p(arg)) { if (!mrb_fixnum_p(arg)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type"); } arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int"); if (mrb_fixnum(arg) < 0) { arg = mrb_fixnum_value(0 - mrb_fixnum(arg)); } } return arg; } static void mrb_random_g_rand_seed(mrb_state *mrb) { mrb_value seed; seed = mrb_gv_get(mrb, mrb_intern(mrb, GLOBAL_RAND_SEED_KEY, GLOBAL_RAND_SEED_KEY_CSTR_LEN)); if (mrb_nil_p(seed)) { mrb_random_mt_g_srand(mrb, mrb_nil_value()); } } static mrb_value mrb_random_g_rand(mrb_state *mrb, mrb_value self) { mrb_value max; max = get_opt(mrb); mrb_random_g_rand_seed(mrb); return mrb_random_mt_g_rand(mrb, max); } static mrb_value mrb_random_g_srand(mrb_state *mrb, mrb_value self) { mrb_value seed; mrb_value old_seed; seed = get_opt(mrb); seed = mrb_random_mt_g_srand(mrb, seed); old_seed = mrb_gv_get(mrb, mrb_intern(mrb, GLOBAL_RAND_SEED_KEY, GLOBAL_RAND_SEED_KEY_CSTR_LEN)); mrb_gv_set(mrb, mrb_intern(mrb, GLOBAL_RAND_SEED_KEY, GLOBAL_RAND_SEED_KEY_CSTR_LEN), seed); return old_seed; } static mrb_value mrb_random_init(mrb_state *mrb, mrb_value self) { mrb_value seed; mt_state *t; DATA_TYPE(self) = &mt_state_type; DATA_PTR(self) = NULL; /* avoid memory leaks */ t = (mt_state*)DATA_PTR(self); if (t) { mrb_free(mrb, t); } 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); mrb_iv_set(mrb, self, mrb_intern(mrb, INSTANCE_RAND_SEED_KEY, INSTANCE_RAND_SEED_KEY_CSTR_LEN), seed); DATA_PTR(self) = t; return self; } static void mrb_random_rand_seed(mrb_state *mrb, mrb_value self) { mrb_value seed; mt_state *t = DATA_PTR(self); seed = mrb_iv_get(mrb, self, mrb_intern(mrb, INSTANCE_RAND_SEED_KEY, INSTANCE_RAND_SEED_KEY_CSTR_LEN)); if (mrb_nil_p(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_PTR(self); max = get_opt(mrb); mrb_random_rand_seed(mrb, self); 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_PTR(self); seed = get_opt(mrb); seed = mrb_random_mt_srand(mrb, t, seed); old_seed = mrb_iv_get(mrb, self, mrb_intern(mrb, INSTANCE_RAND_SEED_KEY, INSTANCE_RAND_SEED_KEY_CSTR_LEN)); mrb_iv_set(mrb, self, mrb_intern(mrb, INSTANCE_RAND_SEED_KEY, INSTANCE_RAND_SEED_KEY_CSTR_LEN), 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; mrb_value random = mrb_nil_value(); if (RARRAY_LEN(ary) > 1) { mrb_get_args(mrb, "|o", &random); if (mrb_nil_p(random)) { mrb_random_g_rand_seed(mrb); } else { mrb_data_check_type(mrb, random, &mt_state_type); 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; if (mrb_nil_p(random)) { j = mrb_fixnum(mrb_random_mt_g_rand(mrb, mrb_fixnum_value(RARRAY_LEN(ary)))); } else { j = mrb_fixnum(mrb_random_mt_rand(mrb, DATA_PTR(random), mrb_fixnum_value(RARRAY_LEN(ary)))); } tmp = RARRAY_PTR(ary)[i]; RARRAY_PTR(ary)[i] = RARRAY_PTR(ary)[j]; RARRAY_PTR(ary)[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; } void mrb_mruby_random_gem_init(mrb_state *mrb) { struct RClass *random; struct RClass *array = mrb->array_class; mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); random = mrb_define_class(mrb, "Random", mrb->object_class); MRB_SET_INSTANCE_TT(random, MRB_TT_DATA); mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); } void mrb_mruby_random_gem_final(mrb_state *mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/src/random.h000066400000000000000000000002441225163307400241560ustar00rootroot00000000000000/* // random.h - Random module // // See Copyright Notice in mruby.h */ #ifndef RANDOM_H #define RANDOM_H void mrb_mruby_random_gem_init(mrb_state *mrb); #endif mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/test/000077500000000000000000000000001225163307400227155ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-random/test/random.rb000066400000000000000000000033251225163307400245250ustar00rootroot00000000000000## # 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 endmruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/000077500000000000000000000000001225163307400223505ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/mrbgem.rake000066400000000000000000000001721225163307400244650ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-range-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/src/000077500000000000000000000000001225163307400231375ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/src/range.c000066400000000000000000000063431225163307400244050ustar00rootroot00000000000000#include "mruby.h" #include "mruby/range.h" static mrb_bool r_le(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ /* output :a < b => -1, a = b => 0, a > b => +1 */ if (mrb_type(r) == MRB_TT_FIXNUM) { 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 */ if (mrb_type(r) == MRB_TT_FIXNUM) { if (mrb_fixnum(r) == -1) return TRUE; } return FALSE; } /* * 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_value num; mrb_value array; struct RRange *r = mrb_range_ptr(range); if (mrb_get_args(mrb, "|o", &num) == 0) { return r->edges->beg; } array = mrb_funcall(mrb, range, "to_a", 0); return mrb_funcall(mrb, array, "first", 1, mrb_to_int(mrb, 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)); } 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)); } void mrb_mruby_range_ext_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/test/000077500000000000000000000000001225163307400233275ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-range-ext/test/range.rb000066400000000000000000000007211225163307400247500ustar00rootroot00000000000000## # 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 mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/000077500000000000000000000000001225163307400221435ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/mrbgem.rake000066400000000000000000000001701225163307400242560ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-sprintf') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/src/000077500000000000000000000000001225163307400227325ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/src/kernel.c000066400000000000000000000011701225163307400243550ustar00rootroot00000000000000/* ** kernel.c - Kernel module suppliment ** ** See Copyright Notice in mruby.h */ #include "mruby.h" mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */ void mrb_mruby_sprintf_gem_init(mrb_state* mrb) { struct RClass *krn; if (mrb->kernel_module == NULL) { mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* Might be PARANOID. */ } krn = mrb->kernel_module; mrb_define_method(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY()); mrb_define_method(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY()); } void mrb_mruby_sprintf_gem_final(mrb_state* mrb) { /* nothing to do. */ } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/src/sprintf.c000066400000000000000000001007061225163307400245670ustar00rootroot00000000000000/* ** sprintf.c - Kernel.#sprintf ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include #include #include #include "mruby/string.h" #include "mruby/hash.h" #include "mruby/numeric.h" #include #include #ifdef HAVE_IEEEFP_H #include #endif #define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ #define BITSPERDIG (sizeof(mrb_int)*CHAR_BIT) #define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n))) 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[64], *b = buf + sizeof buf; mrb_int num = mrb_fixnum(x); unsigned long val = (unsigned long)num; char d; if (base != 2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); } if (val >= (1 << 10)) val &= 0x3ff; if (val == 0) { return mrb_str_new(mrb, "0", 1); } *--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 { \ t = p++; \ n = 0; \ GETNUM(n, val); \ if (*p == '$') { \ tmp = GETPOSARG(n); \ } \ else { \ tmp = GETARG(); \ p = t; \ } \ num = mrb_fixnum(tmp); \ } 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) { 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 tmp; 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--; 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; unsigned int c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1 ) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } c = RSTRING_PTR(tmp)[0]; n = 1; } else { c = mrb_fixnum(val); n = 1; } if (n <= 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } if (!(flags & FWIDTH)) { CHECK(n); buf[blen] = c; blen += n; } else if ((flags & FMINUS)) { CHECK(n); buf[blen] = c; blen += n; FILL(' ', width-1); } else { FILL(' ', width-1); CHECK(n); buf[blen] = c; 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); RSTRING_LEN(result) = 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 fbuf[32], nbuf[64], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0, org_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: if (FIXABLE(mrb_float(val))) { val = mrb_fixnum_value((mrb_int)mrb_float(val)); goto bin_retry; } 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) { org_v = v; 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); } v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, 0/*Qfalse*/)); } if (sign) { char c = *p; if (c == 'i') c = 'd'; /* %d and %i are identical */ if (base == 2) c = 'd'; if (v < 0) { v = -v; sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(nbuf, sizeof(nbuf), fbuf, v); s = nbuf; } else { char c = *p; if (c == 'X') c = 'x'; if (base == 2) c = 'd'; s = nbuf; if (v < 0) { dots = 1; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(++s, sizeof(nbuf) - 1, fbuf, v); 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 || (base == 2 && org_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 (isnan(fval) || isinf(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-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/test/000077500000000000000000000000001225163307400231225ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-sprintf/test/sprintf.rb000066400000000000000000000000501225163307400251270ustar00rootroot00000000000000## # Kernel#sprintf Kernel#format Test mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/000077500000000000000000000000001225163307400225625ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/mrbgem.rake000066400000000000000000000001731225163307400247000ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-string-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/mrblib/000077500000000000000000000000001225163307400240315ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/mrblib/string.rb000066400000000000000000000021121225163307400256600ustar00rootroot00000000000000class String 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 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 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 def lstrip! s = self.lstrip (s == self) ? nil : self.replace(s) end def rstrip! s = self.rstrip (s == self) ? nil : self.replace(s) end 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.downcase end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/src/000077500000000000000000000000001225163307400233515ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/src/string.c000066400000000000000000000114351225163307400250270ustar00rootroot00000000000000#include "mruby.h" #include "mruby/string.h" #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]); } /* * 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 = s->ptr; pend = s->ptr + s->len; 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; 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; 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(); } 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, "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()); } void mrb_mruby_string_ext_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/test/000077500000000000000000000000001225163307400235415ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-string-ext/test/string.rb000066400000000000000000000053721225163307400254030ustar00rootroot00000000000000## # String(Ext) Test 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#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 " s.strip "".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") 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 mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/000077500000000000000000000000001225163307400220025ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/mrbgem.rake000066400000000000000000000001671225163307400241230ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/mrblib/000077500000000000000000000000001225163307400232515ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/mrblib/struct.rb000066400000000000000000000017141225163307400251250ustar00rootroot00000000000000## # 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 end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/src/000077500000000000000000000000001225163307400225715ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/src/struct.c000066400000000000000000000560371225163307400242740ustar00rootroot00000000000000/* ** struct.c - Struct class ** ** See Copyright Notice in mruby.h */ #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/string.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/variable.h" #define RSTRUCT_ARY(st) mrb_ary_ptr(st) #define RSTRUCT_LEN(st) RSTRUCT_ARY(st)->len #define RSTRUCT_PTR(st) RSTRUCT_ARY(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); } } mrb_value mrb_struct_iv_get(mrb_state *mrb, mrb_value c, const char *name) { return struct_ivar_get(mrb, c, mrb_intern_cstr(mrb, name)); } mrb_value mrb_struct_s_members(mrb_state *mrb, mrb_value klass) { mrb_value members = struct_ivar_get(mrb, 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; } mrb_value mrb_struct_members(mrb_state *mrb, mrb_value s) { mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s))); if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "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; mrb_value *p, *pend; members = mrb_struct_s_members(mrb, klass); ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members)); p = RARRAY_PTR(members); pend = p + RARRAY_LEN(members); while (p < pend) { mrb_ary_push(mrb, ary, *p); p++; } 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_m(mrb_state *mrb, mrb_value obj) { return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } mrb_value mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) { mrb_value members, slot, *ptr, *ptr_members; mrb_int i, len; ptr = RSTRUCT_PTR(obj); members = mrb_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 mrb_value (*const ref_func[])(mrb_state*, mrb_value) = { 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, }; mrb_sym mrb_id_attrset(mrb_state *mrb, mrb_sym id) { const char *name; char *buf; size_t len; mrb_sym mid; name = mrb_sym2name_len(mrb, id, &len); buf = (char *)mrb_malloc(mrb, len+2); memcpy(buf, name, 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; size_t i, len; mrb_sym mid; mrb_value members, slot, *ptr, *ptr_members; /* get base id */ name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &len); mid = mrb_intern(mrb, name, len-1); /* omit last "=" */ members = mrb_struct_members(mrb, obj); ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); ptr = RSTRUCT_PTR(obj); for (i=0; itLAST_TOKEN) #define is_local_id(id) (is_notop_id(id))//&&((id)&ID_SCOPE_MASK)==ID_LOCAL) int mrb_is_local_id(mrb_sym id) { return is_local_id(id); } #define is_const_id(id) (is_notop_id(id))//&&((id)&ID_SCOPE_MASK)==ID_CONST) int mrb_is_const_id(mrb_sym id) { return is_const_id(id); } static mrb_value make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass) { mrb_value nstr, *ptr_members; mrb_sym id; mrb_int i, len; struct RClass *c; if (mrb_nil_p(name)) { c = mrb_class_new(mrb, klass); } else { /* old style: should we warn? */ name = mrb_str_to_str(mrb, name); id = mrb_obj_to_sym(mrb, name); if (!mrb_is_const_id(id)) { mrb_name_error(mrb, id, "identifier %S needs to be constant", name); } if (mrb_const_defined_at(mrb, klass, id)) { mrb_warn(mrb, "redefining constant Struct::%S", name); //?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); } c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass); } MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY); nstr = mrb_obj_value(c); mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members); mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE()); //RSTRUCT(nstr)->basic.c->super = c->c; ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); for (i=0; i< len; i++) { mrb_sym id = mrb_symbol(ptr_members[i]); if (mrb_is_local_id(id) || mrb_is_const_id(id)) { if (i < N_REF_FUNC) { mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE()); } else { mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE()); } mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1)); } } return nstr; } mrb_value mrb_struct_define(mrb_state *mrb, const char *name, ...) { va_list ar; mrb_value nm, ary; char *mem; if (!name) nm = mrb_nil_value(); else nm = mrb_str_new_cstr(mrb, name); ary = mrb_ary_new(mrb); va_start(ar, name); while ((mem = va_arg(ar, char*)) != 0) { mrb_sym slot = mrb_intern_cstr(mrb, mem); mrb_ary_push(mrb, ary, mrb_symbol_value(slot)); } va_end(ar); return make_struct(mrb, nm, ary, struct_class(mrb)); } /* 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; int argcnt; mrb_int i; mrb_value b, st; mrb_sym id; mrb_value *argv; 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"); } members = mrb_struct_members(mrb, s); ptr_members = RARRAY_PTR(members); ptr = RSTRUCT_PTR(s); len = RSTRUCT_LEN(s); for (i=0; i 0) { mrb_str_cat2(mrb, str, ", "); } else if (cn) { mrb_str_cat2(mrb, str, " "); } slot = ptr_members[i]; id = mrb_symbol(slot); if (mrb_is_local_id(id) || mrb_is_const_id(id)) { const char *name; size_t len; name = mrb_sym2name_len(mrb, id, &len); mrb_str_append(mrb, str, mrb_str_new(mrb, name, len)); } else { mrb_str_append(mrb, str, mrb_inspect(mrb, slot)); } mrb_str_cat2(mrb, str, "="); mrb_str_append(mrb, str, mrb_inspect(mrb, ptr[i])); } mrb_str_cat2(mrb, str, ">"); return str; } /* * call-seq: * struct.to_s -> string * struct.inspect -> string * * Describe the contents of this struct in a string. */ static mrb_value mrb_struct_inspect(mrb_state *mrb, mrb_value s) { return inspect_struct(mrb, s, 0); } /* 15.2.18.4.9 */ /* :nodoc: */ mrb_value mrb_struct_init_copy(mrb_state *mrb, mrb_value copy) { mrb_value s; int i, len; mrb_get_args(mrb, "o", &s); if (mrb_obj_equal(mrb, copy, s)) return copy; if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } if (!mrb_array_p(s)) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); } if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) { mrb_raise(mrb, E_TYPE_ERROR, "struct size mismatch"); } len = RSTRUCT_LEN(copy); for (i = 0; i < len; i++) { mrb_ary_set(mrb, copy, i, RSTRUCT_PTR(s)[i]); } return copy; } static mrb_value mrb_struct_aref_id(mrb_state *mrb, mrb_value s, mrb_sym id) { mrb_value *ptr, members, *ptr_members; mrb_int i, len; ptr = RSTRUCT_PTR(s); members = mrb_struct_members(mrb, s); ptr_members = RARRAY_PTR(members); len = RARRAY_LEN(members); 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" */ mrb_value mrb_struct_aref_n(mrb_state *mrb, mrb_value s, mrb_value idx) { mrb_int i; if (mrb_string_p(idx)) { mrb_value sym = mrb_check_intern_str(mrb, idx); if (mrb_nil_p(sym)) { mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx); } idx = sym; } if (mrb_symbol_p(idx)) { return mrb_struct_aref_id(mrb, s, mrb_symbol(idx)); } i = mrb_fixnum(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]; } mrb_value mrb_struct_aref(mrb_state *mrb, mrb_value s) { mrb_value idx; mrb_get_args(mrb, "o", &idx); return mrb_struct_aref_n(mrb, s, idx); } static mrb_value mrb_struct_aset_id(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) { mrb_value members, *ptr, *ptr_members; mrb_int i, len; members = mrb_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" */ 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_symbol_p(idx)) { return mrb_struct_aset_id(mrb, s, mrb_obj_to_sym(mrb, idx), val); } i = mrb_fixnum(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_bool equal_p; mrb_get_args(mrb, "o", &s2); if (mrb_obj_equal(mrb, s, s2)) { equal_p = 1; } else if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") || mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { equal_p = 0; } else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { mrb_bug(mrb, "inconsistent struct"); /* should never happen */ equal_p = 0; /* This substuture is just to suppress warnings. never called. */ } else { ptr = RSTRUCT_PTR(s); ptr2 = RSTRUCT_PTR(s2); len = RSTRUCT_LEN(s); equal_p = 1; 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_bool eql_p; mrb_get_args(mrb, "o", &s2); if (mrb_obj_equal(mrb, s, s2)) { eql_p = 1; } else if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") || mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { eql_p = 0; } else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { mrb_bug(mrb, "inconsistent struct"); /* should never happen */ eql_p = 0; /* This substuture is just to suppress warnings. never called. */ } else { ptr = RSTRUCT_PTR(s); ptr2 = RSTRUCT_PTR(s2); len = RSTRUCT_LEN(s); eql_p = 1; for (i=0; iStruct 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_m, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ mrb_define_method(mrb, st, "initialize", mrb_struct_initialize_m,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, "inspect", mrb_struct_inspect, MRB_ARGS_NONE()); /* 15.2.18.4.10(x) */ mrb_define_alias(mrb, st, "to_s", "inspect"); /* 15.2.18.4.11(x) */ mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */ } void mrb_mruby_struct_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/test/000077500000000000000000000000001225163307400227615ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-struct/test/struct.rb000066400000000000000000000030161225163307400246320ustar00rootroot00000000000000## # Struct ISO Test if Object.const_defined?(:Struct) assert('Struct', '15.2.18') do Struct.class == Class end assert('Struct superclass', '15.2.18.2') do Struct.superclass == Object end assert('Struct.new', '15.2.18.3.1') do c = Struct.new(:m1, :m2) c.superclass == Struct and c.members == [:m1,:m2] end # Check crash bug with Struc.new and no params. assert('Struct.new', '15.2.18.3.1') do c = Struct.new() c.superclass == Struct and 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) cc1 == cc2 end assert('Struct#[]', '15.2.18.4.2') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc[:m1] == 1 and cc["m2"] == 2 end assert('Struct#[]=', '15.2.18.4.3') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc[:m1] = 3 cc[:m1] == 3 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 } a[0] == 1 and a[1] == 2 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] } a[0] == [:m1, 1] and a[1] == [:m2, 2] end assert('Struct#members', '15.2.18.4.6') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc.members == [:m1,:m2] end assert('Struct#select', '15.2.18.4.7') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc.select{|v| v % 2 == 0} == [2] end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/000077500000000000000000000000001225163307400225615ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/mrbgem.rake000066400000000000000000000001731225163307400246770ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/mrblib/000077500000000000000000000000001225163307400240305ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/mrblib/symbol.rb000066400000000000000000000015531225163307400256660ustar00rootroot00000000000000class Symbol def to_proc Proc.new do |obj, *args| obj.send(self, *args) end end ## # call-seq: # sym.length -> integer # # Same as sym.to_s.length. def length self.to_s.length end alias :size :length ## # call-seq: # sym.capitalize -> symbol # # Same as sym.to_s.capitalize.intern. def capitalize self.to_s.capitalize.intern end ## # call-seq: # sym.downcase -> symbol # # Same as sym.to_s.downcase.intern. def downcase self.to_s.downcase.intern end ## # call-seq: # sym.upcase -> symbol # # Same as sym.to_s.upcase.intern. def upcase self.to_s.upcase.intern end # # call-seq: # sym.empty? -> true or false # # Returns that _sym_ is :"" or not. def empty? self.to_s.empty? end end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/src/000077500000000000000000000000001225163307400233505ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/src/symbol.c000066400000000000000000000025671225163307400250330ustar00rootroot00000000000000#include "mruby.h" #include "mruby/khash.h" #include "mruby/array.h" typedef struct symbol_name { size_t len; const char *name; } symbol_name; KHASH_DECLARE(n2s, symbol_name, mrb_sym, 1) /* * 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) { khiter_t k; mrb_sym sym; khash_t(n2s) *h = mrb->name2sym; mrb_value ary = mrb_ary_new_capa(mrb, kh_size(h)); for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { sym = kh_value(h, k); mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } } return ary; } 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()); } void mrb_mruby_symbol_ext_gem_final(mrb_state* mrb) { } mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/test/000077500000000000000000000000001225163307400235405ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-symbol-ext/test/symbol.rb000066400000000000000000000013151225163307400253720ustar00rootroot00000000000000## # 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 end assert("Symbol#downcase") do assert_equal :hello, :hEllO.downcase end assert("Symbol#upcase") do assert_equal :HELLO, :hEllO.upcase end assert("Symbol#empty?") do assert_true :''.empty? end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-time/000077500000000000000000000000001225163307400214145ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-time/mrbgem.rake000066400000000000000000000001651225163307400235330ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-time') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-time/src/000077500000000000000000000000001225163307400222035ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-time/src/time.c000066400000000000000000000510671225163307400233160ustar00rootroot00000000000000/* ** time.c - Time class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include #include #include "mruby/class.h" #include "mruby/data.h" /** 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 UTF time */ /* define following macro to use probably faster timegm() on the platform */ /* #define USE_SYSTEM_TIMEGM */ /** end of Time class configuration */ #ifndef NO_GETTIMEOFDAY #include #endif #ifdef NO_GMTIME_R #define gmtime_r(t,r) gmtime(t) #define localtime_r(t,r) (tzset(),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 C89, 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 }; static const char *timezone_names[] = { "none", "UTC", "LOCAL", NULL }; static const char *mon_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static const char *wday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; struct mrb_time { time_t sec; time_t usec; enum mrb_timezone timezone; struct tm datetime; }; static 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)); tm->sec = (time_t)sec; tm->usec = (sec - tm->sec) * 1.0e6 + usec; while (tm->usec < 0) { tm->sec--; tm->usec += 1.0e6; } while (tm->usec > 1.0e6) { tm->sec++; tm->usec -= 1.0e6; } 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)); #ifdef 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 < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time."); } return time_alloc(mrb, 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, 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, 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_cstr(mrb, timezone_names[tm->timezone]); } /* 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); } DATA_TYPE(self) = &mrb_time_type; DATA_PTR(self) = NULL; 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); } DATA_PTR(self) = tm; 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)) { DATA_PTR(copy) = mrb_malloc(mrb, sizeof(struct mrb_time)); DATA_TYPE(copy) = &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); return mrb_fixnum_value(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); return mrb_fixnum_value(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_class_get(mrb, "Comparable")); mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ANY()); /* 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-0.0.0~20131214+git882afdea/mrbgems/mruby-time/test/000077500000000000000000000000001225163307400223735ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-time/test/time.rb000066400000000000000000000104061225163307400236570ustar00rootroot00000000000000## # Time ISO Test if Object.const_defined?(:Time) 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 superclass', '15.2.19.2') do Time.superclass == Object 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 end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/000077500000000000000000000000001225163307400231065ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/mrbgem.rake000066400000000000000000000001751225163307400252260ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' end mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/mrblib/000077500000000000000000000000001225163307400243555ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb000066400000000000000000000002371225163307400265360ustar00rootroot00000000000000 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-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/test/000077500000000000000000000000001225163307400240655ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrbgems/mruby-toplevel-ext/test/toplevel.rb000066400000000000000000000007521225163307400262500ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/mrblib/000077500000000000000000000000001225163307400171355ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/mrblib/array.rb000066400000000000000000000040361225163307400206030ustar00rootroot00000000000000## # 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) idx, length = -1, self.length-1 while idx < length and length <= self.length and length = self.length-1 elm = self[idx += 1] unless elm if elm == nil and length >= self.length break end end block.call(elm) 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) 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) 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 ## # Delete element with index +key+ def delete(key, &block) while i = self.index(key) self.delete_at(i) ret = key end if ret == nil && block block.call else ret end end end ## # Array is enumerable and comparable module Enumerable; end module Comparable; end class Array # ISO 15.2.12.3 include Enumerable include Comparable ## # Sort all elements and replace +self+ with these # elements. def sort!(&block) self.replace(self.sort(&block)) end end mruby-0.0.0~20131214+git882afdea/mrblib/class.rb000066400000000000000000000016201225163307400205660ustar00rootroot00000000000000class Module # 15.2.2.4.13 def attr_reader(*names) names.each do |name| name = name.to_s raise(NameError, "#{name.inspect} is not allowed as an instance variable name") if name.include?('@') || name.include?('?') || name.include?('$') attr_name = '@'+name define_method(name){self.instance_variable_get(attr_name)} end end # 15.2.2.4.14 def attr_writer(*names) names.each do |name| name = name.to_s raise(NameError, "#{name.inspect} is not allowed as an instance variable name") if name.include?('@') || name.include?('?') || name.include?('$') attr_name = '@'+name name = (name+"=").intern define_method(name){|v|self.instance_variable_set(attr_name,v)} end end # 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-0.0.0~20131214+git882afdea/mrblib/compar.rb000066400000000000000000000027561225163307400207550ustar00rootroot00000000000000## # 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? false elsif cmp < 0 true else false end 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? false elsif cmp <= 0 true else false end end ## # Return true if +self+ is equal # to +other+. Otherwise return # false. # # ISO 15.3.3.2.3 def == other cmp = self <=> other if cmp == 0 true else false end 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? false elsif cmp > 0 true else false end 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? false elsif cmp >= 0 true else false end 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) if self < min or self > max false else true end end end mruby-0.0.0~20131214+git882afdea/mrblib/enum.rb000066400000000000000000000200641225163307400204300ustar00rootroot00000000000000## # Enumerable # # ISO 15.3.2 # # 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. 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) st = true if block self.each{|val| unless block.call(val) st = false break end } else self.each{|val| unless val st = false break end } end st 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) st = false if block self.each{|val| if block.call(val) st = true break end } else self.each{|val| if val st = true break end } end st 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) 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 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) i = 0 self.each{|val| block.call(val, 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| ary.push val } 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) ary = [] self.each{|val| ary.push(val) 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| if pattern === val ary.push((block)? block.call(val): val) 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) st = false self.each{|val| if val == obj st = true break end } st 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| 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| 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| 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) else ary_F.push(val) 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) 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)} unless ary.empty? __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 end mruby-0.0.0~20131214+git882afdea/mrblib/error.rb000066400000000000000000000016031225163307400206130ustar00rootroot00000000000000## # Exception # # ISO 15.2.22 class Exception ## # Raise an exception. # # ISO 15.2.22.4.1 def self.exception(*args, &block) self.new(*args, &block) end end # 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 new(message="NameError", name=nil) initialize(message, name) end def initialize(message=nil, name=nil) @name = name super(message) end end # ISO 15.2.32 class NoMethodError < NameError end # ISO 15.2.33 class IndexError < StandardError end class KeyError < IndexError end class NotImplementedError < ScriptError end mruby-0.0.0~20131214+git882afdea/mrblib/hash.rb000066400000000000000000000075541225163307400204200ustar00rootroot00000000000000## # Hash # # ISO 15.2.13 class Hash ## # 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) self.keys.each{|k| block.call([k, self[k]])} 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) 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) self.keys.each{|k| block.call(self[k])} self end ## # Create a direct instance of the class Hash. # # ISO 15.2.13.4.16 def initialize(*args, &block) self.__init_core(block, *args) end ## # 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 "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 # 1.8/1.9 Hash#reject! returns Hash; ISO says nothing. def reject!(&b) keys = [] self.each_key{|k| v = self[k] if b.call(k, v) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end # 1.8/1.9 Hash#reject returns Hash; ISO says nothing. def reject(&b) h = {} self.each_key{|k| v = self[k] unless b.call(k, v) h[k] = v end } h end # 1.9 Hash#select! returns Hash; ISO says nothing. def select!(&b) keys = [] self.each_key{|k| v = self[k] unless b.call(k, v) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end # 1.9 Hash#select returns Hash; ISO says nothing. def select(&b) h = {} self.each_key{|k| v = self[k] if b.call(k, v) h[k] = v end } h end end ## # Hash is enumerable # # ISO 15.2.13.3 module Enumerable; end class Hash include Enumerable end mruby-0.0.0~20131214+git882afdea/mrblib/init_mrblib.c000066400000000000000000000003501225163307400215710ustar00rootroot00000000000000#include "mruby.h" #include "mruby/irep.h" #include "mruby/dump.h" #include "mruby/string.h" #include "mruby/proc.h" extern const uint8_t mrblib_irep[]; void mrb_init_mrblib(mrb_state *mrb) { mrb_load_irep(mrb, mrblib_irep); } mruby-0.0.0~20131214+git882afdea/mrblib/kernel.rb000066400000000000000000000011311225163307400207360ustar00rootroot00000000000000## # Kernel # # ISO 15.3.1 module Kernel # 15.3.1.2.1 def self.`(s) raise NotImplementedError.new("` not implemented") end # 15.3.1.3.5 def `(s) Kernel.`(s) end ## # Calls the given block repetitively. # # ISO 15.3.1.2.8 def self.loop #(&block) while(true) yield end end # 15.3.1.2.3 def self.eval(s) raise NotImplementedError.new("eval not implemented") end # 15.3.1.3.12 def eval(s) Kernel.eval(s) end ## # Alias for +Kernel.loop+. # # ISO 15.3.1.3.29 def loop #(&block) while(true) yield end end end mruby-0.0.0~20131214+git882afdea/mrblib/mrblib.rake000066400000000000000000000013451225163307400212530ustar00rootroot00000000000000MRuby.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}" 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] + Dir.glob("#{current_dir}/*.rb").sort do |t| mrbc_, *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-0.0.0~20131214+git882afdea/mrblib/numeric.rb000066400000000000000000000044641225163307400211340ustar00rootroot00000000000000## # 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) 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) 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) 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) i = if num.kind_of? Float then self.to_f else self end while(i <= num) block.call(i) i += step 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.to_i.times { n /= 2 } n end def << other n = self.to_i other.to_i.times { n *= 2 } n.to_i end def divmod(other) end end mruby-0.0.0~20131214+git882afdea/mrblib/print.rb000066400000000000000000000005441225163307400206210ustar00rootroot00000000000000## # Kernel # # ISO 15.3.1 module Kernel def print(*a) raise NotImplementedError.new('print not available') end def puts(*a) raise NotImplementedError.new('puts not available') end def p(*a) raise NotImplementedError.new('p not available') end def printf(*args) raise NotImplementedError.new('printf not available') end end mruby-0.0.0~20131214+git882afdea/mrblib/range.rb000066400000000000000000000011571225163307400205620ustar00rootroot00000000000000## # 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) val = self.first unless val.respond_to? :succ raise TypeError, "can't iterate" end last = self.last return self if (val <=> last) > 0 while((val <=> last) < 0) block.call(val) val = val.succ end if not exclude_end? and (val <=> last) == 0 block.call(val) end self end end ## # Range is enumerable # # ISO 15.2.14.3 module Enumerable; end class Range include Enumerable end mruby-0.0.0~20131214+git882afdea/mrblib/string.rb000066400000000000000000000064301225163307400207730ustar00rootroot00000000000000## # String # # ISO 15.2.10 class String ## # Calls the given block for each line # and pass the respective line. # # ISO 15.2.10.5.15 def each_line(&block) # expect that str.index accepts an Integer for 1st argument as a byte data offset = 0 while(pos = self.index(0x0a, 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 ## # 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 split(args[0], -1).join(args[1]) 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) if str != self self.replace(str) self else nil end 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 split(args[0], 2).join(args[1]) 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) if str != self self.replace(str) self else nil end 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+ # at the position +pos+ with +value+. def []=(pos, value) b = self[0, pos] a = self[pos+1..-1] self.replace([b, value, a].join('')) end ## # ISO 15.2.10.5.5 def =~(re) re =~ self end ## # ISO 15.2.10.5.27 def match(re, &block) re.match(self, &block) end end ## # String is comparable # # ISO 15.2.10.3 module Comparable; end class String include Comparable end mruby-0.0.0~20131214+git882afdea/src/000077500000000000000000000000001225163307400164555ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/src/array.c000066400000000000000000000673011225163307400177460ustar00rootroot00000000000000/* ** array.c - Array class ** ** See Copyright Notice in mruby.h */ #ifndef SIZE_MAX /* Some versions of VC++ * has SIZE_MAX in stdint.h */ # include #endif #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/string.h" #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 (RARRAY_LEN(ary) == 0) return mrb_nil_value(); 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_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_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, size_t size) { size_t i; for (i = 0; i < size; i++) { dst[i] = src[i]; } } mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) { mrb_value arv[2]; arv[0] = car; arv[1] = cdr; return mrb_ary_new_from_values(mrb, 2, arv); } static void ary_fill_with_nil(mrb_value *ptr, mrb_int size) { mrb_value nil = mrb_nil_value(); while ((int)(size--)) { *ptr++ = nil; } } static void ary_modify(mrb_state *mrb, struct RArray *a) { if (a->flags & MRB_ARY_SHARED) { 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); } a->flags &= ~MRB_ARY_SHARED; } } 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 (!(a->flags & MRB_ARY_SHARED)) { 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; a->flags |= MRB_ARY_SHARED; } } 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"); } while (capa < len) { if (capa == 0) { capa = ARY_DEFAULT_LEN; } else { 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); if (!expanded_ptr) { mrb_raise(mrb, E_RUNTIME_ERROR, "out of memory"); } 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_value mrb_ary_s_create(mrb_state *mrb, mrb_value self) { mrb_value *vals; 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; } 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); } 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; } mrb_value mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value ary; mrb_value *ptr; mrb_int blen; mrb_get_args(mrb, "a", &ptr, &blen); ary = mrb_ary_new_capa(mrb, a1->len + blen); a2 = mrb_ary_ptr(ary); array_copy(a2->ptr, a1->ptr, a1->len); array_copy(a2->ptr + a1->len, ptr, blen); a2->len = a1->len + blen; return ary; } /* * call-seq: * ary <=> other_ary -> -1, 0, +1 or nil * * 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. * * [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1 * [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1 * */ mrb_value mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; struct RArray *a1, *a2; mrb_value r; mrb_int i, len; mrb_get_args(mrb, "o", &ary2); if (!mrb_array_p(ary2)) return mrb_nil_value(); a1 = RARRAY(ary1); a2 = RARRAY(ary2); if (a1->len == a2->len && a1->ptr == a2->ptr) return mrb_fixnum_value(0); else { mrb_sym cmp = mrb_intern_lit(mrb, "<=>"); len = RARRAY_LEN(ary1); if (len > RARRAY_LEN(ary2)) { len = RARRAY_LEN(ary2); } for (i=0; ilen - a2->len; return mrb_fixnum_value((len == 0)? 0: (len > 0)? 1: -1); } 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; } 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); } 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; } mrb_value mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value ary; 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); ary = mrb_ary_new_capa(mrb, a1->len * times); a2 = mrb_ary_ptr(ary); ptr = a2->ptr; while (times--) { array_copy(ptr, a1->ptr, a1->len); ptr += a1->len; a2->len += a1->len; } return ary; } 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; } mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self), *b; mrb_value ary; ary = mrb_ary_new_capa(mrb, a->len); b = mrb_ary_ptr(ary); 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 ary; } mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { mrb_value ary; struct RArray *a; ary = mrb_ary_new_capa(mrb, size); a = mrb_ary_ptr(ary); array_copy(a->ptr, vals, size); a->len = size; return ary; } void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) /* mrb_ary_push */ { 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_write_barrier(mrb, (struct RBasic*)a); } mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; int len; mrb_get_args(mrb, "*", &argv, &len); while (len--) { mrb_ary_push(mrb, self, *argv++); } return self; } 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_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 (a->flags & MRB_ARY_SHARED) { 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 ((int)(--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_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); if ((a->flags & MRB_ARY_SHARED) && 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_write_barrier(mrb, (struct RBasic*)a); return self; } mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_value *vals; int len; mrb_get_args(mrb, "*", &vals, &len); if ((a->flags & MRB_ARY_SHARED) && 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; mrb_write_barrier(mrb, (struct RBasic*)a); return self; } 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 <= (int)n) return mrb_nil_value(); return a->ptr[n]; } void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) /* rb_ary_store */ { 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 <= (int)n) { if (a->aux.capa <= (int)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_write_barrier(mrb, (struct RBasic*)a); } 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; mrb_value *argv; mrb_int i, argc; ary_modify(mrb, a); /* 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, (int)(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); } a->len = size; return ary; } mrb_int mrb_ary_len(mrb_state *mrb, mrb_value ary) { return RARRAY_LEN(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++; b->flags |= MRB_ARY_SHARED; return mrb_obj_value(b); } mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int index, len; mrb_value *argv; int size; mrb_get_args(mrb, "i*", &index, &argv, &size); switch(size) { case 0: return mrb_ary_ref(mrb, self, index); case 1: if (mrb_type(argv[0]) != MRB_TT_FIXNUM) { mrb_raise(mrb, E_TYPE_ERROR, "expected Fixnum"); } if (index < 0) index += a->len; if (index < 0 || a->len < (int)index) return mrb_nil_value(); len = mrb_fixnum(argv[0]); if (len < 0) return mrb_nil_value(); if (a->len == (int)index) return mrb_ary_new(mrb); if (len > a->len - index) len = a->len - index; return ary_subseq(mrb, a, index, len); default: mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); break; } return mrb_nil_value(); /* dummy to avoid warning : not reach here */ } mrb_value mrb_ary_aset(mrb_state *mrb, mrb_value self) { mrb_value *argv; int argc; mrb_get_args(mrb, "*", &argv, &argc); switch(argc) { case 2: if (!mrb_fixnum_p(argv[0])) { /* Should we support Range object for 1st arg ? */ mrb_raise(mrb, E_TYPE_ERROR, "expected Fixnum for 1st argument"); } mrb_ary_set(mrb, self, mrb_fixnum(argv[0]), argv[1]); return argv[1]; case 3: mrb_ary_splice(mrb, self, mrb_fixnum(argv[0]), mrb_fixnum(argv[1]), argv[2]); return argv[2]; default: mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); return mrb_nil_value(); } } 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 <= (int)index) return mrb_nil_value(); ary_modify(mrb, a); val = a->ptr[index]; ptr = a->ptr + index; len = a->len - index; while ((int)(--len)) { *ptr = *(ptr+1); ++ptr; } --a->len; ary_shrink_capa(mrb, a); return val; } 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 (a->flags & MRB_ARY_SHARED) { return ary_subseq(mrb, a, 0, size); } return mrb_ary_new_from_values(mrb, size, a->ptr); } mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int size; mrb_value *vals; int len; mrb_get_args(mrb, "*", &vals, &len); if (len > 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } if (len == 0) return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value(); /* len == 1 */ size = mrb_fixnum(*vals); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } if (size > a->len) size = a->len; if ((a->flags & MRB_ARY_SHARED) || 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); } 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(); } 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_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { if (mrb_array_p(v)) { return v; } 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_value mrb_ary_clear(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); ary_modify(mrb, a); a->len = 0; a->aux.capa = 0; mrb_free(mrb, a->ptr); a->ptr = 0; return self; } 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_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_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 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; } /* 15.2.12.5.31 (x) */ /* * call-seq: * ary.to_s -> string * ary.inspect -> string * * Creates a string representation of +self+. */ static mrb_value mrb_ary_inspect(mrb_state *mrb, mrb_value ary) { if (RARRAY_LEN(ary) == 0) return mrb_str_new(mrb, "[]", 2); return inspect_ary(mrb, ary, mrb_ary_new(mrb)); } 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_buf_cat(mrb, result, RSTRING_PTR(sep), RSTRING_LEN(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_buf_cat(mrb, result, RSTRING_PTR(val), RSTRING_LEN(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_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); } /* 15.2.12.5.33 (x) */ /* * call-seq: * ary == other_ary -> bool * * 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. * * [ "a", "c" ] == [ "a", "c", 7 ] #=> false * [ "a", "c", 7 ] == [ "a", "c", 7 ] #=> true * [ "a", "c", 7 ] == [ "a", "d", "f" ] #=> false * */ static mrb_value mrb_ary_equal(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_int i; mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); if (mrb_special_const_p(ary2)) return mrb_false_value(); if (!mrb_array_p(ary2)) { if (!mrb_respond_to(mrb, ary2, mrb_intern_lit(mrb, "to_ary"))) { return mrb_false_value(); } else { return mrb_bool_value(mrb_equal(mrb, ary2, ary1)); } } if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return mrb_false_value(); for (i=0; i true or false * * Returns true if +self+ and _other_ are the same object, * or are both arrays with the same content. */ static mrb_value mrb_ary_eql(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_int i; 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(); for (i=0; iarray_class = mrb_define_class(mrb, "Array", mrb->object_class); MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); mrb_include_module(mrb, a, mrb_class_get(mrb, "Enumerable")); 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, "inspect", mrb_ary_inspect, MRB_ARGS_NONE()); /* 15.2.12.5.31 (x) */ mrb_define_alias(mrb, a, "to_s", "inspect"); /* 15.2.12.5.32 (x) */ mrb_define_method(mrb, a, "==", mrb_ary_equal, MRB_ARGS_REQ(1)); /* 15.2.12.5.33 (x) */ mrb_define_method(mrb, a, "eql?", mrb_ary_eql, MRB_ARGS_REQ(1)); /* 15.2.12.5.34 (x) */ mrb_define_method(mrb, a, "<=>", mrb_ary_cmp, MRB_ARGS_REQ(1)); /* 15.2.12.5.36 (x) */ } mruby-0.0.0~20131214+git882afdea/src/backtrace.c000066400000000000000000000064741225163307400205530ustar00rootroot00000000000000/* ** backtrace.c - ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/variable.h" #include "mruby/proc.h" #include "mruby/array.h" #include "mruby/string.h" #include "mruby/class.h" #include "mruby/debug.h" #include typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...); #ifdef ENABLE_STDIO static void print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf((FILE*)stream, format, ap); va_end(ap); } #endif #define MIN_BUFSIZE 127 static void get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) { va_list ap; mrb_value ary, str; int ai; if (level > 0) { return; } ai = mrb_gc_arena_save(mrb); ary = mrb_obj_value((struct RArray*)stream); va_start(ap, format); str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1); va_end(ap); va_start(ap, format); vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap); va_end(ap); mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1); mrb_ary_push(mrb, ary, str); mrb_gc_arena_restore(mrb, ai); } static void mrb_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) { mrb_callinfo *ci; mrb_int ciidx; const char *filename, *method, *sep; int i, line; func(mrb, stream, 1, "trace:\n"); ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))); if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ for (i = ciidx; i >= 0; i--) { ci = &mrb->c->cibase[i]; filename = NULL; line = -1; if (MRB_PROC_CFUNC_P(ci->proc)) { continue; } else { mrb_irep *irep = ci->proc->body.irep; mrb_code *pc; 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 = (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))); } filename = mrb_debug_get_filename(irep, pc - irep->iseq); line = mrb_debug_get_line(irep, pc - irep->iseq); } if (line == -1) continue; if (ci->target_class == ci->proc->target_class) sep = "."; else sep = "#"; if (!filename) { filename = "(unknown)"; } method = mrb_sym2name(mrb, ci->mid); if (method) { const char *cn = mrb_class_name(mrb, ci->proc->target_class); if (cn) { func(mrb, stream, 1, "\t[%d] ", i); func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, line, cn, sep, method); func(mrb, stream, 1, "\n"); } else { func(mrb, stream, 1, "\t[%d] ", i); func(mrb, stream, 0, "%s:%d:in %s", filename, line, method); func(mrb, stream, 1, "\n"); } } else { func(mrb, stream, 1, "\t[%d] ", i); func(mrb, stream, 0, "%s:%d", filename, line); func(mrb, stream, 1, "\n"); } } } void mrb_print_backtrace(mrb_state *mrb) { #ifdef ENABLE_STDIO mrb_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr); #endif } mrb_value mrb_get_backtrace(mrb_state *mrb, mrb_value self) { mrb_value ary; ary = mrb_ary_new(mrb); mrb_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary)); return ary; } mruby-0.0.0~20131214+git882afdea/src/class.c000066400000000000000000001434141225163307400177350ustar00rootroot00000000000000/* ** class.c - Class class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include #include #include "mruby/array.h" #include "mruby/class.h" #include "mruby/numeric.h" #include "mruby/proc.h" #include "mruby/string.h" #include "mruby/variable.h" #include "error.h" KHASH_DEFINE(mt, mrb_sym, struct RProc*, 1, 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, c->mt); } void mrb_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)); } #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 = 0; 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)); } struct RClass* mrb_define_module_id(mrb_state *mrb, mrb_sym name) { struct RClass *m = mrb_module_new(mrb); mrb_obj_iv_set(mrb, (struct RObject*)mrb->object_class, name, mrb_obj_value(m)); mrb_name_class(mrb, m, name); return m; } struct RClass* mrb_define_module(mrb_state *mrb, const char *name) { return mrb_define_module_id(mrb, mrb_intern_cstr(mrb, name)); } static void setup_class(mrb_state *mrb, mrb_value outer, struct RClass *c, mrb_sym id) { mrb_name_class(mrb, c, id); mrb_const_set(mrb, outer, id, mrb_obj_value(c)); mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), outer); } 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 0; return mrb_class_ptr(outer); } struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { struct RClass *c; mrb_value v; if (mrb_const_defined(mrb, outer, id)) { v = mrb_const_get(mrb, outer, id); c = mrb_class_ptr(v); } else { c = mrb_module_new(mrb); setup_class(mrb, outer, c, id); } return c; } struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { struct RClass *c = mrb_class_new(mrb, super); mrb_obj_iv_set(mrb, (struct RObject*)mrb->object_class, name, mrb_obj_value(c)); mrb_name_class(mrb, c, name); return c; } 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); } struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *c, *s; if (mrb_const_defined(mrb, outer, id)) { mrb_value v = mrb_const_get(mrb, outer, id); mrb_check_type(mrb, v, MRB_TT_CLASS); c = mrb_class_ptr(v); 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); } if (!c->super || mrb_class_ptr(super) != mrb_class_real(c->super)) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", mrb_sym2str(mrb, id)); } } return 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 = mrb->object_class; } c = mrb_class_new(mrb, s); setup_class(mrb, outer, c, id); mrb_funcall(mrb, mrb_obj_value(s), "inherited", 1, mrb_obj_value(c)); return c; } 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)); } 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); if (mrb_type(c) != MRB_TT_MODULE && mrb_type(c) != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_sym2str(mrb, id)); } return mrb_class_ptr(c); } struct RClass * mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } 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)); } /*! * 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. */ struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) { struct RClass * c; mrb_sym id = mrb_intern_cstr(mrb, name); if (mrb_const_defined_at(mrb, outer, id)) { c = class_from_sym(mrb, outer, id); if (mrb_class_real(c->super) != super) { mrb_name_error(mrb, id, "%S is already defined", name); } return c; } if (!super) { mrb_warn(mrb, "no super class for `%S::%S', Object assumed", outer, name); } c = mrb_class_new(mrb, super); setup_class(mrb, mrb_obj_value(outer), c, id); return c; } struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { struct RClass * c; mrb_sym id = mrb_intern_cstr(mrb, name); if (mrb_const_defined_at(mrb, outer, id)) { c = class_from_sym(mrb, outer, id); return c; } c = mrb_module_new(mrb); setup_class(mrb, mrb_obj_value(outer), c, id); return c; } void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { khash_t(mt) *h = c->mt; khiter_t k; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, h, mid); kh_value(h, k) = p; if (p) { mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); } } 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); } 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); } void mrb_define_method_vm(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_value body) { khash_t(mt) *h = c->mt; khiter_t k; struct RProc *p; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, h, name); p = mrb_proc_ptr(body); kh_value(h, k) = p; if (p) { mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); } } 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"); } /* 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] A: Array [mrb_value] H: Hash [mrb_value] s: String [char*,int] Receive two arguments. z: String [char*] NUL terminated string. a: Array [mrb_value*,mrb_int] Receive two arguments. f: Float [mrb_float] i: Integer [mrb_int] b: Boolean [mrb_bool] n: Symbol [mrb_sym] &: Block [mrb_value] *: rest argument [mrb_value*,int] Receive the rest of the arguments as an array. |: optional Next argument of '|' and later are optional. */ int mrb_get_args(mrb_state *mrb, const char *format, ...) { char c; int i = 0; mrb_value *sp = mrb->c->stack + 1; va_list ap; int argc = mrb->c->ci->argc; int opt = 0; va_start(ap, format); if (argc < 0) { struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); argc = a->len; sp = a->ptr; } while ((c = *format++)) { switch (c) { case '|': case '*': case '&': break; default: if (argc <= i && !opt) { 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 = *sp++; i++; } } break; case 'C': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { mrb_value ss; ss = *sp++; 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 (i < argc) { *p = to_str(mrb, *sp++); i++; } } break; case 'A': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { *p = to_ary(mrb, *sp++); i++; } } break; case 'H': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { *p = to_hash(mrb, *sp++); i++; } } break; case 's': { mrb_value ss; struct RString *s; char **ps = 0; int *pl = 0; ps = va_arg(ap, char**); pl = va_arg(ap, int*); if (i < argc) { ss = to_str(mrb, *sp++); s = mrb_str_ptr(ss); *ps = s->ptr; *pl = s->len; i++; } } break; case 'z': { mrb_value ss; struct RString *s; char **ps; mrb_int len; ps = va_arg(ap, char**); if (i < argc) { ss = to_str(mrb, *sp++); s = mrb_str_ptr(ss); len = (mrb_int)strlen(s->ptr); if (len < s->len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "String contains NUL"); } else if (len > s->len) { mrb_str_modify(mrb, s); } *ps = s->ptr; 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 (i < argc) { aa = to_ary(mrb, *sp++); 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) { switch (mrb_type(*sp)) { case MRB_TT_FLOAT: *p = mrb_float(*sp); break; case MRB_TT_FIXNUM: *p = (mrb_float)mrb_fixnum(*sp); break; case MRB_TT_STRING: mrb_raise(mrb, E_TYPE_ERROR, "String can't be coerced into Float"); break; default: { mrb_value tmp; tmp = mrb_convert_type(mrb, *sp, MRB_TT_FLOAT, "Float", "to_f"); *p = mrb_float(tmp); } break; } sp++; i++; } } break; case 'i': { mrb_int *p; p = va_arg(ap, mrb_int*); if (i < argc) { switch (mrb_type(*sp)) { case MRB_TT_FIXNUM: *p = mrb_fixnum(*sp); break; case MRB_TT_FLOAT: { mrb_float f = mrb_float(*sp); if (!FIXABLE(f)) { mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); } *p = (mrb_int)f; } break; default: *p = mrb_fixnum(mrb_Integer(mrb, *sp)); break; } sp++; i++; } } break; case 'b': { mrb_bool *boolp = va_arg(ap, mrb_bool*); if (i < argc) { mrb_value b = *sp++; *boolp = mrb_test(b); i++; } } break; case 'n': { mrb_sym *symp; symp = va_arg(ap, mrb_sym*); if (i < argc) { mrb_value ss; ss = *sp++; if (mrb_type(ss) == MRB_TT_SYMBOL) { *symp = mrb_symbol(ss); } else if (mrb_string_p(ss)) { *symp = 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); } 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 = 1; break; case '*': { mrb_value **var; int *pl; var = va_arg(ap, mrb_value**); pl = va_arg(ap, int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { *var = sp; } i = argc; sp += *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; } } 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); c->super = super ? super : mrb->object_class; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); c->mt = kh_init(mt, mrb); return c; } void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { struct RClass *ins_pos; ins_pos = c; while (m) { struct RClass *p = c, *ic; int superclass_seen = 0; if (c->mt == m->mt) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } while (p) { if (c != p && p->tt == MRB_TT_CLASS) { superclass_seen = 1; } else if (p->mt == m->mt){ if (p->tt == MRB_TT_ICLASS && !superclass_seen) { ins_pos = p; } goto skip; } p = p->super; } ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); if (m->tt == MRB_TT_ICLASS) { ic->c = m->c; } else { ic->c = m; } ic->mt = m->mt; ic->iv = m->iv; ic->super = ins_pos->super; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); ins_pos = ic; skip: m = m->super; } } static mrb_value mrb_mod_append_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_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); return mod; } static mrb_value mrb_mod_include(mrb_state *mrb, mrb_value klass) { mrb_value *argv; 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); mrb_ary_push(mrb, result, mrb_obj_value(c)); c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } else if (c->tt != MRB_TT_SCLASS) { 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); result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } c = c->super; } return result; } mrb_value 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 class_instance_method_list(mrb, recur, c, 0); } mrb_value mrb_yield_internal(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv, mrb_value self, struct RClass *c); /* 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; struct RClass *c; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented"); } c = mrb_class_ptr(mod); return mrb_yield_internal(mrb, b, 0, 0, mod, c); } mrb_value mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) { return mod; } 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); return mrb_obj_value(obj->c); } 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); } 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); } 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); } 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, h, mid); if (k != kh_end(h)) { m = kh_value(h, k); if (!m) break; *cp = c; return m; } } c = c->super; } return 0; /* no method */ } 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 (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 mrb_instance_alloc(mrb_state *mrb, mrb_value cv) { struct RClass *c = mrb_class_ptr(cv); struct RObject *o; enum mrb_vtype ttype = MRB_INSTANCE_TT(c); if (c->tt == MRB_TT_SCLASS) mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class"); if (ttype == 0) ttype = MRB_TT_OBJECT; o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c); return mrb_obj_value(o); } /* * call-seq: * class.new(args, ...) -> obj * * Calls allocate to create 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_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { mrb_value obj, blk; mrb_value *argv; int argc; obj = mrb_instance_alloc(mrb, cv); mrb_get_args(mrb, "*&", &argv, &argc, &blk); mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk); return obj; } mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, int argc, 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_new_class(mrb_state *mrb, mrb_value cv) { mrb_value super, blk; mrb_value new_class; if (mrb_get_args(mrb, "|C&", &super, &blk) == 0) { super = mrb_obj_value(mrb->object_class); } new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); if (!mrb_nil_p(blk)) { mrb_funcall_with_block(mrb, new_class, mrb_intern_cstr(mrb, "class_eval"), 0, NULL, blk); } mrb_funcall(mrb, super, "inherited", 1, new_class); return new_class; } mrb_value mrb_class_superclass(mrb_state *mrb, mrb_value klass) { struct RClass *c; c = mrb_class_ptr(klass); c = c->super; while (c && c->tt == MRB_TT_ICLASS) { c = 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)); } /* 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; int alen; mrb_value inspect; mrb_get_args(mrb, "n*", &name, &a, &alen); if (mrb_respond_to(mrb,mod,mrb_intern_lit(mrb, "inspect"))){ inspect = mrb_funcall(mrb, mod, "inspect", 0); if (RSTRING_LEN(inspect) > 64) { inspect = mrb_any_to_s(mrb, mod); } } else { inspect = mrb_any_to_s(mrb, mod); } mrb_raisef(mrb, E_NOMETHOD_ERROR, "undefined method '%S' for %S", mrb_sym2str(mrb, name), inspect); /* not reached */ return mrb_nil_value(); } mrb_bool mrb_obj_respond_to(struct RClass* c, mrb_sym mid) { khiter_t k; while (c) { khash_t(mt) *h = c->mt; if (h) { k = kh_get(mt, 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_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid) { return mrb_obj_respond_to(mrb_class(mrb, obj), mid); } mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; const char *name; size_t len; 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); 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_plus(mrb, base, mrb_str_new(mrb, "::", 2)); name = mrb_sym2name_len(mrb, sym, &len); mrb_str_concat(mrb, path, mrb_str_new(mrb, 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; } struct RClass * mrb_class_real(struct RClass* cl) { while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; } return cl; } 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(mrb, "#", 1); } return mrb_str_ptr(path)->ptr; } 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. */ 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. */ 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. */ struct RClass * mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); m->mt = kh_init(mt, mrb); 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 */ struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj) { return mrb_class_real(mrb_class(mrb, obj)); } 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_vm(mrb, c, a, mrb_obj_value(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 */ 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(mrb, "#", 1); } 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(mrb, str, "#", 1); } else { str = path; } } return str; } 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) { mrb_value m; if (!mrb_obj_respond_to(c, a)) { mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); } else { MRB_SET_VALUE(m, MRB_TT_PROC, value.p, 0); mrb_define_method_vm(mrb, c, a, m); } } void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { undef_method(mrb, c, mrb_intern_cstr(mrb, name)); } 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); } mrb_value mrb_mod_undef(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); 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_sym(mrb_state *mrb, mrb_sym id) { const char *s; size_t len; s = mrb_sym2name_len(mrb, id, &len); if (len < 3 || !(s[0] == '@' && s[1] == '@')) { mrb_name_error(mrb, id, "`%S' is not allowed as a class variable name", mrb_sym2str(mrb, id)); } } static void check_cv_name_str(mrb_state *mrb, mrb_value str) { const char *s = RSTRING_PTR(str); size_t const 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 mrb_value get_sym_or_str_arg(mrb_state *mrb) { mrb_value sym_or_str; mrb_get_args(mrb, "o", &sym_or_str); if (mrb_symbol_p(sym_or_str) || mrb_string_p(sym_or_str)) { return sym_or_str; } else { mrb_value obj = mrb_funcall(mrb, sym_or_str, "inspect", 0); mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj); return mrb_nil_value(); } } /* 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_value id; mrb_bool defined_p; id = get_sym_or_str_arg(mrb); if (mrb_symbol_p(id)) { check_cv_name_sym(mrb, mrb_symbol(id)); defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(id)); } else { mrb_value sym; check_cv_name_str(mrb, id); sym = mrb_check_intern_str(mrb, id); if (mrb_nil_p(sym)) { defined_p = FALSE; } else { defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(sym)); } } return mrb_bool_value(defined_p); } /* 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] * [] */ 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_value id; mrb_bool method_defined_p; id = get_sym_or_str_arg(mrb); if (mrb_symbol_p(id)) { method_defined_p = mrb_obj_respond_to(mrb_class_ptr(mod), mrb_symbol(id)); } else { mrb_value sym = mrb_check_intern_str(mrb, id); if (mrb_nil_p(sym)) { method_defined_p = FALSE; } else { method_defined_p = mrb_obj_respond_to(mrb_class_ptr(mod), mrb_symbol(sym)); } } return mrb_bool_value(method_defined_p); } static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); khash_t(mt) *h = c->mt; khiter_t k; if (h) { k = kh_get(mt, h, mid); if (k != kh_end(h)) { kh_del(mt, h, k); 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. */ mrb_value mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) { 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_sym(mrb_state *mrb, mrb_sym id) { const char *s; size_t len; s = mrb_sym2name_len(mrb, id, &len); if (len < 1 || !ISUPPER(*s)) { mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id)); } } 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); } } mrb_value mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) { mrb_value id; mrb_bool const_defined_p; id = get_sym_or_str_arg(mrb); if (mrb_type(id) == MRB_TT_SYMBOL) { check_const_name_sym(mrb, mrb_symbol(id)); const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(id)); } else { mrb_value sym; check_const_name_str(mrb, id); sym = mrb_check_intern_str(mrb, id); if (mrb_nil_p(sym)) { const_defined_p = FALSE; } else { const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(sym)); } } return mrb_bool_value(const_defined_p); } 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); } 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; } 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; } mrb_value mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); 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); } void mrb_init_class(mrb_state *mrb) { struct RClass *bob; /* BasicObject */ struct RClass *obj; /* Object */ struct RClass *mod; /* Module */ struct RClass *cls; /* Class */ //struct RClass *krn; /* Kernel */ /* 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 */ mrb_name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject")); mrb_name_class(mrb, obj, mrb_intern_lit(mrb, "Object")); mrb_name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); mrb_name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); 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_ANY()); 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, "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, "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, "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, "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, "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_REQ(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_NONE()); /* 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-0.0.0~20131214+git882afdea/src/codegen.c000066400000000000000000002067731225163307400202440ustar00rootroot00000000000000/* ** codegen.c - mruby code generator ** ** See Copyright Notice in mruby.h */ #include #include #include #include "mruby.h" #include "mruby/compile.h" #include "mruby/proc.h" #include "mruby/numeric.h" #include "mruby/string.h" #include "mruby/debug.h" #include "node.h" #include "opcode.h" #include "re.h" typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; enum looptype { LOOP_NORMAL, LOOP_BLOCK, LOOP_FOR, LOOP_BEGIN, LOOP_RESCUE, } type; 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; jmp_buf 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; int nlocals; int 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 *node, 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 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; } #ifdef ENABLE_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 longjmp(s->jmp, 1); } 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 void 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; } s->pc++; } #define NOVAL 0 #define VAL 1 static void genop_peep(codegen_scope *s, mrb_code i, int val) { /* peephole optimization */ if (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; } if (val) break; switch (c0) { case OP_MOVE: if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0) && GETARG_A(i) >= s->nlocals) { /* skip swapping OP_MOVE */ return; } if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { s->iseq[s->pc-1] = MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)); return; } 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; } 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; } 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; } 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; } 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; } 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; } } 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; } } break; case OP_EPOP: if (c0 == OP_EPOP) { s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); return; } break; case OP_POPERR: if (c0 == OP_POPERR) { s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); return; } break; case OP_RETURN: switch (c0) { case OP_RETURN: return; case OP_MOVE: s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); return; 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]; genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); return; #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; } case OP_STRCAT: if (c0 == OP_STRING) { int i = GETARG_Bx(i0); if (mrb_type(s->irep->pool[i]) == MRB_TT_STRING && RSTRING_LEN(s->irep->pool[i]) == 0) { s->pc--; return; } } break; default: break; } } 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: #ifdef ENABLE_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; } #define push() push_(s) #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_type(*pv) != MRB_TT_FIXNUM) 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; 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, tree->car); lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); // generate loop variable n2 = tree->car; if (n2->car && !n2->car->cdr && !n2->cdr) { genop(s, MKOP_Ax(OP_ENTER, 0x40000)); gen_assignment(s, n2->car->car, 1, NOVAL); } else { genop(s, MKOP_Ax(OP_ENTER, 0x40000)); 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); 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) { codegen_scope *scope = scope_new(s->mrb, s, tree->car); codegen(scope, tree->cdr, VAL); if (!s->iseq) { genop(scope, MKOP_A(OP_STOP, 0)); } else { if (scope->nregs == 0) { genop(scope, MKOP_A(OP_LOADNIL, 0)); genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); } else { pop(); genop_peep(scope, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); push(); } } 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; size_t len; char *name2; name = mrb_sym2name_len(s->mrb, a, &len); name2 = (char *)codegen_palloc(s, len+1); memcpy(name2, name, 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(); } pop_n(n+1); { size_t len; const char *name = mrb_sym2name_len(s->mrb, sym, &len); if (!noop && len == 1 && name[0] == '+') { genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); } else if (!noop && len == 1 && name[0] == '-') { genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); } else if (!noop && len == 1 && name[0] == '*') { genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); } else if (!noop && len == 1 && name[0] == '/') { genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); } else if (!noop && len == 1 && name[0] == '<') { genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); } else if (!noop && len == 2 && name[0] == '<' && name[1] == '=') { genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); } else if (!noop && len == 1 && name[0] == '>') { genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); } else if (!noop && len == 2 && name[0] == '>' && name[1] == '=') { genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); } else if (!noop && len == 2 && name[0] == '=' && name[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 *node, int sp, int val) { int idx; int type = (intptr_t)node->car; node = node->cdr; switch ((intptr_t)type) { case NODE_GVAR: idx = new_sym(s, sym(node)); genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); break; case NODE_LVAR: idx = lv_idx(s, sym(node)); 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(node)); 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(node)); genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); break; case NODE_CVAR: idx = new_sym(s, sym(node)); genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); break; case NODE_CONST: idx = new_sym(s, sym(node)); genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); break; case NODE_COLON2: idx = new_sym(s, sym(node->cdr)); genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); push(); codegen(s, node->car, VAL); pop_n(2); genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); break; case NODE_CALL: push(); gen_call(s, node, attrsym(s, sym(node->cdr->car)), sp, NOVAL); pop(); if (val) { genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); } break; default: #ifdef ENABLE_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)); push(); } pop(); 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++; } } } else { pop(); } } 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, int 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 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) return; if (s->irep && s->pc > 0 && 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 = new_label(s); 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 = new_label(s); 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(); genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); tmp = new_label(s); genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); pos2 = tmp; if (n4) { n4 = n4->cdr; } } while (n4); pos1 = new_label(s); 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 = new_label(s); 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); 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; codegen(s, tree->car, VAL); pop(); pos1 = new_label(s); genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); codegen(s, tree->cdr->car, val); if (val && !(tree->cdr->car)) { genop(s, MKOP_A(OP_LOADNIL, cursp())); push(); } if (e) { if (val) pop(); pos2 = new_label(s); genop(s, MKOP_sBx(OP_JMP, 0)); dispatch(s, pos1); codegen(s, e, val); dispatch(s, pos2); } else { if (val) { pop(); pos2 = new_label(s); 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); pos = new_label(s); pop(); 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); pos = new_label(s); pop(); 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 = new_label(s); 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 = new_label(s); 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(); genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); } else { pop(); } tmp = new_label(s); genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); pos2 = tmp; n = n->cdr; } if (tree->car->car) { pos1 = new_label(s); genop(s, MKOP_sBx(OP_JMP, 0)); dispatch_linked(s, pos2); } codegen(s, tree->car->cdr, val); if (val) pop(); tmp = new_label(s); genop(s, MKOP_sBx(OP_JMP, pos3)); pos3 = tmp; if (pos1) dispatch(s, pos1); tree = tree->cdr; } if (val) { genop(s, MKOP_A(OP_LOADNIL, cursp())); push(); } if (pos3) dispatch_linked(s, pos3); } break; case NODE_SCOPE: scope_body(s, tree); 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(), 0)); 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(), 1)); 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; while (tree) { codegen(s, tree->car->car, val); codegen(s, tree->car->cdr, val); len++; tree = tree->cdr; } if (val) { pop_n(len*2); genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); 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 = 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); } } break; case NODE_OP_ASGN: { mrb_sym sym = sym(tree->cdr->car); size_t 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 = new_label(s); genop(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0)); 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); 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())); } 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))); if (tree && tree->cdr) { push(); codegen(s, tree->cdr, VAL); pop_n(2); } 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(s, MKOP_AB(OP_MOVE, cursp(), idx)); } 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: { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); push(); } break; case NODE_IVAR: { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); push(); } break; case NODE_CVAR: { 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)); push(); } break; case NODE_DEFINED: codegen(s, tree, VAL); break; case NODE_BACK_REF: { 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: { 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_buf_cat(mrb, str, "$", 1); mrb_str_buf_append(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; int 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; int 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; 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_XSTR: if (val) { 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)); 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)); mrb_gc_arena_restore(s->mrb, ai); push(); } break; case NODE_REGX: if (val) { char *p1 = (char*)tree->car; char *p2 = (char*)tree->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(s->mrb, p1, strlen(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) { push(); off = new_lit(s, mrb_str_new(s->mrb, p2, strlen(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(); } 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(s->mrb, p, strlen(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; int off; push(); off = new_lit(s, mrb_str_new(s->mrb, p2, strlen(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); 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); 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); 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)); 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 0; *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; 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 = 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->type == LOOP_BEGIN) { genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); loop = loop->prev; } while (loop->type == LOOP_RESCUE) { loop = loop->prev; } 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 = new_label(s); 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(); } static void codedump(mrb_state *mrb, mrb_irep *irep) { #ifdef ENABLE_STDIO uint32_t i; int ai; mrb_code c; if (!irep) return; printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", irep, irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen); for (i=0; iilen; i++) { ai = mrb_gc_arena_save(mrb); 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\n", GETARG_A(c), GETARG_B(c)); break; case OP_LOADL: printf("OP_LOADL\tR%d\tL(%d)\n", GETARG_A(c), GETARG_Bx(c)); break; case OP_LOADI: printf("OP_LOADI\tR%d\t%d\n", GETARG_A(c), GETARG_sBx(c)); break; case OP_LOADSYM: printf("OP_LOADSYM\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_LOADNIL: printf("OP_LOADNIL\tR%d\n", GETARG_A(c)); break; case OP_LOADSELF: printf("OP_LOADSELF\tR%d\n", GETARG_A(c)); break; case OP_LOADT: printf("OP_LOADT\tR%d\n", GETARG_A(c)); break; case OP_LOADF: printf("OP_LOADF\tR%d\n", GETARG_A(c)); break; case OP_GETGLOBAL: printf("OP_GETGLOBAL\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_SETGLOBAL: printf("OP_SETGLOBAL\t:%s\tR%d\n", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); break; case OP_GETCONST: printf("OP_GETCONST\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_SETCONST: printf("OP_SETCONST\t:%s\tR%d\n", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); break; case OP_GETMCNST: printf("OP_GETMCNST\tR%d\tR%d::%s\n", GETARG_A(c), GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_SETMCNST: printf("OP_SETMCNST\tR%d::%s\tR%d\n", GETARG_A(c)+1, mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); break; case OP_GETIV: printf("OP_GETIV\tR%d\t%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_SETIV: printf("OP_SETIV\t%s\tR%d\n", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); break; case OP_GETUPVAR: printf("OP_GETUPVAR\tR%d\t%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_SETUPVAR: printf("OP_SETUPVAR\tR%d\t%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_GETCV: printf("OP_GETCV\tR%d\t%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); break; case OP_SETCV: printf("OP_SETCV\t%s\tR%d\n", mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), GETARG_A(c)); break; case OP_JMP: printf("OP_JMP\t\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\n", GETARG_A(c), (GETARG_Bx(c)>>10)&0x3f, (GETARG_Bx(c)>>9)&0x1, (GETARG_Bx(c)>>4)&0x1f, (GETARG_Bx(c)>>0)&0xf); 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: printf("\n"); break; case OP_R_RETURN: printf("\treturn\n"); break; case OP_R_BREAK: printf("\tbreak\n"); break; default: printf("\tbroken\n"); break; break; } break; case OP_BLKPUSH: printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d\n", GETARG_A(c), (GETARG_Bx(c)>>10)&0x3f, (GETARG_Bx(c)>>9)&0x1, (GETARG_Bx(c)>>4)&0x1f, (GETARG_Bx(c)>>0)&0xf); break; case OP_LAMBDA: printf("OP_LAMBDA\tR%d\tI(%+d)\t%d\n", GETARG_A(c), GETARG_b(c), GETARG_c(c)); break; case OP_RANGE: printf("OP_RANGE\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_METHOD: printf("OP_METHOD\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); 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\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\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_ARYCAT: printf("OP_ARYCAT\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c)); break; case OP_ARYPUSH: printf("OP_ARYPUSH\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c)); break; case OP_AREF: printf("OP_AREF\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_APOST: printf("OP_APOST\tR%d\t%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); 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\t%s\n", GETARG_A(c), RSTRING_PTR(s)); } break; case OP_STRCAT: printf("OP_STRCAT\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c)); break; case OP_HASH: printf("OP_HASH\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c)); break; case OP_OCLASS: printf("OP_OCLASS\tR%d\n", GETARG_A(c)); break; case OP_CLASS: printf("OP_CLASS\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); break; case OP_MODULE: printf("OP_MODULE\tR%d\t:%s\n", GETARG_A(c), mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); break; case OP_EXEC: printf("OP_EXEC\tR%d\tI(%+d)\n", GETARG_A(c), GETARG_Bx(c)); break; case OP_SCLASS: printf("OP_SCLASS\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c)); break; case OP_TCLASS: printf("OP_TCLASS\tR%d\n", GETARG_A(c)); break; case OP_ERR: printf("OP_ERR\tL(%d)\n", GETARG_Bx(c)); break; case OP_EPUSH: printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)); break; case OP_ONERR: printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c)); break; case OP_RESCUE: printf("OP_RESCUE\tR%d\n", GETARG_A(c)); break; case OP_RAISE: printf("OP_RAISE\tR%d\n", GETARG_A(c)); break; case OP_POPERR: printf("OP_POPERR\t%d\n", GETARG_A(c)); 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 codedump_all(mrb_state *mrb, struct RProc *proc) { return codedump_recur(mrb, proc->body.irep); } 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; if (setjmp(scope->jmp) == 0) { // 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; } else { if (scope->filename == scope->irep->filename) { scope->irep->filename = NULL; } mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); return NULL; } } mruby-0.0.0~20131214+git882afdea/src/compar.c000066400000000000000000000002661225163307400201060ustar00rootroot00000000000000/* ** compar.c - Comparable module ** ** See Copyright Notice in mruby.h */ #include "mruby.h" void mrb_init_comparable(mrb_state *mrb) { mrb_define_module(mrb, "Comparable"); } mruby-0.0.0~20131214+git882afdea/src/crc.c000066400000000000000000000014631225163307400173740ustar00rootroot00000000000000/* ** 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-0.0.0~20131214+git882afdea/src/debug.c000066400000000000000000000134471225163307400177200ustar00rootroot00000000000000#include #include "mruby.h" #include "mruby/irep.h" #include "mruby/debug.h" 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; } 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; } 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->line_ary[pc - f->start_pos]; case mrb_debug_line_flat_map: { // get upper bound mrb_irep_debug_info_line *ret = f->line_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->line_flat_map <= ret && ret < (f->line_flat_map + f->line_entry_count)); // check pc range mrb_assert(ret->start_pos <= pc && pc < (((ret + 1 - f->line_flat_map) < f->line_entry_count) ? (ret+1)->start_pos : irep->debug_info->pc_count)); return ret->line; } } } } return -1; } 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_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; size_t 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->line_ptr = NULL; switch(ret->line_type) { case mrb_debug_line_ary: ret->line_entry_count = file_pc_count; ret->line_ary = mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count); for(i = 0; i < file_pc_count; ++i) { ret->line_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->line_flat_map = 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->line_flat_map = mrb_realloc( mrb, ret->line_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->line_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; } 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]->line_ptr); mrb_free(mrb, d->files[i]); } mrb_free(mrb, d->files); mrb_free(mrb, d); } mruby-0.0.0~20131214+git882afdea/src/dump.c000066400000000000000000000504211225163307400175700ustar00rootroot00000000000000/* ** dump.c - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #include #include "mruby/dump.h" #include #include "mruby/string.h" #include "mruby/irep.h" #include "mruby/numeric.h" #include "mruby/debug.h" static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep); static uint32_t get_irep_header_size(mrb_state *mrb) { uint32_t size = 0; size += sizeof(uint32_t) * 1; size += sizeof(uint16_t) * 3; return size; } static size_t write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; cur += uint32_to_bin(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 uint32_t get_iseq_block_size(mrb_state *mrb, mrb_irep *irep) { uint32_t size = 0; size += sizeof(uint32_t); /* ilen */ size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */ return size; } static int write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; size_t iseq_no; cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */ } return (cur - buf); } static size_t get_pool_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size_t pool_no; int len; mrb_value str; char buf[32]; 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); size += RSTRING_LEN(str); break; case MRB_TT_FLOAT: len = mrb_float_to_str(buf, mrb_float(irep->pool[pool_no])); size += len; break; case MRB_TT_STRING: size += RSTRING_LEN(irep->pool[pool_no]); break; default: break; } mrb_gc_arena_restore(mrb, ai); } return size; } static int write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { size_t pool_no; uint8_t *cur = buf; size_t len; mrb_value str; const char *char_ptr; char char_buf[30]; 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); char_ptr = RSTRING_PTR(str); len = RSTRING_LEN(str); break; case MRB_TT_FLOAT: cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ len = mrb_float_to_str(char_buf, mrb_float(irep->pool[pool_no])); char_ptr = &char_buf[0]; break; case MRB_TT_STRING: cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ char_ptr = RSTRING_PTR(irep->pool[pool_no]); len = RSTRING_LEN(irep->pool[pool_no]); break; default: continue; } cur += uint16_to_bin(len, cur); /* data length */ memcpy(cur, char_ptr, len); cur += len; mrb_gc_arena_restore(mrb, ai); } return (int)(cur - buf); } static size_t get_syms_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size_t sym_no; size_t 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 int write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { size_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) { size_t len; name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len); if (len > UINT16_MAX) { return MRB_DUMP_GENERAL_FAILURE; } 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 (int)(cur - buf); } static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) { uint32_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) { uint32_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, uint32_t *irep_record_size) { size_t i; 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; } memset(bin, 0, *irep_record_size); bin += write_irep_header(mrb, irep, bin); bin += write_iseq_block(mrb, irep, bin); bin += write_pool_block(mrb, irep, bin); bin += write_syms_block(mrb, irep, bin); for (i = 0; i < irep->rlen; i++) { int result; uint32_t rlen; result = write_irep_record(mrb, irep->reps[i], bin, &rlen); if (result != MRB_DUMP_OK) { return result; } *irep_record_size += rlen; bin += rlen; } return MRB_DUMP_OK; } static size_t write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; memcpy(footer.section_identify, RITE_BINARY_EOF, sizeof(footer.section_identify)); 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, uint32_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; memcpy(header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(header->section_identify)); uint32_to_bin(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) { int result; uint32_t section_size = 0, 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_irep_header); section_size += sizeof(struct rite_section_irep_header); result = write_irep_record(mrb, irep, cur, &rlen); if (result != MRB_DUMP_OK) { return result; } cur += rlen; section_size += rlen; write_section_irep_header(mrb, section_size, bin); return MRB_DUMP_OK; } static int write_section_lineno_header(mrb_state *mrb, uint32_t section_size, uint8_t *bin) { struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin; // TODO memcpy(header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(header->section_identify)); uint32_to_bin(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 int write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) { uint8_t *cur = bin; size_t filename_len = 0, iseq_no; cur += sizeof(uint32_t); /* record size */ if (irep->filename) { filename_len = strlen(irep->filename); } cur += uint16_to_bin(filename_len, cur); /* filename size */ if (filename_len) { memcpy(cur, irep->filename, filename_len); cur += filename_len; /* filename */ } if (irep->lines) { cur += uint32_to_bin(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 */ } uint32_to_bin(cur - bin, bin); /* record size */ return (cur - bin); } static int write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) { size_t i; uint32_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) { uint32_t section_size = 0, 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; uint32_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) * file->line_entry_count; break; case mrb_debug_line_flat_map: ret += (sizeof(uint32_t) + sizeof(uint16_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, size_t ary_len, mrb_sym s) { size_t 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, size_t *lp) { mrb_sym *filenames = *fp; size_t tsize = 0; size_t file_i; size_t size = 0; mrb_irep_debug_info *di = irep->debug_info; if (lp == NULL) { lp = &tsize; } for (file_i = 0; file_i < di->flen; ++file_i) { mrb_irep_debug_info_file *file; size_t filename_len; size_t i; file = di->files[file_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) + filename_len; } for (i=0; irlen; i++) { size += get_filename_table_size(mrb, irep->reps[i], fp, lp); filenames = *fp; } } return size; } static int write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, size_t filenames_len) { uint8_t *cur; uint32_t f_idx; size_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(filename_idx != -1); cur += uint16_to_bin(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: { size_t l; for (l = 0; l < file->line_entry_count; ++l) { cur += uint16_to_bin(file->line_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->line_flat_map[line].start_pos, cur); cur += uint16_to_bin(file->line_flat_map[line].line, cur); } } break; default: mrb_assert(0); break; } } ret = cur - bin; uint32_to_bin(ret, bin); return ret; } static int write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, size_t filenames_len) { uint32_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 == (int)get_debug_record_size(mrb, irep)); return size; } static int write_filename_table(mrb_state *mrb, mrb_irep *irep, uint8_t **cp, mrb_sym **fp, size_t *lp) { uint8_t *cur = *cp; mrb_sym *filenames = *fp; size_t file_i; uint16_t fn_len; size_t size = 0; mrb_irep_debug_info *debug_info = irep->debug_info; for (file_i = 0; file_i < debug_info->flen; ++file_i) { mrb_irep_debug_info_file *file = debug_info->files[file_i]; if (find_filename_index(filenames, *lp, file->filename_sym) != -1) continue; // register filename *lp += 1; *fp = filenames = (mrb_sym*)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp)); filenames[*lp - 1] = file->filename_sym; // filename fn_len = (uint16_t)strlen(file->filename); cur += uint16_to_bin(fn_len, cur); memcpy(cur, file->filename, fn_len); cur += fn_len; size += sizeof(uint16_t) + fn_len; } *cp = cur; return size; } static int write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur) { uint32_t section_size = 0; const uint8_t *bin = cur; struct rite_section_debug_header *header; mrb_sym *filenames; size_t filenames_len = 0, i; uint8_t *filenames_len_out; uint32_t dlen; 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 filenames = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * 1); filenames_len_out = cur; cur += sizeof(uint16_t); section_size += sizeof(uint16_t); section_size += write_filename_table(mrb, irep, &cur, &filenames, &filenames_len); for (i=0; irlen; i++) { section_size += write_filename_table(mrb, irep->reps[i], &cur, &filenames, &filenames_len); } uint16_to_bin(filenames_len, filenames_len_out); // debug records dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; memcpy(header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(header->section_identify)); uint32_to_bin(section_size, header->section_size); mrb_free(mrb, filenames); return MRB_DUMP_OK; } static int write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin) { struct rite_binary_header *header = (struct rite_binary_header *)bin; uint16_t crc; size_t offset; memcpy(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)); 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)); uint32_to_bin(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 0; for (i=0; irlen; i++) { if (!is_debug_info_defined(irep->reps[i])) return 0; } return 1; } static int dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size) { int result = MRB_DUMP_GENERAL_FAILURE; size_t section_irep_size; size_t section_lineno_size = 0; uint8_t *cur = NULL; mrb_bool const debug_info_defined = is_debug_info_defined(irep); 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 (debug_info) { if (debug_info_defined) { mrb_sym *filenames; 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, NULL); mrb_free(mrb, filenames); 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); } } *bin_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + sizeof(struct rite_binary_footer); cur = *bin = (uint8_t*)mrb_malloc(mrb, *bin_size); if (cur == NULL) { goto error_exit; } cur += sizeof(struct rite_binary_header); result = write_section_irep(mrb, irep, cur); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_irep_size; /* write DEBUG section */ if (debug_info) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur); } else { result = write_section_lineno(mrb, irep, cur); } if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lineno_size; } write_footer(mrb, cur); write_rite_binary_header(mrb, *bin_size, *bin); error_exit: if (result != MRB_DUMP_OK) { mrb_free(mrb, *bin); *bin = NULL; } return result; } #ifdef ENABLE_STDIO int mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, 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, debug_info, &bin, &bin_size); if (result == MRB_DUMP_OK) { fwrite(bin, bin_size, 1, fp); } mrb_free(mrb, bin); return result; } static int is_valid_c_symbol_name(const char *name) { const char *c = NULL; if (name == NULL || name[0] == '\0') return 0; if (!ISALPHA(name[0]) && name[0] != '_') return 0; c = &name[1]; for (; *c != '\0'; ++c) { if (!ISALNUM(*c) && *c != '_') return 0; } return 1; } int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE *fp, const char *initname) { uint8_t *bin = NULL; size_t bin_size = 0, bin_idx = 0; int result; if (fp == NULL || initname == NULL || !is_valid_c_symbol_name(initname)) { return MRB_DUMP_INVALID_ARGUMENT; } result = dump_irep(mrb, irep, debug_info, &bin, &bin_size); if (result == MRB_DUMP_OK) { fprintf(fp, "#include \n"); // for uint8_t under at least Darwin fprintf(fp, "const uint8_t %s[] = {", initname); while (bin_idx < bin_size) { if (bin_idx % 16 == 0) fputs("\n", fp); fprintf(fp, "0x%02x,", bin[bin_idx++]); } fputs("\n};\n", fp); } mrb_free(mrb, bin); return result; } #endif /* ENABLE_STDIO */ mruby-0.0.0~20131214+git882afdea/src/enum.c000066400000000000000000000002651225163307400175700ustar00rootroot00000000000000/* ** enum.c - Enumerable module ** ** See Copyright Notice in mruby.h */ #include "mruby.h" void mrb_init_enumerable(mrb_state *mrb) { mrb_define_module(mrb, "Enumerable"); } mruby-0.0.0~20131214+git882afdea/src/error.c000066400000000000000000000260111225163307400177520ustar00rootroot00000000000000/* ** error.c - Exception class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/irep.h" #include "mruby/proc.h" #include "mruby/string.h" #include "mruby/variable.h" #include "mruby/debug.h" #include "error.h" mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len) { return mrb_funcall(mrb, mrb_obj_value(c), "new", 1, mrb_str_new(mrb, ptr, len)); } mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) { str = mrb_str_to_str(mrb, str); return mrb_funcall(mrb, mrb_obj_value(c), "new", 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")); if (mrb_nil_p(mesg)) return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); 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 * * Return this exception's class name an message */ static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, mesg, file, line; 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")); if (!mrb_nil_p(file) && !mrb_nil_p(line)) { str = file; mrb_str_cat(mrb, str, ":", 1); mrb_str_append(mrb, str, line); mrb_str_cat(mrb, str, ": ", 2); if (!mrb_nil_p(mesg) && RSTRING_LEN(mesg) > 0) { mrb_str_append(mrb, str, mesg); mrb_str_cat(mrb, str, " (", 2); } mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); if (!mrb_nil_p(mesg) && RSTRING_LEN(mesg) > 0) { mrb_str_cat(mrb, str, ")", 1); } } else { str = mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); if (!mrb_nil_p(mesg) && RSTRING_LEN(mesg) > 0) { mrb_str_cat(mrb, str, ": ", 2); mrb_str_append(mrb, str, mesg); } else { mrb_str_cat(mrb, str, ": ", 2); mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); } } return str; } static mrb_value exc_equal(mrb_state *mrb, mrb_value exc) { mrb_value obj; mrb_value mesg; mrb_bool equal_p; mrb_sym id_mesg = mrb_intern_lit(mrb, "mesg"); mrb_get_args(mrb, "o", &obj); if (mrb_obj_equal(mrb, exc, obj)) { equal_p = 1; } else { if (mrb_obj_class(mrb, exc) != mrb_obj_class(mrb, obj)) { if (mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "message"))) { mesg = mrb_funcall(mrb, obj, "message", 0); } else return mrb_false_value(); } else { mesg = mrb_attr_get(mrb, obj, id_mesg); } equal_p = mrb_equal(mrb, mrb_attr_get(mrb, exc, id_mesg), mesg); } return mrb_bool_value(equal_p); } 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(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, err - irep->iseq); char const* file = mrb_debug_get_filename(irep, 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--; } } void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { mrb->exc = mrb_obj_ptr(exc); exc_debug_info(mrb, mrb->exc); if (!mrb->jmp) { mrb_p(mrb, exc); abort(); } mrb_longjmp(mrb); } 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_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_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; } 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)); } 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); } void mrb_warn(mrb_state *mrb, const char *fmt, ...) { #ifdef ENABLE_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 } void mrb_bug(mrb_state *mrb, const char *fmt, ...) { #ifdef ENABLE_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); } int sysexit_status(mrb_state *mrb, mrb_value err) { mrb_value st = mrb_iv_get(mrb, err, mrb_intern_lit(mrb, "status")); return mrb_fixnum(st); } static void set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt) { mrb_funcall(mrb, info, "set_backtrace", 1, bt); } mrb_value make_exception(mrb_state *mrb, int argc, mrb_value *argv, int 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_value mrb_make_exception(mrb_state *mrb, int argc, mrb_value *argv) { return make_exception(mrb, argc, argv, TRUE); } 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_value mrb_get_backtrace(mrb_state*, mrb_value); void mrb_init_exception(mrb_state *mrb) { struct RClass *e; mrb->eException_class = e = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ mrb_define_class_method(mrb, e, "exception", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_method(mrb, e, "exception", exc_exception, MRB_ARGS_ANY()); mrb_define_method(mrb, e, "initialize", exc_initialize, MRB_ARGS_ANY()); mrb_define_method(mrb, e, "==", exc_equal, MRB_ARGS_REQ(1)); mrb_define_method(mrb, e, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, e, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, e, "inspect", exc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, e, "backtrace", mrb_get_backtrace, MRB_ARGS_NONE()); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ e = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", e); /* 15.2.38 */ } mruby-0.0.0~20131214+git882afdea/src/error.h000066400000000000000000000011731225163307400177610ustar00rootroot00000000000000/* ** error.h - Exception class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ERROR_H #define MRUBY_ERROR_H void mrb_sys_fail(mrb_state *mrb, const char *mesg); int sysexit_status(mrb_state *mrb, mrb_value err); mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str); mrb_value make_exception(mrb_state *mrb, int argc, mrb_value *argv, int isstr); mrb_value mrb_make_exception(mrb_state *mrb, int argc, mrb_value *argv); mrb_value mrb_format(mrb_state *mrb, const char *format, ...); void mrb_exc_print(mrb_state *mrb, struct RObject *exc); void mrb_longjmp(mrb_state *mrb); #endif /* MRUBY_ERROR_H */ mruby-0.0.0~20131214+git882afdea/src/etc.c000066400000000000000000000110071225163307400173730ustar00rootroot00000000000000/* ** etc.c - ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/string.h" #include "error.h" #include "mruby/numeric.h" #include "mruby/data.h" #include "mruby/class.h" 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 = (mrb_data_type*) type; return data; } void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_special_const_p(obj) || (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)); } } } void * mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_special_const_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { return NULL; } if (DATA_TYPE(obj) != type) { return NULL; } return DATA_PTR(obj); } 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_value mrb_lastline_get(mrb_state *mrb) { mrb_value *argv; int argc; mrb_get_args(mrb, "*", &argv, &argc); if (argc < 1) { return mrb_nil_value(); } else { return argv[0]; } } /* ------------------------------------------------ */ /* * Calls func(obj, arg, recursive), where recursive is non-zero if the * current method is called recursively on obj */ mrb_value mrb_exec_recursive(mrb_state *mrb, mrb_value (*func) (mrb_state *, mrb_value, mrb_value, int), mrb_value obj, void *arg) { return func(mrb, obj, *(mrb_value*)arg, 0); } mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { mrb_value tmp; mrb_sym id; switch (mrb_type(name)) { default: tmp = mrb_check_string_type(mrb, name); if (mrb_nil_p(tmp)) { tmp = mrb_inspect(mrb, name); mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); } name = tmp; /* fall through */ case MRB_TT_STRING: name = mrb_str_intern(mrb, name); /* fall through */ case MRB_TT_SYMBOL: return mrb_symbol(name); } return id; } /* * call-seq: * proc { |...| block } -> a_proc * * Equivalent to Proc.new. */ mrb_value mrb_block_proc(void) { return mrb_nil_value(); } static mrb_int 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_int mrb_obj_id(mrb_value obj) { mrb_int tt = mrb_type(obj); #define MakeID2(p,t) (((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(float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT); case MRB_TT_FLOAT: return MakeID(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_value mrb_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_value mrb_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_value mrb_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 */ mruby-0.0.0~20131214+git882afdea/src/ext/000077500000000000000000000000001225163307400172555ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/src/ext/.gitkeep000066400000000000000000000000001225163307400206740ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/src/gc.c000066400000000000000000001056001225163307400172140ustar00rootroot00000000000000/* ** gc.c - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #ifndef SIZE_MAX /* Some versions of VC++ * has SIZE_MAX in stdint.h */ # include #endif #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/hash.h" #include "mruby/proc.h" #include "mruby/range.h" #include "mruby/string.h" #include "mruby/variable.h" #include "mruby/gc.h" /* = 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 fassion: 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 paint them as White-B, just switch the meaning of White-A and White-B would 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 write a write barrier when writing a pointer to an object on object's field. Two different write barrier are available: * mrb_field_write_barrier * mrb_write_barrier == 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 paint them to White. The key idea are still same as the 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 to a "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; } 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(mrb));\ 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", mrb->gc_state);\ fprintf(stderr, "live: %zu\n", mrb->live);\ fprintf(stderr, "majorgc_old_threshold: %zu\n", mrb->majorgc_old_threshold);\ fprintf(stderr, "gc_threshold: %zu\n", mrb->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 #define GC_STEP_SIZE 1024 void* mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = (mrb->allocf)(mrb, p, len, mrb->ud); if (!p2 && len > 0 && mrb->heaps) { mrb_full_gc(mrb); p2 = (mrb->allocf)(mrb, p, len, mrb->ud); } return p2; } 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->out_of_memory) { /* mrb_panic(mrb); */ } else { mrb->out_of_memory = TRUE; mrb_raise(mrb, E_RUNTIME_ERROR, "Out of memory"); } } else { mrb->out_of_memory = FALSE; } return p2; } void* mrb_malloc(mrb_state *mrb, size_t len) { return mrb_realloc(mrb, 0, len); } void* mrb_malloc_simple(mrb_state *mrb, size_t len) { return mrb_realloc_simple(mrb, 0, len); } 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_realloc(mrb, 0, size); if (p) { memset(p, 0, size); } } else { p = NULL; } return p; } void mrb_free(mrb_state *mrb, void *p) { (mrb->allocf)(mrb, p, 0, mrb->ud); } #ifndef MRB_HEAP_PAGE_SIZE #define MRB_HEAP_PAGE_SIZE 1024 #endif struct heap_page { struct RBasic *freelist; struct heap_page *prev; struct heap_page *next; struct heap_page *free_next; struct heap_page *free_prev; mrb_bool old:1; RVALUE objects[MRB_HEAP_PAGE_SIZE]; }; static void link_heap_page(mrb_state *mrb, struct heap_page *page) { page->next = mrb->heaps; if (mrb->heaps) mrb->heaps->prev = page; mrb->heaps = page; } static void unlink_heap_page(mrb_state *mrb, struct heap_page *page) { if (page->prev) page->prev->next = page->next; if (page->next) page->next->prev = page->prev; if (mrb->heaps == page) mrb->heaps = page->next; page->prev = NULL; page->next = NULL; } static void link_free_heap_page(mrb_state *mrb, struct heap_page *page) { page->free_next = mrb->free_heaps; if (mrb->free_heaps) { mrb->free_heaps->free_prev = page; } mrb->free_heaps = page; } static void unlink_free_heap_page(mrb_state *mrb, struct 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 (mrb->free_heaps == page) mrb->free_heaps = page->free_next; page->free_prev = NULL; page->free_next = NULL; } static void add_heap(mrb_state *mrb) { struct heap_page *page = (struct heap_page *)mrb_calloc(mrb, 1, sizeof(struct heap_page)); RVALUE *p, *e; struct RBasic *prev = NULL; for (p = page->objects, 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(mrb, page); link_free_heap_page(mrb, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 #define DEFAULT_MAJOR_GC_INC_RATIO 200 #define is_generational(mrb) ((mrb)->is_generational_gc_mode) #define is_major_gc(mrb) (is_generational(mrb) && (mrb)->gc_full) #define is_minor_gc(mrb) (is_generational(mrb) && !(mrb)->gc_full) void mrb_init_heap(mrb_state *mrb) { mrb->heaps = NULL; mrb->free_heaps = NULL; add_heap(mrb); mrb->gc_interval_ratio = DEFAULT_GC_INTERVAL_RATIO; mrb->gc_step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL mrb->is_generational_gc_mode = TRUE; mrb->gc_full = TRUE; #endif #ifdef GC_PROFILE program_invoke_time = gettimeofday_time(); #endif } static void obj_free(mrb_state *mrb, struct RBasic *obj); void mrb_free_heap(mrb_state *mrb) { struct heap_page *page = mrb->heaps; struct heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic); } mrb_free(mrb, tmp); } } static void gc_protect(mrb_state *mrb, struct RBasic *p) { if (mrb->arena_idx >= MRB_GC_ARENA_SIZE) { #ifdef MRB_GC_FIXED_ARENA /* arena overflow error */ mrb->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error"); #else /* extend arena */ mrb->arena_capa *= 1.5; mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*mrb->arena_capa); #endif } mrb->arena[mrb->arena_idx++] = p; } void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { if (mrb_special_const_p(obj)) return; gc_protect(mrb, mrb_basic_ptr(obj)); } 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 } } }; #ifdef MRB_GC_STRESS mrb_full_gc(mrb); #endif if (mrb->gc_threshold < mrb->live) { mrb_incremental_gc(mrb); } if (mrb->free_heaps == NULL) { add_heap(mrb); } p = mrb->free_heaps->freelist; mrb->free_heaps->freelist = ((struct free_obj*)p)->next; if (mrb->free_heaps->freelist == NULL) { unlink_free_heap_page(mrb, mrb->free_heaps); } mrb->live++; gc_protect(mrb, p); *(RVALUE *)p = RVALUE_zero; p->tt = ttype; p->c = cls; paint_partial_white(mrb, p); return p; } static inline void add_gray_list(mrb_state *mrb, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { abort(); } #endif paint_gray(obj); obj->gcnext = mrb->gray_list; mrb->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]); } } static void mark_context(mrb_state *mrb, struct mrb_context *c) { size_t i; size_t e; mrb_callinfo *ci; /* mark stack */ mark_context_stack(mrb, c); /* mark ensure stack */ e = (c->ci) ? c->ci->eidx : 0; for (i=0; iensure[i]); } /* mark closure */ for (ci = c->cibase; ci <= c->ci; ci++) { if (!ci) continue; 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); } if (c->prev && c->prev->fib) { mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib); } } static void gc_mark_children(mrb_state *mrb, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); mrb->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: 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: 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; if (e->cioff < 0) { int i, len; len = (int)e->flags; for (i=0; istack[i]); } } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; 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; } } 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, 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_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_ENV: { struct REnv *e = (struct REnv*)obj; if (e->cioff < 0) { mrb_free(mrb, e->stack); e->stack = NULL; } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c != mrb->root_c) mrb_free_context(mrb, c); } break; case MRB_TT_ARRAY: if (obj->flags & MRB_ARY_SHARED) 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) { size_t i, e; if (!is_minor_gc(mrb)) { mrb->gray_list = NULL; mrb->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ for (i=0,e=mrb->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_context(mrb, mrb->root_c); if (mrb->root_c != mrb->c) { mark_context(mrb, mrb->c); } } static size_t gc_gray_mark(mrb_state *mrb, struct RBasic *obj) { size_t children = 0; gc_mark_children(mrb, 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: 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; /* 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) { while (mrb->gray_list) { if (is_gray(mrb->gray_list)) gc_mark_children(mrb, mrb->gray_list); else mrb->gray_list = mrb->gray_list->gcnext; } } static size_t incremental_marking_phase(mrb_state *mrb, size_t limit) { size_t tried_marks = 0; while (mrb->gray_list && tried_marks < limit) { tried_marks += gc_gray_mark(mrb, mrb->gray_list); } return tried_marks; } static void final_marking_phase(mrb_state *mrb) { mark_context_stack(mrb, mrb->root_c); gc_mark_gray_list(mrb); mrb_assert(mrb->gray_list == NULL); mrb->gray_list = mrb->atomic_gray_list; mrb->atomic_gray_list = NULL; gc_mark_gray_list(mrb); mrb_assert(mrb->gray_list == NULL); } static void prepare_incremental_sweep(mrb_state *mrb) { mrb->gc_state = GC_STATE_SWEEP; mrb->sweeps = mrb->heaps; mrb->gc_live_after_mark = mrb->live; } static size_t incremental_sweep_phase(mrb_state *mrb, size_t limit) { struct heap_page *page = mrb->sweeps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { RVALUE *p = page->objects; RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; int dead_slot = 1; int full = (page->freelist == NULL); if (is_minor_gc(mrb) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = 0; } 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(mrb)) paint_partial_white(mrb, &p->as.basic); /* next gc target */ dead_slot = 0; } p++; } /* free dead slot */ if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) { struct heap_page *next = page->next; unlink_heap_page(mrb, page); unlink_free_heap_page(mrb, page); mrb_free(mrb, page); page = next; } else { if (full && freed > 0) { link_free_heap_page(mrb, page); } if (page->freelist == NULL && is_minor_gc(mrb)) page->old = TRUE; else page->old = FALSE; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; mrb->live -= freed; mrb->gc_live_after_mark -= freed; } mrb->sweeps = page; return tried_sweep; } static size_t incremental_gc(mrb_state *mrb, size_t limit) { switch (mrb->gc_state) { case GC_STATE_NONE: root_scan_phase(mrb); mrb->gc_state = GC_STATE_MARK; flip_white_part(mrb); return 0; case GC_STATE_MARK: if (mrb->gray_list) { return incremental_marking_phase(mrb, limit); } else { final_marking_phase(mrb); prepare_incremental_sweep(mrb); return 0; } case GC_STATE_SWEEP: { size_t tried_sweep = 0; tried_sweep = incremental_sweep_phase(mrb, limit); if (tried_sweep == 0) mrb->gc_state = GC_STATE_NONE; return tried_sweep; } default: /* unknown state */ mrb_assert(0); return 0; } } static void incremental_gc_until(mrb_state *mrb, enum gc_state to_state) { do { incremental_gc(mrb, ~0); } while (mrb->gc_state != to_state); } static void incremental_gc_step(mrb_state *mrb) { size_t limit = 0, result = 0; limit = (GC_STEP_SIZE/100) * mrb->gc_step_ratio; while (result < limit) { result += incremental_gc(mrb, limit); if (mrb->gc_state == GC_STATE_NONE) break; } mrb->gc_threshold = mrb->live + GC_STEP_SIZE; } static void clear_all_old(mrb_state *mrb) { size_t origin_mode = mrb->is_generational_gc_mode; mrb_assert(is_generational(mrb)); if (is_major_gc(mrb)) { /* finish the half baked GC */ incremental_gc_until(mrb, GC_STATE_NONE); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ mrb->is_generational_gc_mode = FALSE; prepare_incremental_sweep(mrb); incremental_gc_until(mrb, GC_STATE_NONE); mrb->is_generational_gc_mode = origin_mode; /* The gray objects has already been painted as white */ mrb->atomic_gray_list = mrb->gray_list = NULL; } void mrb_incremental_gc(mrb_state *mrb) { if (mrb->gc_disabled) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; if (is_minor_gc(mrb)) { incremental_gc_until(mrb, GC_STATE_NONE); } else { incremental_gc_step(mrb); } if (mrb->gc_state == GC_STATE_NONE) { mrb_assert(mrb->live >= mrb->gc_live_after_mark); mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; if (mrb->gc_threshold < GC_STEP_SIZE) { mrb->gc_threshold = GC_STEP_SIZE; } if (is_major_gc(mrb)) { mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; mrb->gc_full = FALSE; } else if (is_minor_gc(mrb)) { if (mrb->live > mrb->majorgc_old_threshold) { clear_all_old(mrb); mrb->gc_full = TRUE; } } } GC_TIME_STOP_AND_REPORT; } /* Perform a full gc cycle */ void mrb_full_gc(mrb_state *mrb) { if (mrb->gc_disabled) return; GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; if (is_generational(mrb)) { /* clear all the old objects back to young */ clear_all_old(mrb); mrb->gc_full = TRUE; } else if (mrb->gc_state != GC_STATE_NONE) { /* finish half baked GC cycle */ incremental_gc_until(mrb, GC_STATE_NONE); } incremental_gc_until(mrb, GC_STATE_NONE); mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; if (is_generational(mrb)) { mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; mrb->gc_full = FALSE; } GC_TIME_STOP_AND_REPORT; } void mrb_garbage_collect(mrb_state *mrb) { mrb_full_gc(mrb); } int mrb_gc_arena_save(mrb_state *mrb) { return mrb->arena_idx; } void mrb_gc_arena_restore(mrb_state *mrb, int idx) { #ifndef MRB_GC_FIXED_ARENA int capa = mrb->arena_capa; if (idx < capa / 2) { capa *= 0.66; if (capa < MRB_GC_ARENA_SIZE) { capa = MRB_GC_ARENA_SIZE; } if (capa != mrb->arena_capa) { mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*capa); mrb->arena_capa = capa; } } #endif mrb->arena_idx = idx; } /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). */ void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { if (!is_black(obj)) return; if (!is_white(value)) return; mrb_assert(!is_dead(mrb, value) && !is_dead(mrb, obj)); mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE); if (is_generational(mrb) || mrb->gc_state == GC_STATE_MARK) { add_gray_list(mrb, value); } else { mrb_assert(mrb->gc_state == GC_STATE_SWEEP); paint_partial_white(mrb, 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. */ void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { if (!is_black(obj)) return; mrb_assert(!is_dead(mrb, obj)); mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE); paint_gray(obj); obj->gcnext = mrb->atomic_gray_list; mrb->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) { int 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) { int 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_int enable) { if (is_generational(mrb) && !enable) { clear_all_old(mrb); mrb_assert(mrb->gc_state == GC_STATE_NONE); mrb->gc_full = FALSE; } else if (!is_generational(mrb) && enable) { incremental_gc_until(mrb, GC_STATE_NONE); mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; mrb->gc_full = FALSE; } mrb->is_generational_gc_mode = 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->is_generational_gc_mode); } /* * 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->is_generational_gc_mode != enable) change_gen_gc_mode(mrb, enable); return mrb_bool_value(enable); } void mrb_objspace_each_objects(mrb_state *mrb, each_object_callback* callback, void *data) { struct heap_page* page = mrb->heaps; while (page != NULL) { RVALUE *p, *pend; p = page->objects; pend = p + MRB_HEAP_PAGE_SIZE; for (;p < pend; p++) { (*callback)(mrb, &p->as.basic, data); } page = page->next; } } #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; puts("test_mrb_field_write_barrier"); mrb->is_generational_gc_mode = FALSE; obj = mrb_basic_ptr(mrb_ary_new(mrb)); value = mrb_basic_ptr(mrb_str_new_cstr(mrb, "value")); paint_black(obj); paint_partial_white(mrb,value); puts(" in GC_STATE_MARK"); mrb->gc_state = GC_STATE_MARK; mrb_field_write_barrier(mrb, obj, value); mrb_assert(is_gray(value)); puts(" in GC_STATE_SWEEP"); paint_partial_white(mrb,value); mrb->gc_state = GC_STATE_SWEEP; mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & mrb->current_white_part); mrb_assert(value->color & mrb->current_white_part); puts(" fail with black"); mrb->gc_state = GC_STATE_MARK; paint_white(obj); paint_partial_white(mrb,value); mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & mrb->current_white_part); puts(" fail with gray"); mrb->gc_state = 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_cstr(mrb, "value"); paint_black(obj); paint_partial_white(mrb, mrb_basic_ptr(value)); mrb->gc_state = 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; puts("test_mrb_write_barrier"); obj = mrb_basic_ptr(mrb_ary_new(mrb)); paint_black(obj); puts(" in GC_STATE_MARK"); mrb->gc_state = GC_STATE_MARK; mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); mrb_assert(mrb->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; puts("test_add_gray_list"); change_gen_gc_mode(mrb, FALSE); mrb_assert(mrb->gray_list == NULL); obj1 = mrb_basic_ptr(mrb_str_new_cstr(mrb, "test")); add_gray_list(mrb, obj1); mrb_assert(mrb->gray_list == obj1); mrb_assert(is_gray(obj1)); obj2 = mrb_basic_ptr(mrb_str_new_cstr(mrb, "test")); add_gray_list(mrb, obj2); mrb_assert(mrb->gray_list == obj2); mrb_assert(mrb->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; 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, 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_cstr(mrb, "test"); paint_gray(mrb_basic_ptr(obj_v)); paint_partial_white(mrb, mrb_basic_ptr(value_v)); mrb_ary_push(mrb, obj_v, value_v); gray_num = gc_gray_mark(mrb, 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; struct heap_page *page; puts("test_incremental_gc"); change_gen_gc_mode(mrb, FALSE); puts(" in mrb_full_gc"); mrb_full_gc(mrb); mrb_assert(mrb->gc_state == GC_STATE_NONE); puts(" in GC_STATE_NONE"); incremental_gc(mrb, max); mrb_assert(mrb->gc_state == GC_STATE_MARK); puts(" in GC_STATE_MARK"); incremental_gc_until(mrb, GC_STATE_SWEEP); mrb_assert(mrb->gc_state == GC_STATE_SWEEP); puts(" in GC_STATE_SWEEP"); page = mrb->heaps; while (page) { RVALUE *p = page->objects; RVALUE *e = p + MRB_HEAP_PAGE_SIZE; while (pas.basic)) { live++; } if (is_gray(&p->as.basic) && !is_dead(mrb, &p->as.basic)) { printf("%p\n", &p->as.basic); } p++; } page = page->next; total += MRB_HEAP_PAGE_SIZE; } mrb_assert(mrb->gray_list == NULL); incremental_gc(mrb, max); mrb_assert(mrb->gc_state == GC_STATE_SWEEP); incremental_gc(mrb, max); mrb_assert(mrb->gc_state == GC_STATE_NONE); free = (RVALUE*)mrb->heaps->freelist; while (free) { freed++; free = (RVALUE*)free->as.free.next; } mrb_assert(mrb->live == live); mrb_assert(mrb->live == total-freed); puts("test_incremental_gc(gen)"); incremental_gc_until(mrb, GC_STATE_SWEEP); change_gen_gc_mode(mrb, TRUE); mrb_assert(mrb->gc_full == FALSE); mrb_assert(mrb->gc_state == GC_STATE_NONE); puts(" in minor"); mrb_assert(is_minor_gc(mrb)); mrb_assert(mrb->majorgc_old_threshold > 0); mrb->majorgc_old_threshold = 0; mrb_incremental_gc(mrb); mrb_assert(mrb->gc_full == TRUE); mrb_assert(mrb->gc_state == GC_STATE_NONE); puts(" in major"); mrb_assert(is_major_gc(mrb)); do { mrb_incremental_gc(mrb); } while (mrb->gc_state != GC_STATE_NONE); mrb_assert(mrb->gc_full == FALSE); mrb_close(mrb); } void test_incremental_sweep_phase(void) { mrb_state *mrb = mrb_open(); puts("test_incremental_sweep_phase"); add_heap(mrb); mrb->sweeps = mrb->heaps; mrb_assert(mrb->heaps->next->next == NULL); mrb_assert(mrb->free_heaps->next->next == NULL); incremental_sweep_phase(mrb, MRB_HEAP_PAGE_SIZE*3); mrb_assert(mrb->heaps->next == NULL); mrb_assert(mrb->heaps == mrb->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 #endif mruby-0.0.0~20131214+git882afdea/src/hash.c000066400000000000000000001022261225163307400175470ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/hash.h" #include "mruby/khash.h" #include "mruby/string.h" #include "mruby/variable.h" static inline khint_t mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key) { khint_t h = (khint_t)mrb_type(key) << 24; mrb_value h2; h2 = mrb_funcall(mrb, key, "hash", 0, 0); h ^= h2.value.i; return h; } static inline khint_t mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b) { return mrb_eql(mrb, a, b); } KHASH_DECLARE(ht, mrb_value, mrb_value, 1) KHASH_DEFINE (ht, mrb_value, mrb_value, 1, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal) static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); static inline mrb_value mrb_hash_ht_key(mrb_state *mrb, mrb_value key) { if (mrb_string_p(key)) return mrb_str_dup(mrb, key); else return key; } #define KEY(key) mrb_hash_ht_key(mrb, key) void mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash) { khiter_t k; khash_t(ht) *h = hash->ht; 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); 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, hash->ht); } 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, h->ht, capa); } h->iv = 0; return mrb_obj_value(h); } mrb_value mrb_hash_new(mrb_state *mrb) { return mrb_hash_new_capa(mrb, 0); } 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, h, key); if (k != kh_end(h)) return kh_value(h, k); } /* not found */ if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); } return RHASH_IFNONE(hash); } 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, h, key); if (k != kh_end(h)) return kh_value(h, k); } /* not found */ return def; } void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) /* mrb_hash_aset */ { khash_t(ht) *h; khiter_t k; mrb_hash_modify(mrb, hash); h = RHASH_TBL(hash); if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb); k = kh_get(ht, h, key); if (k == kh_end(h)) { /* expand */ int ai = mrb_gc_arena_save(mrb); k = kh_put(ht, h, KEY(key)); mrb_gc_arena_restore(mrb, ai); } kh_value(h, k) = val; mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash)); return; } 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, 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_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash) { return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); } khash_t(ht) * mrb_hash_tbl(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (!h) { 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_core(mrb_state *mrb, mrb_value hash) { mrb_value block, ifnone; mrb_value *argv; int argc; mrb_get_args(mrb, "o*", &block, &argv, &argc); mrb_hash_modify(mrb, hash); if (mrb_nil_p(block)) { if (argc > 0) { if (argc != 1) mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); ifnone = argv[0]; } else { ifnone = mrb_nil_value(); } } else { if (argc > 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; ifnone = block; } mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); return hash; } /* * 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. Equivalent 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} */ static mrb_value to_hash(mrb_state *mrb, mrb_value hash) { return mrb_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); } /* * 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 */ /* 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 * */ 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); } /* * 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 * */ /* 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 *argv; int argc; mrb_value key; mrb_get_args(mrb, "*", &argv, &argc); if (MRB_RHASH_PROCDEFAULT_P(hash)) { if (argc == 0) return mrb_nil_value(); key = argv[0]; 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); 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); RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; return ifnone; } 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; if (h) { k = kh_get(ht, h, key); if (k != kh_end(h)) { delVal = kh_value(h, k); kh_del(ht, h, k); 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" * */ 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) { if (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_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); } else { return RHASH_IFNONE(hash); } } /* * 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} * */ /* * 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. */ /* * call-seq: * hsh.reject {| key, value | block } -> a_hash * * Same as Hash#delete_if, but works on (and returns) a * copy of the hsh. Equivalent to * hsh.dup.delete_if. * */ /* * 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} */ /* * 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. */ /* * 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. * */ /* 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_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (h) kh_clear(ht, 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} * */ 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.17 */ /* 15.2.13.4.23 */ /* * call-seq: * hsh.replace(other_hash) -> hsh * * Replaces the contents of hsh with the contents of * other_hash. * * h = { "a" => 100, "b" => 200 } * h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400} * */ static mrb_value mrb_hash_replace(mrb_state *mrb, mrb_value hash) { mrb_value hash2, ifnone; khash_t(ht) *h2; khiter_t k; mrb_get_args(mrb, "o", &hash2); hash2 = to_hash(mrb, hash2); if (mrb_obj_equal(mrb, hash, hash2)) return hash; mrb_hash_clear(mrb, hash); h2 = RHASH_TBL(hash2); if (h2) { for (k = kh_begin(h2); k != kh_end(h2); k++) { if (kh_exist(h2, k)) mrb_hash_set(mrb, hash, kh_key(h2, k), kh_value(h2, k)); } } if (MRB_RHASH_PROCDEFAULT_P(hash2)) { RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; ifnone = RHASH_PROCDEFAULT(hash2); } else { ifnone = RHASH_IFNONE(hash2); } mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); return hash; } /* 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_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(); } static mrb_value inspect_hash(mrb_state *mrb, mrb_value hash, int recur) { mrb_value str, str2; khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; if (recur) return mrb_str_new(mrb, "{...}", 5); str = mrb_str_new(mrb, "{", 1); if (h && kh_size(h) > 0) { for (k = kh_begin(h); k != kh_end(h); k++) { int ai; if (!kh_exist(h,k)) continue; ai = mrb_gc_arena_save(mrb); if (RSTRING_LEN(str) > 1) mrb_str_cat(mrb, str, ", ", 2); str2 = mrb_inspect(mrb, kh_key(h,k)); mrb_str_append(mrb, str, str2); mrb_str_buf_cat(mrb, str, "=>", 2); str2 = mrb_inspect(mrb, kh_value(h,k)); mrb_str_append(mrb, str, str2); mrb_gc_arena_restore(mrb, ai); } } mrb_str_buf_cat(mrb, str, "}", 1); return str; } /* 15.2.13.4.30 (x)*/ /* * call-seq: * hsh.to_s -> string * hsh.inspect -> string * * Return the contents of this hash as a string. * * h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 } * h.to_s #=> "{\"c\"=>300, \"a\"=>100, \"d\"=>400}" */ static mrb_value mrb_hash_inspect(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (!h || kh_size(h) == 0) return mrb_str_new(mrb, "{}", 2); return inspect_hash(mrb, hash, 0); } /* 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_value mrb_hash_keys(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_value v = kh_key(h,k); mrb_ary_push(mrb, ary, v); } } 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_value v = kh_value(h,k); mrb_ary_push(mrb, ary, v); } } return ary; } static mrb_value mrb_hash_has_keyWithKey(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, h, key); return mrb_bool_value(k != kh_end(h)); } return mrb_false_value(); } /* 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; mrb_get_args(mrb, "o", &key); return mrb_hash_has_keyWithKey(mrb, hash, key); } static mrb_value mrb_hash_has_valueWithvalue(mrb_state *mrb, mrb_value hash, mrb_value value) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; 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), value)) { return mrb_true_value(); } } } 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; mrb_get_args(mrb, "o", &val); return mrb_hash_has_valueWithvalue(mrb, hash, val); } static mrb_value hash_equal(mrb_state *mrb, mrb_value hash1, mrb_value hash2, int eql) { khash_t(ht) *h1, *h2; if (mrb_obj_equal(mrb, hash1, hash2)) return mrb_true_value(); if (!mrb_hash_p(hash2)) { if (!mrb_respond_to(mrb, hash2, mrb_intern_lit(mrb, "to_hash"))) { return mrb_false_value(); } if (eql) return mrb_fixnum_value(mrb_eql(mrb, hash2, hash1)); else return mrb_fixnum_value(mrb_equal(mrb, hash2, hash1)); } h1 = RHASH_TBL(hash1); h2 = RHASH_TBL(hash2); if (!h1) { return mrb_bool_value(!h2); } if (!h2) return mrb_false_value(); if (kh_size(h1) != kh_size(h2)) return mrb_false_value(); else { khiter_t k1, k2; mrb_value key; for (k1 = kh_begin(h1); k1 != kh_end(h1); k1++) { if (!kh_exist(h1, k1)) continue; key = kh_key(h1,k1); k2 = kh_get(ht, h2, key); if (k2 != kh_end(h2)) { if (mrb_equal(mrb, kh_value(h1,k1), kh_value(h2,k2))) { continue; /* next key */ } } return mrb_false_value(); } } return mrb_true_value(); } /* 15.2.13.4.1 */ /* * call-seq: * hsh == other_hash -> true or false * * 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. * * h1 = { "a" => 1, "c" => 2 } * h2 = { 7 => 35, "c" => 2, "a" => 1 } * h3 = { "a" => 1, "c" => 2, 7 => 35 } * h4 = { "a" => 1, "d" => 2, "f" => 35 } * h1 == h2 #=> false * h2 == h3 #=> true * h3 == h4 #=> false * */ static mrb_value mrb_hash_equal(mrb_state *mrb, mrb_value hash1) { mrb_value hash2; mrb_get_args(mrb, "o", &hash2); return hash_equal(mrb, hash1, hash2, FALSE); } /* 15.2.13.4.32 (x)*/ /* * call-seq: * hash.eql?(other) -> true or false * * Returns true if hash and other are * both hashes with the same content. */ static mrb_value mrb_hash_eql(mrb_state *mrb, mrb_value hash1) { mrb_value hash2; mrb_get_args(mrb, "o", &hash2); return hash_equal(mrb, hash1, hash2, TRUE); } /* * call-seq: * hsh.merge!(other_hash) -> hsh * hsh.update(other_hash) -> hsh * hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh * hsh.update(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} */ /* 15.2.13.4.22 */ /* * call-seq: * hsh.merge(other_hash) -> new_hash * hsh.merge(other_hash){|key, oldval, newval| block} -> new_hash * * Returns a new hash containing the contents of other_hash and * the contents of hsh. If no block is specified, the value for * entries with duplicate keys will be that of other_hash. Otherwise * the value for 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.merge(h2){|key, oldval, newval| newval - oldval} * #=> {"a"=>100, "b"=>54, "c"=>300} * h1 #=> {"a"=>100, "b"=>200} * */ /* * call-seq: * hash.assoc(obj) -> an_array or nil * * Searches through the hash comparing _obj_ with the key using ==. * Returns the key-value pair (two elements array) or +nil+ * if no match is found. See Array#assoc. * * h = {"colors" => ["red", "blue", "green"], * "letters" => ["a", "b", "c" ]} * h.assoc("letters") #=> ["letters", ["a", "b", "c"]] * h.assoc("foo") #=> nil */ mrb_value mrb_hash_assoc(mrb_state *mrb, mrb_value hash) { mrb_value key, value, has_key; mrb_get_args(mrb, "o", &key); if (mrb_nil_p(key)) mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); has_key = mrb_hash_has_keyWithKey(mrb, hash, key); if (mrb_test(has_key)) { value = mrb_hash_get(mrb, hash, key); return mrb_assoc_new(mrb, key, value); } else { return mrb_nil_value(); } } /* * call-seq: * hash.rassoc(key) -> an_array or nil * * Searches through the hash comparing _obj_ with the value using ==. * Returns the first key-value pair (two-element array) that matches. See * also Array#rassoc. * * a = {1=> "one", 2 => "two", 3 => "three", "ii" => "two"} * a.rassoc("two") #=> [2, "two"] * a.rassoc("four") #=> nil */ mrb_value mrb_hash_rassoc(mrb_state *mrb, mrb_value hash) { mrb_value key, value, has_key; mrb_get_args(mrb, "o", &key); has_key = mrb_hash_has_keyWithKey(mrb, hash, key); if (mrb_test(has_key)) { value = mrb_hash_get(mrb, hash, key); return mrb_assoc_new(mrb, value, key); } else { return mrb_nil_value(); } } /* * 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"] */ /* * A Hash is a collection of key-value pairs. It is * similar to an Array, except that indexing is done via * arbitrary keys of any object type, not an integer index. Hashes enumerate * their values in the order that the corresponding keys were inserted. * * Hashes have a default value that is returned when accessing * keys that do not exist in the hash. By default, that value is * nil. * */ void mrb_init_hash(mrb_state *mrb) { struct RClass *h; h = mrb->hash_class = mrb_define_class(mrb, "Hash", mrb->object_class); MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); mrb_include_module(mrb, h, mrb_class_get(mrb, "Enumerable")); mrb_define_method(mrb, h, "==", mrb_hash_equal, MRB_ARGS_REQ(1)); /* 15.2.13.4.1 */ 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, "__init_core", mrb_hash_init_core, MRB_ARGS_ANY()); /* core of 15.2.13.4.16 */ mrb_define_method(mrb, h, "initialize_copy", mrb_hash_replace, MRB_ARGS_REQ(1)); /* 15.2.13.4.17 */ 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, "replace", mrb_hash_replace, MRB_ARGS_REQ(1)); /* 15.2.13.4.23 */ mrb_define_method(mrb, h, "shift", mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */ 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)*/ mrb_define_method(mrb, h, "inspect", mrb_hash_inspect, MRB_ARGS_NONE()); /* 15.2.13.4.30 (x)*/ mrb_define_alias(mrb, h, "to_s", "inspect"); /* 15.2.13.4.31 (x)*/ mrb_define_method(mrb, h, "eql?", mrb_hash_eql, MRB_ARGS_REQ(1)); /* 15.2.13.4.32 (x)*/ } mruby-0.0.0~20131214+git882afdea/src/init.c000066400000000000000000000027011225163307400175640ustar00rootroot00000000000000/* ** init.c - initialize mruby core ** ** See Copyright Notice in mruby.h */ #include "mruby.h" 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_mrblib(mrb_state*); void mrb_init_mrbgems(mrb_state*); void mrb_final_mrbgems(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_mrblib(mrb); DONE; #ifndef DISABLE_GEMS mrb_init_mrbgems(mrb); DONE; #endif } void mrb_final_core(mrb_state *mrb) { #ifndef DISABLE_GEMS mrb_final_mrbgems(mrb); DONE; #endif } mruby-0.0.0~20131214+git882afdea/src/kernel.c000066400000000000000000001036201225163307400201030ustar00rootroot00000000000000/* ** kernel.c - Kernel module ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/proc.h" #include "mruby/string.h" #include "mruby/variable.h" #include "error.h" 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; 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 (me && 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_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_bool eql_p; mrb_get_args(mrb, "o", &arg); eql_p = mrb_obj_equal(mrb, self, arg); return mrb_bool_value(eql_p); } static mrb_value mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_bool eql_p; mrb_get_args(mrb, "o", &arg); eql_p = mrb_equal(mrb, self, arg); return mrb_bool_value(!eql_p); } /* 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_bool equal_p; mrb_get_args(mrb, "o", &arg); equal_p = mrb_equal(mrb, self, arg); return mrb_bool_value(equal_p); } /* 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.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" */ static mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_value block, *argv; int argc; mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); return mrb_funcall_with_block(mrb,self, name, argc, argv, block); } /* 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 = mrb->c->stbase + ci->stackidx + 1; ci--; if (ci <= mrb->c->cibase) { given_p = 0; } else { /* block_given? called within block; check upper scope */ if (ci->proc->env && ci->proc->env->stack) { given_p = !(ci->proc->env->stack == mrb->c->stbase || mrb_nil_p(ci->proc->env->stack[1])); } else { 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)); } 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)) { /* BUILTIN_TYPE(obj) == T_CLASS */ 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 init_copy(mrb_state *mrb, mrb_value dest, 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: 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_value mrb_obj_clone(mrb_state *mrb, mrb_value self) { struct RObject *p; mrb_value clone; if (mrb_special_const_p(self)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self); } 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_value mrb_obj_dup(mrb_state *mrb, mrb_value obj) { struct RBasic *p; mrb_value dup; if (mrb_special_const_p(obj)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj); } 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, int argc, mrb_value *argv, mrb_value obj) { 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" */ mrb_value mrb_obj_extend_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; 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_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } /* 15.3.1.3.16 */ 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; } mrb_value mrb_yield_internal(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv, mrb_value self, struct RClass *c); /* 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 mrb_yield_internal(mrb, b, 0, 0, self, c); } 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_bool instance_of_p; mrb_get_args(mrb, "C", &arg); instance_of_p = mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)); return mrb_bool_value(instance_of_p); } static void valid_iv_name(mrb_state *mrb, mrb_sym iv_name_id, const char* s, size_t len) { if (len < 2 || !(s[0] == '@' && s[1] != '@')) { mrb_name_error(mrb, iv_name_id, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name_id)); } } static void check_iv_name(mrb_state *mrb, mrb_sym iv_name_id) { const char *s; size_t len; s = mrb_sym2name_len(mrb, iv_name_id, &len); valid_iv_name(mrb, iv_name_id, s, len); } static mrb_sym get_valid_iv_sym(mrb_state *mrb, mrb_value iv_name) { mrb_sym iv_name_id; mrb_assert(mrb_symbol_p(iv_name) || mrb_string_p(iv_name)); if (mrb_string_p(iv_name)) { iv_name_id = mrb_intern_cstr(mrb, RSTRING_PTR(iv_name)); valid_iv_name(mrb, iv_name_id, RSTRING_PTR(iv_name), RSTRING_LEN(iv_name)); } else { iv_name_id = mrb_symbol(iv_name); check_iv_name(mrb, iv_name_id); } return iv_name_id; } /* 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 */ mrb_value mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) { mrb_sym mid; mrb_value sym; mrb_bool defined_p; mrb_get_args(mrb, "o", &sym); mid = get_valid_iv_sym(mrb, sym); defined_p = mrb_obj_iv_defined(mrb, mrb_obj_ptr(self), mid); return mrb_bool_value(defined_p); } /* 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 */ mrb_value mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) { mrb_sym iv_name_id; mrb_value iv_name; mrb_get_args(mrb, "o", &iv_name); iv_name_id = get_valid_iv_sym(mrb, iv_name); return mrb_iv_get(mrb, self, iv_name_id); } /* 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 #=> "#" */ mrb_value mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) { mrb_sym iv_name_id; mrb_value iv_name, val; mrb_get_args(mrb, "oo", &iv_name, &val); iv_name_id = get_valid_iv_sym(mrb, iv_name); mrb_iv_set(mrb, self, iv_name_id, 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 */ mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_bool kind_of_p; mrb_get_args(mrb, "C", &arg); kind_of_p = mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)); return mrb_bool_value(kind_of_p); } KHASH_DECLARE(st, mrb_sym, char, 0) KHASH_DEFINE(st, mrb_sym, char, 0, 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;itt == MRB_TT_ICLASS) || (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 */ 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?. */ 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. */ 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. */ 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. */ 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_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 */ 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); check_iv_name(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. */ mrb_value obj_respond_to(mrb_state *mrb, mrb_value self) { mrb_value *argv; int argc; mrb_value mid, priv; mrb_sym id, rtm_id; mrb_bool respond_to_p = TRUE; mrb_get_args(mrb, "*", &argv, &argc); mid = argv[0]; if (argc > 1) priv = argv[1]; else priv = mrb_nil_value(); 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, !mrb_test(priv)); } if (!respond_to_p) { rtm_id = mrb_intern_lit(mrb, "respond_to_missing?"); if (basic_obj_respond_to(mrb, self, rtm_id, !mrb_test(priv))) { return mrb_funcall_argv(mrb, self, rtm_id, argc, argv); } } 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] */ 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); } void mrb_init_kernel(mrb_state *mrb) { struct RClass *krn; krn = mrb->kernel_module = mrb_define_module(mrb, "Kernel"); 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 */ ; /* 15.3.1.2.11 */ mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 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, "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, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */ 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-0.0.0~20131214+git882afdea/src/keywords000066400000000000000000000057141225163307400202560ustar00rootroot00000000000000%{ 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-0.0.0~20131214+git882afdea/src/lex.def000066400000000000000000000213631225163307400177320ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.0.3 */ /* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/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 "src/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 "src/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 #ifdef __GNUC_STDC_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 "src/keywords" {"break", {keyword_break, keyword_break}, EXPR_MID}, #line 23 "src/keywords" {"else", {keyword_else, keyword_else}, EXPR_BEG}, #line 33 "src/keywords" {"nil", {keyword_nil, keyword_nil}, EXPR_END}, #line 26 "src/keywords" {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, #line 25 "src/keywords" {"end", {keyword_end, keyword_end}, EXPR_END}, #line 42 "src/keywords" {"then", {keyword_then, keyword_then}, EXPR_BEG}, #line 34 "src/keywords" {"not", {keyword_not, keyword_not}, EXPR_ARG}, #line 27 "src/keywords" {"false", {keyword_false, keyword_false}, EXPR_END}, #line 40 "src/keywords" {"self", {keyword_self, keyword_self}, EXPR_END}, #line 24 "src/keywords" {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, #line 37 "src/keywords" {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, #line 43 "src/keywords" {"true", {keyword_true, keyword_true}, EXPR_END}, #line 46 "src/keywords" {"until", {keyword_until, modifier_until}, EXPR_VALUE}, #line 45 "src/keywords" {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, #line 39 "src/keywords" {"return", {keyword_return, keyword_return}, EXPR_MID}, #line 21 "src/keywords" {"def", {keyword_def, keyword_def}, EXPR_FNAME}, #line 16 "src/keywords" {"and", {keyword_and, keyword_and}, EXPR_VALUE}, #line 22 "src/keywords" {"do", {keyword_do, keyword_do}, EXPR_BEG}, #line 49 "src/keywords" {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, #line 28 "src/keywords" {"for", {keyword_for, keyword_for}, EXPR_VALUE}, #line 44 "src/keywords" {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, #line 35 "src/keywords" {"or", {keyword_or, keyword_or}, EXPR_VALUE}, #line 30 "src/keywords" {"in", {keyword_in, keyword_in}, EXPR_VALUE}, #line 47 "src/keywords" {"when", {keyword_when, keyword_when}, EXPR_VALUE}, #line 38 "src/keywords" {"retry", {keyword_retry, keyword_retry}, EXPR_END}, #line 29 "src/keywords" {"if", {keyword_if, modifier_if}, EXPR_VALUE}, #line 19 "src/keywords" {"case", {keyword_case, keyword_case}, EXPR_VALUE}, #line 36 "src/keywords" {"redo", {keyword_redo, keyword_redo}, EXPR_END}, #line 32 "src/keywords" {"next", {keyword_next, keyword_next}, EXPR_MID}, #line 41 "src/keywords" {"super", {keyword_super, keyword_super}, EXPR_ARG}, #line 31 "src/keywords" {"module", {keyword_module, keyword_module}, EXPR_VALUE}, #line 17 "src/keywords" {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, #line 12 "src/keywords" {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, #line 11 "src/keywords" {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, #line 10 "src/keywords" {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, #line 14 "src/keywords" {"END", {keyword_END, keyword_END}, EXPR_END}, #line 15 "src/keywords" {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, #line 13 "src/keywords" {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, {""}, #line 20 "src/keywords" {"class", {keyword_class, keyword_class}, EXPR_CLASS}, {""}, {""}, #line 48 "src/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 "src/keywords" mruby-0.0.0~20131214+git882afdea/src/load.c000066400000000000000000000441371225163307400175510ustar00rootroot00000000000000/* ** load.c - mruby binary loader ** ** See Copyright Notice in mruby.h */ #ifndef SIZE_MAX /* Some versions of VC++ * has SIZE_MAX in stdint.h */ # include #endif #include #include #include "mruby/dump.h" #include "mruby/irep.h" #include "mruby/proc.h" #include "mruby/string.h" #include "mruby/debug.h" #if !defined(_WIN32) && SIZE_MAX < UINT32_MAX # define SIZE_ERROR_MUL(x, y) ((x) > SIZE_MAX / (y)) # define SIZE_ERROR(x) ((x) > SIZE_MAX) #else # define SIZE_ERROR_MUL(x, y) (0) # define SIZE_ERROR(x) (0) #endif #if CHAR_BIT != 8 # error This code assumes CHAR_BIT == 8 #endif 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, uint32_t *len) { size_t i; const uint8_t *src = bin; 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 = bin_to_uint16(src); src += sizeof(uint16_t); // Binary Data Section // ISEQ BLOCK irep->ilen = bin_to_uint32(src); src += sizeof(uint32_t); if (irep->ilen > 0) { if (SIZE_ERROR_MUL(sizeof(mrb_code), irep->ilen)) { return NULL; } irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen); if (irep->iseq == NULL) { return NULL; } for (i = 0; i < irep->ilen; i++) { irep->iseq[i] = bin_to_uint32(src); //iseq src += sizeof(uint32_t); } } //POOL BLOCK plen = bin_to_uint32(src); /* number of pool */ src += sizeof(uint32_t); if (plen > 0) { if (SIZE_ERROR_MUL(sizeof(mrb_value), plen)) { return NULL; } irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); if (irep->pool == NULL) { return NULL; } 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); s = mrb_str_new(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 = bin_to_uint32(src); //syms length src += sizeof(uint32_t); if (irep->slen > 0) { if (SIZE_ERROR_MUL(sizeof(mrb_sym), irep->slen)) { return NULL; } irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); if (irep->syms == NULL) { return NULL; } 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; } irep->syms[i] = mrb_intern(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); *len = src - bin; return irep; } static mrb_irep* read_irep_record(mrb_state *mrb, const uint8_t *bin, uint32_t *len) { mrb_irep *irep = read_irep_record_1(mrb, bin, len); size_t i; bin += *len; for (i=0; irlen; i++) { uint32_t rlen; irep->reps[i] = read_irep_record(mrb, bin, &rlen); bin += rlen; *len += rlen; } return irep; } static mrb_irep* read_section_irep(mrb_state *mrb, const uint8_t *bin) { uint32_t len; bin += sizeof(struct rite_section_irep_header); return read_irep_record(mrb, bin, &len); } static int read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, uint32_t *len) { int ret; size_t i, fname_len, niseq; char *fname; uint16_t *lines; ret = MRB_DUMP_OK; *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); if (SIZE_ERROR(fname_len + 1)) { return MRB_DUMP_GENERAL_FAILURE; } fname = (char *)mrb_malloc(mrb, fname_len + 1); if (fname == NULL) { return MRB_DUMP_GENERAL_FAILURE; } memcpy(fname, bin, fname_len); fname[fname_len] = '\0'; bin += fname_len; *len += fname_len; niseq = 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)); if (lines == NULL) { return MRB_DUMP_GENERAL_FAILURE; } 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 ret; } static int read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, uint32_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++) { uint32_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) { uint32_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, uint32_t *len, const mrb_sym *filenames, size_t filenames_len) { const uint8_t *bin = start; 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 = 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; size_t 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 = bin_to_uint8(bin); bin += sizeof(uint8_t); switch(file->line_type) { case mrb_debug_line_ary: { size_t l; file->line_ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * file->line_entry_count); for(l = 0; l < file->line_entry_count; ++l) { file->line_ary[l] = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; case mrb_debug_line_flat_map: { size_t l; file->line_flat_map = mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * file->line_entry_count); for(l = 0; l < file->line_entry_count; ++l) { file->line_flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); file->line_flat_map[l].line = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; default: return MRB_DUMP_GENERAL_FAILURE; } } if((long)record_size != (bin - start)) { return MRB_DUMP_GENERAL_FAILURE; } for (i = 0; i < irep->rlen; i++) { uint32_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; } *len = bin - start; return MRB_DUMP_OK; } static int read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep) { const uint8_t *bin; struct rite_section_debug_header *header; uint16_t i; uint32_t len = 0; int result; size_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) * filenames_len); for(i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); filenames[i] = mrb_intern(mrb, (const char *)bin, 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; if ((bin - start) != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } debug_exit: mrb_free(mrb, filenames); return result; } static int read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc) { const struct rite_binary_header *header = (const struct rite_binary_header *)bin; if (memcmp(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } *crc = bin_to_uint16(header->binary_crc); if (bin_size) { *bin_size = bin_to_uint32(header->binary_size); } return MRB_DUMP_OK; } mrb_irep* mrb_read_irep(mrb_state *mrb, const uint8_t *bin) { 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); 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_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { irep = read_section_irep(mrb, bin); if (!irep) return NULL; } else if (memcmp(section_header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header->section_identify)) == 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_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_debug(mrb, bin, irep); if (result < MRB_DUMP_OK) { return NULL; } } bin += bin_to_uint32(section_header->section_size); } while (memcmp(section_header->section_identify, RITE_BINARY_EOF, sizeof(section_header->section_identify)) != 0); return irep; } static void irep_error(mrb_state *mrb) { static const char msg[] = "irep load error"; mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SCRIPT_ERROR, msg, sizeof(msg) - 1)); } mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) { mrb_irep *irep = mrb_read_irep(mrb, bin); 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->no_exec) return mrb_obj_value(proc); val = mrb_context_run(mrb, proc, mrb_top_self(mrb), 0); return val; } mrb_value mrb_load_irep(mrb_state *mrb, const uint8_t *bin) { return mrb_load_irep_cxt(mrb, bin, NULL); } #ifdef ENABLE_STDIO static int read_lineno_record_file(mrb_state *mrb, FILE *fp, mrb_irep *irep) { uint8_t header[4]; const size_t record_header_size = sizeof(header); int result; size_t i, buf_size; uint32_t len; void *ptr; uint8_t *buf; if (fread(header, record_header_size, 1, fp) == 0) { return MRB_DUMP_READ_FAULT; } buf_size = bin_to_uint32(&header[0]); if (SIZE_ERROR(buf_size)) { return MRB_DUMP_GENERAL_FAILURE; } ptr = mrb_malloc(mrb, buf_size); if (!ptr) { return MRB_DUMP_GENERAL_FAILURE; } buf = (uint8_t *)ptr; if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) { return MRB_DUMP_READ_FAULT; } result = read_lineno_record_1(mrb, buf, irep, &len); mrb_free(mrb, ptr); if (result != MRB_DUMP_OK) return result; for (i = 0; i < irep->rlen; i++) { result = read_lineno_record_file(mrb, fp, irep->reps[i]); if (result != MRB_DUMP_OK) break; } return result; } static int32_t read_section_lineno_file(mrb_state *mrb, FILE *fp, mrb_irep *irep) { struct rite_section_lineno_header header; if (fread(&header, sizeof(struct rite_section_lineno_header), 1, fp) == 0) { return MRB_DUMP_READ_FAULT; } //Read Binary Data Section return read_lineno_record_file(mrb, fp, irep); } static mrb_irep* read_irep_record_file(mrb_state *mrb, FILE *fp) { uint8_t header[1 + 4]; const size_t record_header_size = sizeof(header); size_t buf_size, i; uint32_t len; mrb_irep *irep = NULL; void *ptr; uint8_t *buf; if (fread(header, record_header_size, 1, fp) == 0) { return NULL; } buf_size = bin_to_uint32(&header[0]); if (SIZE_ERROR(buf_size)) { return NULL; } ptr = mrb_malloc(mrb, buf_size); if (!ptr) return NULL; buf = (uint8_t *)ptr; memcpy(buf, header, record_header_size); if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) { return NULL; } irep = read_irep_record_1(mrb, buf, &len); mrb_free(mrb, ptr); if (!irep) return NULL; for (i=0; irlen; i++) { irep->reps[i] = read_irep_record_file(mrb, fp); if (!irep->reps[i]) return NULL; } return irep; } static mrb_irep* read_section_irep_file(mrb_state *mrb, FILE *fp) { struct rite_section_irep_header header; if (fread(&header, sizeof(struct rite_section_irep_header), 1, fp) == 0) { return NULL; } return read_irep_record_file(mrb, fp); } mrb_irep* mrb_read_irep_file(mrb_state *mrb, FILE* fp) { mrb_irep *irep = NULL; int result; uint8_t *buf; uint16_t crc, crcwk = 0; uint32_t section_size = 0; size_t nbytes; struct rite_section_header section_header; long fpos; size_t block_size = 1 << 14; const uint8_t block_fallback_count = 4; int i; const size_t buf_size = sizeof(struct rite_binary_header); if ((mrb == NULL) || (fp == NULL)) { return NULL; } /* You don't need use SIZE_ERROR as buf_size is enough small. */ buf = mrb_malloc(mrb, buf_size); if (!buf) { return NULL; } if (fread(buf, buf_size, 1, fp) == 0) { mrb_free(mrb, buf); return NULL; } result = read_binary_header(buf, NULL, &crc); mrb_free(mrb, buf); if (result != MRB_DUMP_OK) { return NULL; } /* verify CRC */ fpos = ftell(fp); /* You don't need use SIZE_ERROR as block_size is enough small. */ for (i = 0; i < block_fallback_count; i++,block_size >>= 1){ buf = mrb_malloc_simple(mrb, block_size); if (buf) break; } if (!buf) { return NULL; } fseek(fp, offset_crc_body(), SEEK_SET); while ((nbytes = fread(buf, 1, block_size, fp)) > 0) { crcwk = calc_crc_16_ccitt(buf, nbytes, crcwk); } mrb_free(mrb, buf); if (nbytes == 0 && ferror(fp)) { return NULL; } if (crcwk != crc) { return NULL; } fseek(fp, fpos + section_size, SEEK_SET); // read sections do { fpos = ftell(fp); if (fread(§ion_header, sizeof(struct rite_section_header), 1, fp) == 0) { return NULL; } section_size = bin_to_uint32(section_header.section_size); if (memcmp(section_header.section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { fseek(fp, fpos, SEEK_SET); irep = read_section_irep_file(mrb, fp); if (!irep) return NULL; } else if (memcmp(section_header.section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { if (!irep) return NULL; /* corrupted data */ fseek(fp, fpos, SEEK_SET); result = read_section_lineno_file(mrb, fp, irep); if (result < MRB_DUMP_OK) return NULL; } else if (memcmp(section_header.section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { if (!irep) return NULL; /* corrupted data */ else { uint8_t* const bin = mrb_malloc(mrb, section_size); fseek(fp, fpos, SEEK_SET); if(fread((char*)bin, section_size, 1, fp) != 1) { mrb_free(mrb, bin); return NULL; } result = read_section_debug(mrb, bin, irep); mrb_free(mrb, bin); } if (result < MRB_DUMP_OK) return NULL; } fseek(fp, fpos + section_size, SEEK_SET); } while (memcmp(section_header.section_identify, RITE_BINARY_EOF, sizeof(section_header.section_identify)) != 0); return irep; } 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->no_exec) return mrb_obj_value(proc); val = mrb_context_run(mrb, proc, mrb_top_self(mrb), 0); return val; } mrb_value mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } #endif /* ENABLE_STDIO */ mruby-0.0.0~20131214+git882afdea/src/mruby_core.rake000066400000000000000000000017171225163307400214750ustar00rootroot00000000000000MRuby.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}" lex_def = "#{current_dir}/lex.def" objs = Dir.glob("#{current_dir}/*.c").map { |f| objfile(f.pathmap("#{current_build_dir}/%n")) } objs += [objfile("#{current_build_dir}/y.tab")] self.libmruby << objs file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| archiver.run t.name, t.prerequisites end # Parser file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t| yacc.run t.name, t.prerequisites.first end file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t| cc.run t.name, t.prerequisites.first, [], [current_dir] end # Lexical analyzer file lex_def => "#{current_dir}/keywords" do |t| gperf.run t.name, t.prerequisites.first end end mruby-0.0.0~20131214+git882afdea/src/node.h000066400000000000000000000034601225163307400175560ustar00rootroot00000000000000/* ** node.h - nodes of abstract syntax tree ** ** See Copyright Notice in mruby.h */ #ifndef NODE_H #define 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 /* NODE_H */ mruby-0.0.0~20131214+git882afdea/src/numeric.c000066400000000000000000000722661225163307400203000ustar00rootroot00000000000000/* ** numeric.c - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #include #if defined(__FreeBSD__) && __FreeBSD__ < 4 # include #endif #ifdef HAVE_IEEEFP_H # include #endif #include #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/numeric.h" #include "mruby/string.h" #ifdef MRB_USE_FLOAT #define floor(f) floorf(f) #define ceil(f) ceilf(f) #define floor(f) floorf(f) #define fmod(x,y) fmodf(x,y) #endif static 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; int both_int = FALSE; mrb_float d; mrb_get_args(mrb, "o", &y); if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) both_int = TRUE; d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); if (both_int && FIXABLE(d)) 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. */ mrb_value mrb_flo_to_str(mrb_state *mrb, mrb_value flo, int max_digit) { mrb_value result; mrb_float n; if (max_digit > 40) { mrb_raise(mrb, E_RANGE_ERROR, "Too large max_digit."); } else if (!mrb_float_p(flo)) { mrb_raise(mrb, E_TYPE_ERROR, "non float value"); } n = mrb_float(flo); if (isnan(n)) { result = mrb_str_new(mrb, "NaN", 3); } else if (isinf(n)) { if (n < 0) { result = mrb_str_new(mrb, "-inf", 4); } else { result = mrb_str_new(mrb, "inf", 3); } } else { int digit; int m; int exp; int e = 0; char s[48]; char *c = &s[0]; if (n < 0) { n = -n; *(c++) = '-'; } exp = (int)log10(n); if ((exp < 0 ? -exp : exp) > max_digit) { /* exponent representation */ e = 1; m = exp; if (m < 0) { m -= 1; } n = n / pow(10.0, m); m = 0; } else { /* un-exponent (normal) representation */ m = exp; if (m < 0) { m = 0; } } /* puts digits */ while (max_digit >= 0) { mrb_float weight = pow(10.0, m); digit = (int)floor(n / weight + FLT_EPSILON); *(c++) = '0' + digit; n -= (digit * weight); max_digit--; if (m-- == 0) { *(c++) = '.'; } else if (m < -1 && n < FLT_EPSILON) { break; } } if (e) { *(c++) = 'e'; if (exp > 0) { *(c++) = '+'; } else { *(c++) = '-'; exp = -exp; } if (exp >= 100) { mrb_raise(mrb, E_RANGE_ERROR, "Too large expornent."); } *(c++) = '0' + exp / 10; *(c++) = '0' + exp % 10; } *c = '\0'; result = mrb_str_new(mrb, &s[0], c - &s[0]); } return result; } /* 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) { #ifdef MRB_USE_FLOAT return mrb_flo_to_str(mrb, flt, 7); #else return mrb_flo_to_str(mrb, flt, 14); #endif } /* 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 = str_to_mrb_float("inf"); mod = str_to_mrb_float("nan"); } else { mod = fmod(x, y); if (isinf(x) && !isinf(y) && !isnan(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 fy, mod; mrb_get_args(mrb, "o", &y); fy = mrb_to_flo(mrb, y); flodivmod(mrb, mrb_float(x), fy, 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 num_eql(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_bool eql_p; mrb_get_args(mrb, "o", &y); if (mrb_type(x) != mrb_type(y)) { eql_p = 0; } else { eql_p = mrb_equal(mrb, x, y); } return mrb_bool_value(eql_p); } static mrb_value num_equal(mrb_state *mrb, mrb_value x, mrb_value y) { if (mrb_obj_equal(mrb, x, y)) return mrb_true_value(); return mrb_funcall(mrb, y, "==", 1, x); } /* 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; volatile mrb_float a, b; mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: b = (mrb_float)mrb_fixnum(y); break; case MRB_TT_FLOAT: b = mrb_float(y); break; default: return num_equal(mrb, x, y); } a = mrb_float(x); return mrb_bool_value(a == b); } /* 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) { mrb_float value = mrb_float(num); mrb_bool finite_p; finite_p = !(isinf(value) || isnan(value)); return mrb_bool_value(finite_p); } /* 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; int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); f = 1.0; i = abs(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) 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); } /* * 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; } #define SQRT_INT_MAX ((mrb_int)1<<((sizeof(mrb_int)*CHAR_BIT-1)/2)) /*tests if N*N would overflow*/ #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 (a == 0) return x; if (mrb_fixnum_p(y)) { mrb_int b, c; 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) { 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.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, b; mrb_get_args(mrb, "o", &y); a = mrb_fixnum(x); if (mrb_fixnum_p(y) && (b=mrb_fixnum(y)) != 0) { mrb_int mod; if (mrb_fixnum(y) == 0) { return mrb_float_value(mrb, str_to_mrb_float("nan")); } fixdivmod(mrb, a, mrb_fixnum(y), 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, str_to_mrb_float("inf")), mrb_float_value(mrb, str_to_mrb_float("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_bool equal_p; mrb_get_args(mrb, "o", &y); equal_p = mrb_obj_equal(mrb, x, y) || (mrb_type(y) == MRB_TT_FLOAT && (mrb_float)mrb_fixnum(x) == mrb_float(y)); return mrb_bool_value(equal_p); } /* 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); val = ~val; 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_int val; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); val = mrb_fixnum(x) & mrb_fixnum(y); return mrb_fixnum_value(val); } /* 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_int val; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); val = mrb_fixnum(x) | mrb_fixnum(y); return mrb_fixnum_value(val); } /* 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_int val; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); val = mrb_fixnum(x) ^ mrb_fixnum(y); return mrb_fixnum_value(val); } #define NUMERIC_SHIFT_WIDTH_MAX (sizeof(mrb_int)*CHAR_BIT-1) static mrb_value lshift(mrb_state *mrb, mrb_int val, size_t width) { if (width > NUMERIC_SHIFT_WIDTH_MAX) { mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:sizeof(mrb_int)*CHAR_BIT-1)", mrb_fixnum_value(width), mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); } val = val << width; return mrb_fixnum_value(val); } static mrb_value rshift(mrb_int val, size_t width) { if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { val = -1; } else { val = 0; } } else { val = val >> width; } return mrb_fixnum_value(val); } static inline void fix_shift_get_width(mrb_state *mrb, mrb_int *width) { mrb_value y; mrb_get_args(mrb, "o", &y); y = bit_coerce(mrb, y); *width = mrb_fixnum(y); } /* 15.2.8.3.12 */ /* * call-seq: * fix << count -> integer * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ static mrb_value fix_lshift(mrb_state *mrb, mrb_value x) { mrb_int width; mrb_value result; fix_shift_get_width(mrb, &width); if (width == 0) { result = x; } else { mrb_int val; val = mrb_fixnum(x); if (width < 0) { result = rshift(val, -width); } else { result = lshift(mrb, val, width); } } return result; } /* 15.2.8.3.13 */ /* * call-seq: * fix >> count -> integer * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ static mrb_value fix_rshift(mrb_state *mrb, mrb_value x) { mrb_int width; mrb_value result; fix_shift_get_width(mrb, &width); if (width == 0) { result = x; } else { mrb_int val; val = mrb_fixnum(x); if (width < 0) { result = lshift(mrb, val, -width); } else { result = rshift(val, width); } } return result; } /* 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) { mrb_float val; val = (mrb_float)mrb_fixnum(num); return mrb_float_value(mrb, val); } /* * 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_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"); } z = (mrb_int)d; } 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 (a == 0) return y; if (mrb_fixnum_p(y)) { mrb_int b, c; b = mrb_fixnum(y); c = a + b; if (((a < 0) ^ (b < 0)) == 0 && (a < 0) != (c < 0)) { /* integer overflow */ 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); c = a - b; if (((a < 0) ^ (b < 0)) != 0 && (a < 0) != (c < 0)) { /* integer overflow */ 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_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base) { char buf[sizeof(mrb_int)*CHAR_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 self) { mrb_float x, y; x = mrb_float(self); mrb_get_args(mrb, "f", &y); return mrb_float_value(mrb, x + 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); 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); 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 = mrb->fixnum_class = 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?", num_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 */ fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); 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_NONE()); /* 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, "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()); } mruby-0.0.0~20131214+git882afdea/src/object.c000066400000000000000000000347551225163307400201050ustar00rootroot00000000000000/* ** object.c - Object, NilClass, TrueClass, FalseClass class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/numeric.h" #include "mruby/string.h" #include "error.h" 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 (v1.value.i == v2.value.i); case MRB_TT_SYMBOL: return (v1.value.sym == v2.value.sym); case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); default: return (mrb_ptr(v1) == mrb_ptr(v2)); } } mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2) { /* temporary definition */ return mrb_obj_eq(mrb, v1, v2); } 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(mrb, "nil", 3); } /*********************************************************************** * 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(mrb, "true", 4); } /* 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(mrb, "false", 5); } void mrb_init_object(mrb_state *mrb) { struct RClass *n; struct RClass *t; struct RClass *f; n = mrb->nil_class = 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()); t = mrb->true_class = 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()); f = mrb->false_class = 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 convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, int 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", val, mrb_str_new_cstr(mrb, tname)); return mrb_nil_value(); } else { return mrb_nil_value(); } } return mrb_funcall_argv(mrb, val, m, 0, 0); } mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method) { mrb_value v; if (mrb_type(val) == MRB_TT_FIXNUM) return val; v = convert_type(mrb, val, "Integer", method, FALSE); if (mrb_nil_p(v) || mrb_type(v) != MRB_TT_FIXNUM) { return mrb_nil_value(); } return v; } 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, 1/*Qtrue*/); 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_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, 0/*Qfalse*/); 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} }; void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; struct RString *s; 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_type(x) == MRB_TT_FIXNUM) { etype = "Fixnum"; } else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } else if (mrb_special_const_p(x)) { s = mrb_str_ptr(mrb_obj_as_string(mrb, x)); etype = s->ptr; } 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_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_buf_cat(mrb, str, "#<", 2); mrb_str_cat2(mrb, str, cname); mrb_str_cat(mrb, str, ":", 1); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(obj))); mrb_str_buf_cat(mrb, str, ">", 1); 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_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: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } 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_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)", val, val, mrb_str_new_cstr(mrb, method), v); } return v; } mrb_value mrb_to_int(mrb_state *mrb, mrb_value val) { return mrb_to_integer(mrb, val, "to_int"); } static 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; default: break; } if (base != 0) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { return mrb_str_to_inum(mrb, val, base, TRUE); } 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_value mrb_Integer(mrb_state *mrb, mrb_value val) { return mrb_convert_to_integer(mrb, val, 0); } 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_value mrb_inspect(mrb_state *mrb, mrb_value obj) { return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0, 0)); } 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-0.0.0~20131214+git882afdea/src/opcode.h000066400000000000000000000214651225163307400201070ustar00rootroot00000000000000/* ** opcode.h - RiteVM operation codes ** ** See Copyright Notice in mruby.h */ #ifndef OPCODE_H #define 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) (mSyms[B]=:>,C=1) */ OP_GE,/* A B C R(A) := R(A)>=R(A+1) (mSyms[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],Cm) */ 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),mSym(B),R(A+1)) */ OP_MODULE,/* A B R(A) := newmodule(R(A),mSym(B)) */ OP_EXEC,/* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ OP_METHOD,/* A B R(A).newmethod(mSym(B),R(A+1)) */ OP_SCLASS,/* A B R(A) := R(B).singleton_class */ OP_TCLASS,/* A R(A) := target_class */ OP_DEBUG,/* A print R(A) */ 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 /* OPCODE_H */ mruby-0.0.0~20131214+git882afdea/src/parse.y000066400000000000000000003616501225163307400177740ustar00rootroot00000000000000/* ** parse.y - mruby parser ** ** See Copyright Notice in mruby.h */ %{ #undef PARSER_DEBUG #define YYDEBUG 1 #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 "mruby.h" #include "mruby/compile.h" #include "mruby/proc.h" #include "node.h" #define YYLEX_PARAM p typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; typedef struct mrb_parser_heredoc_info parser_heredoc_info; static int 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, int c); #ifndef isascii #define isascii(c) (((c) & ~0x7f) == 0) #endif #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 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) { longjmp(p->jmp, 1); } 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; } #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) { p->locals = p->locals->cdr; } static int 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 1; n = n->cdr; } l = l->cdr; } return 0; } static void local_add_f(parser_state *p, mrb_sym sym) { 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); } } // (:scope (vars..) (prog...)) static node* new_scope(parser_state *p, node *body) { return cons((node*)NODE_SCOPE, cons(p->locals->car, 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) { return list4((node*)NODE_CALL, a, nsym(b), c); } // (:fcall self mid args) static node* new_fcall(parser_state *p, mrb_sym b, node *c) { return list4((node*)NODE_FCALL, new_self(p), nsym(b), c); } // (: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(p->locals->car, b)); } // (:sclass obj body) static node* new_sclass(parser_state *p, node *o, node *b) { return list3((node*)NODE_SCLASS, o, cons(p->locals->car, b)); } // (:module module body) static node* new_module(parser_state *p, node *m, node *b) { return list3((node*)NODE_MODULE, m, cons(p->locals->car, 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), p->locals->car, 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), p->locals->car, 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, p->locals->car, a, b); } // (:lambda arg body) static node* new_lambda(parser_state *p, node *a, node *b) { return list4((node*)NODE_LAMBDA, p->locals->car, 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) { return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); } // (: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, 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, 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; if (a->car == (node*)NODE_SUPER || a->car == (node*)NODE_ZSUPER) { if (!a->cdr) a->cdr = cons(0, b); else { args_with_block(p, a->cdr, b); } } else { n = a->cdr->cdr->cdr; if (!n->car) n->car = cons(0, b); else { args_with_block(p, n->car, b); } } } 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; } 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; unsigned int stack; const struct vtable *vars; } %token keyword_class keyword_module keyword_def keyword_undef keyword_begin keyword_rescue keyword_ensure keyword_end keyword_if keyword_unless keyword_then keyword_elsif keyword_else keyword_case keyword_when keyword_while keyword_until keyword_for 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 %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 %nonassoc idNULL %nonassoc idRespond_to %nonassoc idIFUNC %nonassoc idCFUNC %nonassoc id_core_set_method_alias %nonassoc id_core_set_variable_alias %nonassoc id_core_undef_method %nonassoc id_core_define_method %nonassoc id_core_define_singleton_method %nonassoc id_core_set_postexe %token tLAST_TOKEN %% program : { p->lstate = EXPR_BEG; if (!p->locals) p->locals = cons(0,0); } top_compstmt { p->tree = new_scope(p, $2); } ; top_compstmt : top_stmts opt_terms { $$ = $1; } ; top_stmts : none { $$ = new_begin(p, 0); } | top_stmt { $$ = new_begin(p, $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); } 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); } | 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 suported"); $$ = 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 { $$ = list1($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 { $$ = $2; } ; 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; } | args ',' assocs trailer { $$ = push($1, new_hash(p, $3)); } | assocs trailer { $$ = cons(new_hash(p, $1), 0); } ; paren_args : '(' opt_call_args rparen { $$ = $2; } ; opt_paren_args : none | paren_args ; opt_call_args : none | call_args | args ',' { $$ = cons($1,0); } | args ',' assocs ',' { $$ = cons(push($1, new_hash(p, $3)), 0); } | assocs ',' { $$ = cons(list1(new_hash(p, $1)), 0); } ; call_args : command { $$ = cons(list1($1), 0); } | args opt_block_arg { $$ = cons($1, $2); } | assocs opt_block_arg { $$ = cons(list1(new_hash(p, $1)), $2); } | args ',' assocs opt_block_arg { $$ = cons(push($1, new_hash(p, $3)), $4); } | block_arg { $$ = cons(0, $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); } | tSTAR arg_value { $$ = cons(new_splat(p, $2), 0); } | 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 { $1 = p->cmdarg_stack; p->cmdarg_stack = 0; } bodystmt keyword_end { p->cmdarg_stack = $1; $$ = $3; } | tLPAREN_ARG expr {p->lstate = EXPR_ENDARG;} rparen { $$ = $2; } | 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); } | tLBRACE assoc_list '}' { $$ = new_hash(p, $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 lambda_body { p->lpar_beg = $2; $$ = new_lambda(p, $3, $4); local_unnest(p); } | keyword_if expr_value then compstmt if_tail keyword_end { $$ = new_if(p, cond($2), $4, $5); } | keyword_unless expr_value then compstmt opt_else keyword_end { $$ = new_unless(p, cond($2), $4, $5); } | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} compstmt keyword_end { $$ = new_while(p, cond($3), $6); } | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} compstmt keyword_end { $$ = new_until(p, cond($3), $6); } | 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); } | 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); 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); 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); local_resume(p, $3); } | keyword_def fname { p->in_def++; $$ = local_switch(p); } f_arglist bodystmt keyword_end { $$ = new_def(p, $2, $4, $5); local_resume(p, $3); p->in_def--; } | keyword_def singleton dot_or_colon {p->lstate = EXPR_FNAME;} 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); local_resume(p, $6); p->in_single--; } | 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 '|' { local_add_f(p, 0); $$ = 0; } | tOROP { local_add_f(p, 0); $$ = 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); } opt_block_param compstmt '}' { $$ = new_block(p,$3,$4); local_unnest(p); } | keyword_do { local_nest(p); } opt_block_param compstmt keyword_end { $$ = new_block(p,$3,$4); 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_interp 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 : tIDENTIFIER '=' arg_value { local_add_f(p, $1); $$ = cons(nsym($1), $3); } ; f_block_opt : tIDENTIFIER '=' primary_value { local_add_f(p, $1); $$ = cons(nsym($1), $3); } ; 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); } | 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) { #ifdef ENABLE_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) { #ifdef ENABLE_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 == %d", c); } } static int peeks(parser_state *p, const char *s); static int 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 { #ifdef ENABLE_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++; } } p->column++; return c; eof: if (!p->cxt) return -1; else { mrbc_context *cxt = p->cxt; if (cxt->partial_hook(p) < 0) return -1; c = '\n'; p->lineno = 1; p->cxt = cxt; return c; } } static void pushback(parser_state *p, int c) { if (c < 0) return; 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 peek_n(parser_state *p, int c, int n) { node *list = 0; int c0; do { c0 = nextc(p); if (c0 < 0) return FALSE; list = push(list, (node*)(intptr_t)c0); } while(n--); if (p->pb) { p->pb = append(p->pb, (node*)list); } else { p->pb = list; } if (c0 == c) return TRUE; return FALSE; } #define peek(p,c) peek_n((p), (c), 0) static int peeks(parser_state *p, const char *s) { int len = strlen(s); #ifdef ENABLE_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 int 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 == *s) break; } s++; if (peeks(p, s)) { int len = strlen(s); while (len--) { nextc(p); } 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, int c) { if (p->bidx < MRB_PARSER_BUF_SIZE) { p->buf[p->bidx++] = c; } } static int toklast(parser_state *p) { return p->buf[p->bidx-1]; } static void tokfix(parser_state *p) { if (p->bidx >= MRB_PARSER_BUF_SIZE) { yyerror(p, "string too long (truncated)"); } p->buf[p->bidx] = '\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 int scan_hex(const int *start, int len, int *retlen) { static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; register const int *s = start; register int retval = 0; char *tmp; /* mrb_assert(len <= 2) */ while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { retval <<= 4; retval |= (tmp - hexdigit) & 15; s++; } *retlen = s - start; return retval; } static int read_escape(parser_state *p) { int 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] == -1) 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] == -1) 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 '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 == -1) 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 == -1) goto eof; return c & 0x9f; eof: case -1: 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 == -1)) { int 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 == -1) { 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 == -1) { 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') && (type & STR_FUNC_ARRAY)) { p->lineno++; p->column = 0; tokadd(p, '\n'); } else { if (type & STR_FUNC_REGEXP) { tokadd(p, '\\'); if (c != -1) tokadd(p, c); } else { pushback(p, c); tokadd(p, read_escape(p)); } if (hinf) hinf->line_head = FALSE; } } else { if (c != beg && c != end) { switch (c) { case '\n': p->lineno++; p->column = 0; break; case '\\': break; default: if (! 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; } } } while (ISSPACE(c = nextc(p))); 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 c; char *s = strndup(tok(p), toklen(p)); char flags[3]; char *flag = flags; char *dup; newtok(p); while (c = nextc(p), c != -1 && ISALPHA(c)) { switch (c) { case 'i': f |= 1; break; case 'x': f |= 2; break; case 'm': f |= 4; break; default: tokadd(p, c); break; } } pushback(p, c); 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'; dup = strndup(flags, (size_t)(flag - flags)); } else { dup = NULL; } yylval.nd = new_regx(p, s, dup); 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; int indent = FALSE; int 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)) != -1 && c != term) { if (c == '\n') { c = -1; break; } tokadd(p, c); } if (c == -1) { yyerror(p, "unterminated here document identifier"); return 0; } } else { if (c == -1) { 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)) != -1 && 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) { register int 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 '\0': /* NUL */ case '\004': /* ^D */ case '\032': /* ^Z */ return 0; 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 '\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) { 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 */ 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) { if (peeks(p, "begin\n")) { skips(p, "\n=end\n"); 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 '<': last_state = p->lstate; 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 == -1) { 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 '?'; } token_column = 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 = nextc(p); if (c == 'u') { #if 0 tokadd_utf8(p); #endif } else { pushback(p, 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 != -1 && 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 != -1 && 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 != -1 && 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; token_column = 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 != -1 && 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)) != -1); } 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)) != -1); } 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 != -1 && ISDIGIT(c)) { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (!ISDIGIT(c)) break; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) != -1); } 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 == -1 || 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)) != -1); 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 == -1 || !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--; 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 == -1 || !ISALNUM(c)) { term = c; c = 'Q'; } else { term = nextc(p); if (isalnum(term)) { yyerror(p, "unknown type of %string"); return 0; } } if (c == -1 || term == -1) { 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 == -1) { yyerror(p, "incomplete global variable syntax"); return 0; } switch (c) { case '_': /* $_: last read line string */ c = nextc(p); if (c != -1 && 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 != -1 && isdigit(c)); pushback(p, c); if (last_state == EXPR_FNAME) goto gvar; tokfix(p); yylval.nd = new_nth_ref(p, atoi(tok(p))); return tNTH_REF; default: if (!identchar(c)) { pushback(p, c); return '$'; } case '0': tokadd(p, '$'); } break; case '@': c = nextc(p); token_column = newtok(p); tokadd(p, '@'); if (c == '@') { tokadd(p, '@'); c = nextc(p); } if (c == -1) { 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; last_state = p->lstate; 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((int)(unsigned char)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; 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) { int t; p->ylval = lval; t = parser_yylex(p); return t; } static void parser_init_cxt(parser_state *p, mrbc_context *cxt) { if (!cxt) return; if (cxt->lineno) p->lineno = cxt->lineno; if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); 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; 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 codedump_all(mrb_state*, struct RProc*); void parser_dump(mrb_state *mrb, node *tree, int offset); void mrb_parser_parse(parser_state *p, mrbc_context *c) { if (setjmp(p->jmp) != 0) { yyerror(p, "memory allocation error"); p->nerr++; p->tree = 0; return; } p->cmd_start = TRUE; p->in_def = p->in_single = FALSE; 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) { parser_dump(p->mrb, p->tree, 0); } } 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 0; p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); if (!p) return 0; *p = parser_state_zero; p->mrb = mrb; p->pool = pool; p->in_def = p->in_single = 0; p->s = p->send = NULL; #ifdef ENABLE_STDIO p->f = NULL; #endif p->cmd_start = TRUE; p->in_def = p->in_single = FALSE; p->capture_errors = 0; 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; } void mrb_parser_free(parser_state *p) { mrb_pool_close(p->pool); } mrbc_context* mrbc_context_new(mrb_state *mrb) { mrbc_context *c; c = (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); return c; } void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) { mrb_free(mrb, cxt->syms); mrb_free(mrb, cxt); } 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; } 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; } void mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) { mrb_sym sym; size_t len; size_t i; mrb_sym* new_table; sym = mrb_intern_cstr(p->mrb, f); p->filename = mrb_sym2name_len(p->mrb, sym, &len); 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 = parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); if (p->filename_table) { memcpy(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; } char const* mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { if (idx >= p->filename_table_length) { return NULL; } else { size_t len; return mrb_sym2name_len(p->mrb, p->filename_table[idx], &len); } } #ifdef ENABLE_STDIO parser_state* mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) { parser_state *p; p = mrb_parser_new(mrb); if (!p) return 0; p->s = p->send = NULL; p->f = f; mrb_parser_parse(p, c); return p; } #endif 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 0; p->s = s; p->send = s + len; mrb_parser_parse(p, c); return p; } 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; 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 { static const char msg[] = "syntax error"; mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, msg, sizeof(msg) - 1)); mrb_parser_free(p); return mrb_undef_value(); } } proc = mrb_generate_code(mrb, p); mrb_parser_free(p); if (proc == NULL) { static const char msg[] = "codegen error"; mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SCRIPT_ERROR, msg, sizeof(msg) - 1)); return mrb_undef_value(); } if (c) { if (c->dump_result) codedump_all(mrb, proc); if (c->no_exec) return mrb_obj_value(proc); if (c->target_class) { target = c->target_class; } } proc->target_class = target; if (mrb->c->ci) { mrb->c->ci->target_class = target; } v = mrb_context_run(mrb, proc, mrb_top_self(mrb), 0); if (mrb->exc) return mrb_nil_value(); return v; } #ifdef ENABLE_STDIO 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_value mrb_load_file(mrb_state *mrb, FILE *f) { return mrb_load_file_cxt(mrb, f, NULL); } #endif 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_value mrb_load_nstring(mrb_state *mrb, const char *s, int len) { return mrb_load_nstring_cxt(mrb, s, len, NULL); } 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_value mrb_load_string(mrb_state *mrb, const char *s) { return mrb_load_string_cxt(mrb, s, NULL); } #ifdef ENABLE_STDIO static void dump_prefix(int offset) { while (offset--) { putc(' ', stdout); putc(' ', stdout); } } static void dump_recur(mrb_state *mrb, node *tree, int offset) { while (tree) { parser_dump(mrb, tree->car, offset); tree = tree->cdr; } } #endif void parser_dump(mrb_state *mrb, node *tree, int offset) { #ifdef ENABLE_STDIO int n; if (!tree) return; again: dump_prefix(offset); n = (int)(intptr_t)tree->car; tree = tree->cdr; switch (n) { 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(offset+1); printf("body:\n"); parser_dump(mrb, tree->car, offset+2); } tree = tree->cdr; if (tree->car) { node *n2 = tree->car; dump_prefix(offset+1); printf("rescue:\n"); while (n2) { node *n3 = n2->car; if (n3->car) { dump_prefix(offset+2); printf("handle classes:\n"); dump_recur(mrb, n3->car, offset+3); } if (n3->cdr->car) { dump_prefix(offset+2); printf("exc_var:\n"); parser_dump(mrb, n3->cdr->car, offset+3); } if (n3->cdr->cdr->car) { dump_prefix(offset+2); printf("rescue body:\n"); parser_dump(mrb, n3->cdr->cdr->car, offset+3); } n2 = n2->cdr; } } tree = tree->cdr; if (tree->car) { dump_prefix(offset+1); printf("else:\n"); parser_dump(mrb, tree->car, offset+2); } break; case NODE_ENSURE: printf("NODE_ENSURE:\n"); dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->car, offset+2); dump_prefix(offset+1); printf("ensure:\n"); 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(offset+1); printf("mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("optional args:\n"); { node *n2 = n->car; while (n2) { dump_prefix(offset+2); printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); parser_dump(mrb, n2->car->cdr, 0); n2 = n2->cdr; } } } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("post mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n) { dump_prefix(offset+1); printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); } } dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->cdr->car, offset+2); break; case NODE_IF: printf("NODE_IF:\n"); dump_prefix(offset+1); printf("cond:\n"); parser_dump(mrb, tree->car, offset+2); dump_prefix(offset+1); printf("then:\n"); parser_dump(mrb, tree->cdr->car, offset+2); if (tree->cdr->cdr->car) { dump_prefix(offset+1); printf("else:\n"); parser_dump(mrb, tree->cdr->cdr->car, offset+2); } break; case NODE_AND: printf("NODE_AND:\n"); parser_dump(mrb, tree->car, offset+1); parser_dump(mrb, tree->cdr, offset+1); break; case NODE_OR: printf("NODE_OR:\n"); parser_dump(mrb, tree->car, offset+1); parser_dump(mrb, tree->cdr, offset+1); break; case NODE_CASE: printf("NODE_CASE:\n"); if (tree->car) { parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; while (tree) { dump_prefix(offset+1); printf("case:\n"); dump_recur(mrb, tree->car->car, offset+2); dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_WHILE: printf("NODE_WHILE:\n"); dump_prefix(offset+1); printf("cond:\n"); parser_dump(mrb, tree->car, offset+2); dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->cdr, offset+2); break; case NODE_UNTIL: printf("NODE_UNTIL:\n"); dump_prefix(offset+1); printf("cond:\n"); parser_dump(mrb, tree->car, offset+2); dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->cdr, offset+2); break; case NODE_FOR: printf("NODE_FOR:\n"); dump_prefix(offset+1); printf("var:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(offset+2); printf("rest:\n"); parser_dump(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } } tree = tree->cdr; dump_prefix(offset+1); printf("in:\n"); parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(offset+1); printf("do:\n"); parser_dump(mrb, tree->car, offset+2); break; case NODE_SCOPE: printf("NODE_SCOPE:\n"); { node *n2 = tree->car; if (n2 && (n2->car || n2->cdr)) { dump_prefix(offset+1); printf("local variables:\n"); dump_prefix(offset+2); while (n2) { if (n2->car) { if (n2 != tree->car) printf(", "); printf("%s", mrb_sym2name(mrb, sym(n2->car))); } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; offset++; goto again; case NODE_FCALL: case NODE_CALL: printf("NODE_CALL:\n"); parser_dump(mrb, tree->car, offset+1); dump_prefix(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(offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(offset+1); printf("block:\n"); parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_DOT2: printf("NODE_DOT2:\n"); parser_dump(mrb, tree->car, offset+1); parser_dump(mrb, tree->cdr, offset+1); break; case NODE_DOT3: printf("NODE_DOT3:\n"); parser_dump(mrb, tree->car, offset+1); parser_dump(mrb, tree->cdr, offset+1); break; case NODE_COLON2: printf("NODE_COLON2:\n"); parser_dump(mrb, tree->car, offset+1); dump_prefix(offset+1); printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); break; case NODE_COLON3: printf("NODE_COLON3:\n"); dump_prefix(offset+1); printf("::%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(offset+1); printf("key:\n"); parser_dump(mrb, tree->car->car, offset+2); dump_prefix(offset+1); printf("value:\n"); parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_SPLAT: printf("NODE_SPLAT:\n"); parser_dump(mrb, tree, offset+1); break; case NODE_ASGN: printf("NODE_ASGN:\n"); dump_prefix(offset+1); printf("lhs:\n"); parser_dump(mrb, tree->car, offset+2); dump_prefix(offset+1); printf("rhs:\n"); parser_dump(mrb, tree->cdr, offset+2); break; case NODE_MASGN: printf("NODE_MASGN:\n"); dump_prefix(offset+1); printf("mlhs:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(offset+2); printf("rest:\n"); if (n2->car == (node*)-1) { dump_prefix(offset+2); printf("(empty)\n"); } else { parser_dump(mrb, n2->car, offset+3); } } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } } dump_prefix(offset+1); printf("rhs:\n"); parser_dump(mrb, tree->cdr, offset+2); break; case NODE_OP_ASGN: printf("NODE_OP_ASGN:\n"); dump_prefix(offset+1); printf("lhs:\n"); parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(offset+1); printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); tree = tree->cdr; parser_dump(mrb, tree->car, offset+1); break; case NODE_SUPER: printf("NODE_SUPER:\n"); if (tree) { dump_prefix(offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(offset+1); printf("block:\n"); parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_ZSUPER: printf("NODE_ZSUPER\n"); break; case NODE_RETURN: printf("NODE_RETURN:\n"); 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"); parser_dump(mrb, tree, offset+1); break; case NODE_NEXT: printf("NODE_NEXT:\n"); 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(offset + 1); printf("lhs:\n"); parser_dump(mrb, tree->car, offset + 2); dump_prefix(offset + 1); printf("rhs:\n"); 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"); 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"); 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(offset); printf("tail: %s\n", (char*)tree->cdr->cdr->car); dump_prefix(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(offset+1); printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == (node*)1) { dump_prefix(offset+1); printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } else { parser_dump(mrb, tree->car->car, offset+1); dump_prefix(offset+1); printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } if (tree->cdr->car) { dump_prefix(offset+1); printf("super:\n"); parser_dump(mrb, tree->cdr->car, offset+2); } dump_prefix(offset+1); printf("body:\n"); 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(offset+1); printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == (node*)1) { dump_prefix(offset+1); printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } else { parser_dump(mrb, tree->car->car, offset+1); dump_prefix(offset+1); printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); } dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_SCLASS: printf("NODE_SCLASS:\n"); parser_dump(mrb, tree->car, offset+1); dump_prefix(offset+1); printf("body:\n"); parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_DEF: printf("NODE_DEF:\n"); dump_prefix(offset+1); printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); tree = tree->cdr; { node *n2 = tree->car; if (n2 && (n2->car || n2->cdr)) { dump_prefix(offset+1); printf("local variables:\n"); dump_prefix(offset+2); while (n2) { if (n2->car) { if (n2 != tree->car) printf(", "); printf("%s", mrb_sym2name(mrb, sym(n2->car))); } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; if (tree->car) { node *n = tree->car; if (n->car) { dump_prefix(offset+1); printf("mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("optional args:\n"); { node *n2 = n->car; while (n2) { dump_prefix(offset+2); printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); parser_dump(mrb, n2->car->cdr, 0); n2 = n2->cdr; } } } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("post mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n) { dump_prefix(offset+1); printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); } } parser_dump(mrb, tree->cdr->car, offset+1); break; case NODE_SDEF: printf("NODE_SDEF:\n"); parser_dump(mrb, tree->car, offset+1); tree = tree->cdr; dump_prefix(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(offset+1); printf("mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("optional args:\n"); { node *n2 = n->car; while (n2) { dump_prefix(offset+2); printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); parser_dump(mrb, n2->car->cdr, 0); n2 = n2->cdr; } } } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); } n = n->cdr; if (n->car) { dump_prefix(offset+1); printf("post mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n) { dump_prefix(offset+1); printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); } } tree = tree->cdr; parser_dump(mrb, tree->car, offset+1); break; case NODE_POSTEXE: printf("NODE_POSTEXE:\n"); parser_dump(mrb, tree, offset+1); break; case NODE_HEREDOC: printf("NODE_HEREDOC:\n"); parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; default: printf("node type: %d (0x%x)\n", (int)n, (int)n); break; } #endif } mruby-0.0.0~20131214+git882afdea/src/pool.c000066400000000000000000000070051225163307400175740ustar00rootroot00000000000000/* ** pool.c - memory pool ** ** See Copyright Notice in mruby.h */ #include #include #include "mruby.h" /* 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(m,s) malloc(s) #define mrb_free(m,p) free(p) #endif #ifdef POOL_ALIGNMENT # define ALIGN_PADDING(x) ((-x) & (POOL_ALIGNMENT - 1)) #else # define ALIGN_PADDING(x) (0) #endif mrb_pool* mrb_pool_open(mrb_state *mrb) { mrb_pool *pool = (mrb_pool *)mrb_malloc(mrb, sizeof(mrb_pool)); if (pool) { pool->mrb = mrb; pool->pages = NULL; } return pool; } 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(pool->mrb, sizeof(struct mrb_pool_page)+len); if (page) { page->offset = 0; page->len = len; } return page; } 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_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; } 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); 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-0.0.0~20131214+git882afdea/src/print.c000066400000000000000000000025051225163307400177570ustar00rootroot00000000000000/* ** print.c - Kernel.#p ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/string.h" static void printstr(mrb_state *mrb, mrb_value obj) { #ifdef ENABLE_STDIO struct RString *str; char *s; int len; if (mrb_string_p(obj)) { str = mrb_str_ptr(obj); s = str->ptr; len = str->len; fwrite(s, len, 1, stdout); } #endif } void mrb_p(mrb_state *mrb, mrb_value obj) { #ifdef ENABLE_STDIO obj = mrb_funcall(mrb, obj, "inspect", 0); printstr(mrb, obj); putc('\n', stdout); #endif } void mrb_print_error(mrb_state *mrb) { #ifdef ENABLE_STDIO mrb_value s; mrb_print_backtrace(mrb); s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); if (mrb_string_p(s)) { struct RString *str = mrb_str_ptr(s); fwrite(str->ptr, str->len, 1, stderr); putc('\n', stderr); } #endif } void mrb_show_version(mrb_state *mrb) { static const char version_msg[] = "mruby - Embeddable Ruby Copyright (c) 2010-2013 mruby developers\n"; mrb_value msg; msg = mrb_str_new(mrb, version_msg, sizeof(version_msg) - 1); printstr(mrb, msg); } void mrb_show_copyright(mrb_state *mrb) { static const char copyright_msg[] = "mruby - Copyright (c) 2010-2013 mruby developers\n"; mrb_value msg; msg = mrb_str_new(mrb, copyright_msg, sizeof(copyright_msg) - 1); printstr(mrb, msg); } mruby-0.0.0~20131214+git882afdea/src/proc.c000066400000000000000000000116331225163307400175700ustar00rootroot00000000000000/* ** proc.c - Proc class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/class.h" #include "mruby/proc.h" #include "opcode.h" static mrb_code call_iseq[] = { MKOP_A(OP_CALL, 0), }; struct RProc * mrb_proc_new(mrb_state *mrb, mrb_irep *irep) { struct RProc *p; p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); p->target_class = (mrb->c->ci) ? mrb->c->ci->target_class : 0; p->body.irep = irep; p->env = 0; mrb_irep_incref(mrb, irep); return p; } static inline void closure_setup(mrb_state *mrb, struct RProc *p, int nlocals) { struct REnv *e; if (!mrb->c->ci->env) { e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); e->flags= (unsigned int)nlocals; e->mid = mrb->c->ci->mid; e->cioff = mrb->c->ci - mrb->c->cibase; e->stack = mrb->c->stack; 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; } 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; return p; } struct RProc * mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) { struct RProc *p = mrb_proc_new_cfunc(mrb, func); closure_setup(mrb, p, nlocals); return p; } 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 = GETARG_Ax(*iseq); int ma, ra, pa, arity; ma = MRB_ASPEC_REQ(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); arity = ra ? -(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"); } 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 }; if (call_irep == NULL) return; *call_irep = mrb_irep_zero; call_irep->flags = MRB_ISEQ_NO_FREE; call_irep->iseq = call_iseq; call_irep->ilen = 1; mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); 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-0.0.0~20131214+git882afdea/src/range.c000066400000000000000000000254361225163307400177270ustar00rootroot00000000000000/* ** range.c - Range class ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/class.h" #include "mruby/range.h" #include "mruby/string.h" #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_value mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, int excl) { struct RRange *r; r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS); range_check(mrb, beg, end); 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, int 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 = 0; } /* 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_type(r) == MRB_TT_FIXNUM) { 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 */ if (mrb_type(r) == MRB_TT_FIXNUM) { if (mrb_fixnum(r) == 1) return TRUE; } return FALSE; } 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_type(r) == MRB_TT_FIXNUM) { 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); } /* * call-seq: * rng.each {| i | block } => rng * * Iterates over the elements rng, passing each in turn to the * block. You can only iterate if the start object of the range * supports the +succ+ method (which means that you can't iterate over * ranges of +Float+ objects). * * (10..15).each do |n| * print n, ' ' * end * * produces: * * 10 11 12 13 14 15 */ mrb_value mrb_range_each(mrb_state *mrb, mrb_value range) { return range; } mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len) { mrb_int beg, end, b, e; struct RRange *r = mrb_range_ptr(range); if (mrb_type(range) != MRB_TT_RANGE) { mrb_raise(mrb, E_TYPE_ERROR, "expected Range."); } beg = b = mrb_fixnum(r->edges->beg); end = e = mrb_fixnum(r->edges->end); if (beg < 0) { beg += len; if (beg < 0) return FALSE; } if (beg > len) return FALSE; if (end > len) end = len; if (end < 0) end += len; if (!r->excl && end < len) end++; /* include end point */ len = end - beg; if (len < 0) len = 0; *begp = beg; *lenp = len; return 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_append(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_append(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) */ 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; } void mrb_init_range(mrb_state *mrb) { struct RClass *r; r = mrb_define_class(mrb, "Range", mrb->object_class); MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE); mrb_include_module(mrb, r, mrb_class_get(mrb, "Enumerable")); 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, "each", mrb_range_each, MRB_ARGS_NONE()); /* 15.2.14.4.4 */ 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-0.0.0~20131214+git882afdea/src/re.h000066400000000000000000000002161225163307400172330ustar00rootroot00000000000000/* ** re.h - Regexp class ** ** See Copyright Notice in mruby.h */ #ifndef RE_H #define RE_H #define REGEXP_CLASS "Regexp" #endif mruby-0.0.0~20131214+git882afdea/src/state.c000066400000000000000000000113231225163307400177410ustar00rootroot00000000000000/* ** state.c - mrb_state open/close functions ** ** See Copyright Notice in mruby.h */ #include #include #include "mruby.h" #include "mruby/class.h" #include "mruby/irep.h" #include "mruby/variable.h" #include "mruby/debug.h" #include "mruby/string.h" void mrb_init_heap(mrb_state*); void mrb_init_core(mrb_state*); void mrb_final_core(mrb_state*); static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) { return mrb_str_new(mrb, "main", 4); } mrb_state* mrb_open_allocf(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; #ifdef MRB_NAN_BOXING mrb_assert(sizeof(void*) == 4); #endif mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); if (mrb == NULL) return NULL; *mrb = mrb_state_zero; mrb->ud = ud; mrb->allocf = f; mrb->current_white_part = MRB_GC_WHITE_A; #ifndef MRB_GC_FIXED_ARENA mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); mrb->arena_capa = MRB_GC_ARENA_SIZE; #endif mrb_init_heap(mrb); 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; } static void* 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[]; }; 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); if (p == NULL) return NULL; 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_state* mrb_open(void) { mrb_state *mrb = mrb_open_allocf(allocf, NULL); return mrb; } void mrb_free_symtbl(mrb_state *mrb); void mrb_free_heap(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_free(mrb, mrb_str_ptr(irep->pool[i])->ptr); 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, (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; mrb_int len; ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString)); ns->tt = MRB_TT_STRING; ns->c = mrb->string_class; len = s->len; ns->len = len; ns->ptr = (char *)mrb_malloc(mrb, (size_t)len+1); if (s->ptr) { memcpy(ns->ptr, s->ptr, len); } ns->ptr[len] = '\0'; return mrb_obj_value(ns); } 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); } void mrb_close(mrb_state *mrb) { mrb_final_core(mrb); /* free */ mrb_gc_free_gv(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_free_heap(mrb); mrb_alloca_free(mrb); #ifndef MRB_GC_FIXED_ARENA mrb_free(mrb, mrb->arena); #endif mrb_free(mrb, mrb); } #ifndef MRB_IREP_ARRAY_INIT_SIZE # define MRB_IREP_ARRAY_INIT_SIZE (256u) #endif 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_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); } mruby-0.0.0~20131214+git882afdea/src/string.c000066400000000000000000001754301225163307400201410ustar00rootroot00000000000000/* ** string.c - String class ** ** See Copyright Notice in mruby.h */ #include #ifndef SIZE_MAX /* Some versions of VC++ * has SIZE_MAX in stdint.h */ # include #endif #include #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/range.h" #include "mruby/string.h" #include "re.h" const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; typedef struct mrb_shared_string { mrb_bool nofree; int refcnt; char *ptr; mrb_int len; } mrb_shared_string; #define MRB_STR_SHARED 1 #define MRB_STR_NOFREE 2 static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2); static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); #define RESIZE_CAPA(s,capacity) do {\ s->ptr = (char *)mrb_realloc(mrb, s->ptr, (capacity)+1);\ s->aux.capa = capacity;\ } while(0) 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_str_modify(mrb_state *mrb, struct RString *s) { if (s->flags & MRB_STR_SHARED) { mrb_shared_string *shared = s->aux.shared; if (shared->refcnt == 1 && s->ptr == shared->ptr) { s->ptr = shared->ptr; s->aux.capa = shared->len; s->ptr[s->len] = '\0'; mrb_free(mrb, shared); } else { char *ptr, *p; mrb_int len; p = s->ptr; len = s->len; ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); if (p) { memcpy(ptr, p, len); } ptr[len] = '\0'; s->ptr = ptr; s->aux.capa = len; str_decref(mrb, shared); } s->flags &= ~MRB_STR_SHARED; return; } if (s->flags & MRB_STR_NOFREE) { char *p = s->ptr; s->ptr = (char *)mrb_malloc(mrb, (size_t)s->len+1); if (p) { memcpy(s->ptr, p, s->len); } s->ptr[s->len] = '\0'; s->aux.capa = s->len; return; } } mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { int slen; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); slen = s->len; if (len != slen) { if (slen < len || slen - len > 256) { RESIZE_CAPA(s, len); } s->len = len; s->ptr[len] = '\0'; /* sentinel */ } return str; } static inline void str_mod_check(mrb_state *mrb, mrb_value str, char *p, mrb_int len) { struct RString *s = mrb_str_ptr(str); if (s->ptr != p || s->len != len) { mrb_raise(mrb, E_RUNTIME_ERROR, "string modified"); } } #define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) /* char offset to byte offset */ int mrb_str_offset(mrb_state *mrb, mrb_value str, int pos) { return pos; } static struct RString* str_new(mrb_state *mrb, const char *p, mrb_int len) { struct RString *s; s = mrb_obj_alloc_string(mrb); s->len = len; s->aux.capa = len; s->ptr = (char *)mrb_malloc(mrb, (size_t)len+1); if (p) { memcpy(s->ptr, p, len); } s->ptr[len] = '\0'; return s; } 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_value mrb_str_buf_new(mrb_state *mrb, mrb_int capa) { struct RString *s; s = mrb_obj_alloc_string(mrb); if (capa < MRB_STR_BUF_MIN_SIZE) { capa = MRB_STR_BUF_MIN_SIZE; } s->len = 0; s->aux.capa = capa; s->ptr = (char *)mrb_malloc(mrb, capa+1); s->ptr[0] = '\0'; return mrb_obj_value(s); } static void str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) { mrb_int capa; mrb_int total; ptrdiff_t off = -1; mrb_str_modify(mrb, s); if (ptr >= s->ptr && ptr <= s->ptr + s->len) { off = ptr - s->ptr; } if (len == 0) return; capa = s->aux.capa; if (s->len >= MRB_INT_MAX - (mrb_int)len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string sizes too big"); } total = s->len+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(s, capa); } if (off != -1) { ptr = s->ptr + off; } memcpy(s->ptr + s->len, ptr, len); s->len = total; s->ptr[total] = '\0'; /* sentinel */ } mrb_value mrb_str_buf_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { if (len == 0) return str; str_buf_cat(mrb, mrb_str_ptr(str), ptr, len); return str; } mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len) { struct RString *s; s = str_new(mrb, p, len); return mrb_obj_value(s); } /* * call-seq: (Caution! NULL string) * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { struct RString *s; size_t len; if (p) { len = strlen(p); if ((mrb_int)len < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big"); } } else { len = 0; } s = str_new(mrb, p, len); return mrb_obj_value(s); } mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) { struct RString *s; s = mrb_obj_alloc_string(mrb); s->len = len; s->aux.capa = 0; /* nofree */ s->ptr = (char *)p; s->flags = MRB_STR_NOFREE; return mrb_obj_value(s); } void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { if (str->flags & MRB_STR_SHARED) str_decref(mrb, str->aux.shared); else if ((str->flags & MRB_STR_NOFREE) == 0) mrb_free(mrb, str->ptr); } 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(s->ptr) ^ s->len) != 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } return s->ptr; } static void str_make_shared(mrb_state *mrb, struct RString *s) { if (!(s->flags & MRB_STR_SHARED)) { mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 1; if (s->flags & MRB_STR_NOFREE) { shared->nofree = TRUE; shared->ptr = s->ptr; s->flags &= ~MRB_STR_NOFREE; } else { shared->nofree = FALSE; if (s->aux.capa > s->len) { s->ptr = shared->ptr = (char *)mrb_realloc(mrb, s->ptr, s->len+1); } else { shared->ptr = s->ptr; } } shared->len = s->len; s->aux.shared = shared; s->flags |= MRB_STR_SHARED; } } /* * call-seq: * char* str = String("abcd"), len=strlen("abcd") * * Returns a new string object containing a copy of str. */ const char* mrb_str_body(mrb_value str, int *len_p) { struct RString *s = mrb_str_ptr(str); *len_p = s->len; return s->ptr; } /* * call-seq: (Caution! String("abcd") change) * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ 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 = s1->len + s2->len; if (s1->aux.capa < len) { s1->aux.capa = len; s1->ptr = (char *)mrb_realloc(mrb, s1->ptr, len+1); } memcpy(s1->ptr+s1->len, s2->ptr, s2->len); s1->len = len; s1->ptr[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_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, s->len + s2->len); memcpy(t->ptr, s->ptr, s->len); memcpy(t->ptr + s->len, s2->ptr, s2->len); 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); } /* * call-seq: * len = strlen(String("abcd")) * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); return mrb_fixnum_value(s->len); } /* 15.2.10.5.26 */ /* 15.2.10.5.33 */ /* * call-seq: * len = strlen(String("abcd")) * * Returns a new string object containing a copy of str. */ mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); return mrb_fixnum_value(s->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 = str2->ptr; 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[str2->len] = '\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 */ 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(s1->len, s2->len); retval = memcmp(s1->ptr, s2->ptr, len); if (retval == 0) { if (s1->len == s2->len) return 0; if (s1->len > s2->len) 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_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { if (mrb_obj_equal(mrb, str1, str2)) return TRUE; 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_bool equal_p; mrb_get_args(mrb, "o", &str2); equal_p = mrb_str_equal(mrb, str1, str2); return mrb_bool_value(equal_p); } /* ---------------------------------- */ 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; } char * mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr) { mrb_value str = mrb_str_to_str(mrb, ptr); return RSTRING_PTR(str); } static mrb_value noregexp(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); return mrb_nil_value(); } static void regexp_check(mrb_state *mrb, mrb_value obj) { if (!memcmp(mrb_obj_classname(mrb, obj), REGEXP_CLASS, sizeof(REGEXP_CLASS) - 1)) { noregexp(mrb, obj); } } static inline mrb_int mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) { const unsigned char *x = xs, *xe = xs + m; const unsigned char *y = ys; int i, qstable[256]; /* Preprocessing */ for (i = 0; i < 256; ++i) qstable[i] = m + 1; for (; x < xe; ++x) qstable[*x] = xe - x; /* Searching */ for (; y + m <= ys + n; y += *(qstable + y[m])) { if (*xs == *y && memcmp(xs, y, m) == 0) return y - ys; } return -1; } static mrb_int mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) { const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; if (m > 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 = y, *ye = ys + n; for (; y < ye; ++y) { if (*x == *y) return y - ys; } return -1; } return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } static mrb_int mrb_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; } mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { /* should return shared string */ struct RString *s = mrb_str_ptr(str); return mrb_str_new(mrb, s->ptr, s->len); } static mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) { mrb_int idx; regexp_check(mrb, indx); switch (mrb_type(indx)) { case MRB_TT_FIXNUM: idx = mrb_fixnum(indx); num_index: str = mrb_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 (mrb_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; mrb_value tmp; len = RSTRING_LEN(str); if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { tmp = mrb_str_subseq(mrb, str, beg, len); return tmp; } else { return mrb_nil_value(); } } default: 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(regexp) => new_str or nil * str.slice(regexp, fixnum) => 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 Regexp is supplied, the matching portion of str is * returned. If a numeric parameter follows the regular expression, that * component of the MatchData is returned instead. 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,3] #=> "ell" * a[1..3] #=> "ell" * a[-3,2] #=> "er" * a[-4..-2] #=> "her" * a[12..-1] #=> nil * a[-2..-4] #=> "" * a[/[aeiou](.)\1/] #=> "ell" * a[/[aeiou](.)\1/, 0] #=> "ell" * a[/[aeiou](.)\1/, 1] #=> "l" * a[/[aeiou](.)\1/, 2] #=> nil * 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) { regexp_check(mrb, a1); return mrb_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; int modify = 0; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); if (s->len == 0 || !s->ptr) return mrb_nil_value(); p = s->ptr; pend = s->ptr + s->len; if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = 1; } while (++p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = 1; } } 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=$/) => 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 = s->len; if (mrb_get_args(mrb, "|S", &rs) == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: if (s->ptr[len-1] == '\n') { s->len--; if (s->len > 0 && s->ptr[s->len-1] == '\r') { s->len--; } } else if (s->ptr[len-1] == '\r') { s->len--; } else { return mrb_nil_value(); } s->ptr[s->len] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); p = s->ptr; 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 < s->len) { s->len = 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)) { s->len = len - rslen; p[s->len] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.9 */ /* * call-seq: * str.chomp(separator=$/) => 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 (s->len > 0) { int len; len = s->len - 1; if (s->ptr[len] == '\n') { if (len > 0 && s->ptr[len-1] == '\r') { len--; } } s->len = len; s->ptr[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; int modify = 0; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); p = s->ptr; pend = s->ptr + s->len; while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = 1; } 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(s->len == 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); } static mrb_value mrb_str_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); str_make_shared(mrb, orig); shared = orig->aux.shared; s = mrb_obj_alloc_string(mrb); s->ptr = orig->ptr + beg; s->len = len; s->aux.shared = shared; s->flags |= MRB_STR_SHARED; shared->refcnt++; return mrb_obj_value(s); } mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { mrb_value str2; if (len < 0) return mrb_nil_value(); if (!RSTRING_LEN(str)) { len = 0; } if (beg > RSTRING_LEN(str)) return mrb_nil_value(); if (beg < 0) { beg += RSTRING_LEN(str); if (beg < 0) return mrb_nil_value(); } if (beg + len > RSTRING_LEN(str)) len = RSTRING_LEN(str) - beg; if (len <= 0) { len = 0; } str2 = mrb_str_subseq(mrb, str, beg, len); return str2; } mrb_value mrb_str_buf_append(mrb_state *mrb, mrb_value str, mrb_value str2) { mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); return str; } mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); mrb_int len = s->len; char *p = s->ptr; mrb_int key = 0; while (len--) { key = key*65599 + *p; p++; } key = key + (key>>5); return key; } /* 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_type(str2) == MRB_TT_FIXNUM) { include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL); } else { str2 = mrb_str_to_str(mrb, str2); i = mrb_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_m(mrb_state *mrb, mrb_value str) { mrb_value *argv; int argc; mrb_value sub; mrb_int pos; 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(); } regexp_check(mrb, sub); if (pos < 0) { pos += RSTRING_LEN(str); if (pos < 0) { return mrb_nil_value(); } } switch (mrb_type(sub)) { case MRB_TT_FIXNUM: { int c = mrb_fixnum(sub); mrb_int len = RSTRING_LEN(str); unsigned char *p = (unsigned char*)RSTRING_PTR(str); for (;posflags & MRB_STR_SHARED) { L_SHARE: if (s1->flags & MRB_STR_SHARED){ str_decref(mrb, s1->aux.shared); } else { mrb_free(mrb, s1->ptr); } s1->ptr = s2->ptr; s1->len = s2->len; s1->aux.shared = s2->aux.shared; s1->flags |= MRB_STR_SHARED; s1->aux.shared->refcnt++; } else if (s2->len > STR_REPLACE_SHARED_MIN) { str_make_shared(mrb, s2); goto L_SHARE; } else { if (s1->flags & MRB_STR_SHARED) { str_decref(mrb, s1->aux.shared); s1->flags &= ~MRB_STR_SHARED; s1->ptr = (char *)mrb_malloc(mrb, s2->len+1); } else { s1->ptr = (char *)mrb_realloc(mrb, s1->ptr, s2->len+1); } memcpy(s1->ptr, s2->ptr, s2->len); s1->ptr[s2->len] = 0; s1->len = s2->len; s1->aux.capa = s2->len; } return mrb_obj_value(s1); } /* 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_value mrb_str_intern(mrb_state *mrb, mrb_value self) { mrb_sym id; id = mrb_intern_str(mrb, self); return mrb_symbol_value(id); } /* ---------------------------------- */ 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_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 = p_str->ptr; *p1++ = '0'; *p1++ = 'x'; p2 = p1; do { *p2++ = mrb_digitmap[n % 16]; n /= 16; } while (n > 0); *p2 = '\0'; p_str->len = (mrb_int)(p2 - p_str->ptr); while (p1 < p2) { const char c = *p1; *p1++ = *--p2; *p2 = c; } return mrb_obj_value(p_str); } mrb_value mrb_string_type(mrb_state *mrb, mrb_value str) { return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); } 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.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) { struct RString *s2; char *s, *e, *p; if (RSTRING(str)->len <= 1) return mrb_str_dup(mrb, str); s2 = str_new(mrb, 0, RSTRING(str)->len); str_with_class(mrb, s2, str); s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; p = s2->ptr; while (e >= s) { *p++ = *e--; } return mrb_obj_value(s2); } /* 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) { struct RString *s = mrb_str_ptr(str); char *p, *e; char c; mrb_str_modify(mrb, s); if (s->len > 1) { p = s->ptr; e = p + s->len - 1; while (p < e) { c = *p; *p++ = *e; *e-- = c; } } return str; } /* * 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_int mrb_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); struct RString *psub = mrb_str_ptr(sub); mrb_int len = psub->len; /* substring longer than string */ if (ps->len < len) return -1; if (ps->len - pos < len) { pos = ps->len - len; } sbeg = ps->ptr; s = ps->ptr + pos; t = psub->ptr; if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { return s - ps->ptr; } s--; } return -1; } else { return pos; } } /* 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_m(mrb_state *mrb, mrb_value str) { mrb_value *argv; int argc; mrb_value sub; mrb_value vpos; int pos, len = RSTRING_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) { 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(); } regexp_check(mrb, sub); switch (mrb_type(sub)) { case MRB_TT_FIXNUM: { int c = mrb_fixnum(sub); mrb_int len = RSTRING_LEN(str); unsigned char *p = (unsigned char*)RSTRING_PTR(str); for (pos=len;pos>=0;pos--) { if (p[pos] == c) return mrb_fixnum_value(pos); } return mrb_nil_value(); } 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 = mrb_str_rindex(mrb, str, sub, pos); if (pos >= 0) return mrb_fixnum_value(pos); break; } /* end of switch (TYPE(sub)) */ return mrb_nil_value(); } static const char isspacetable[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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 }; #define ascii_isspace(c) isspacetable[(unsigned char)(c)] /* 15.2.10.5.35 */ /* * call-seq: * str.split(pattern=$;, [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"] * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"] * "hello".split(//) #=> ["h", "e", "l", "l", "o"] * "hello".split(//, 3) #=> ["h", "e", "llo"] * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"] * * "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 { noregexp(mrb, str); } } result = mrb_ary_new(mrb); beg = 0; if (split_type == awk) { char *ptr = RSTRING_PTR(str); char *eptr = RSTRING_END(str); char *bptr = ptr; int skip = 1; unsigned int c; end = beg; while (ptr < eptr) { int ai = mrb_gc_arena_save(mrb); c = (unsigned char)*ptr++; if (skip) { if (ascii_isspace(c)) { beg = ptr - bptr; } else { end = ptr - bptr; skip = 0; if (lim_p && lim <= i) break; } } else if (ascii_isspace(c)) { mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = 1; beg = ptr - bptr; if (lim_p) ++i; } else { end = ptr - bptr; } } } else if (split_type == string) { char *ptr = RSTRING_PTR(str); char *temp = ptr; char *eptr = RSTRING_END(str); mrb_int slen = RSTRING_LEN(spat); if (slen == 0) { int ai = mrb_gc_arena_save(mrb); while (ptr < eptr) { mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr-temp, 1)); mrb_gc_arena_restore(mrb, ai); ptr++; if (lim_p && lim <= ++i) break; } } else { char *sptr = RSTRING_PTR(spat); int ai = mrb_gc_arena_save(mrb); while (ptr < eptr && (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) { mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr - temp, end)); mrb_gc_arena_restore(mrb, ai); ptr += end + slen; if (lim_p && lim <= ++i) break; } } beg = ptr - temp; } else { 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 = mrb_str_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_value mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) { char *end; char sign = 1; int c; unsigned long n; mrb_int val; #undef ISDIGIT #define ISDIGIT(c) ('0' <= (c) && (c) <= '9') #define conv_digit(c) \ (!ISASCII(c) ? -1 : \ isdigit(c) ? ((c) - '0') : \ islower(c) ? ((c) - 'a' + 10) : \ isupper(c) ? ((c) - 'A' + 10) : \ -1) if (!str) { if (badcheck) goto bad; return mrb_fixnum_value(0); } while (ISSPACE(*str)) str++; if (str[0] == '+') { str++; } else if (str[0] == '-') { str++; sign = 0; } if (str[0] == '+' || str[0] == '-') { if (badcheck) goto bad; return mrb_fixnum_value(0); } if (base <= 0) { if (str[0] == '0') { switch (str[1]) { case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'o': case 'O': base = 8; break; case 'd': case 'D': base = 10; break; default: base = 8; } } else if (base < -1) { base = -base; } else { base = 10; } } switch (base) { case 2: if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) { str += 2; } break; case 3: break; case 8: if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) { str += 2; } case 4: case 5: case 6: case 7: break; case 10: if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) { str += 2; } case 9: case 11: case 12: case 13: case 14: case 15: break; case 16: if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) { str += 2; } break; default: if (base < 2 || 36 < base) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); } break; } /* end of switch (base) { */ if (*str == '0') { /* squeeze preceeding 0s */ int us = 0; while ((c = *++str) == '0' || c == '_') { if (c == '_') { if (++us >= 2) break; } else us = 0; } if (!(c = *str) || ISSPACE(c)) --str; } c = *str; c = conv_digit(c); if (c < 0 || c >= base) { if (badcheck) goto bad; return mrb_fixnum_value(0); } n = strtoul((char*)str, &end, base); if (n > MRB_INT_MAX) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); } val = n; if (badcheck) { if (end == str) goto bad; /* no number */ while (*end && ISSPACE(*end)) end++; if (*end) goto bad; /* trailing garbage */ } return mrb_fixnum_value(sign ? val : -val); bad: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str)); /* not reached */ return mrb_fixnum_value(0); } char * mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { struct RString *ps = mrb_str_ptr(*ptr); char *s = ps->ptr; if (!s || ps->len != strlen(s)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } return s; } mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, int base, int badcheck) { char *s; int len; str = mrb_str_to_str(mrb, str); if (badcheck) { s = mrb_string_value_cstr(mrb, &str); } else { s = RSTRING_PTR(str); } if (s) { len = RSTRING_LEN(str); if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); s = temp_str->ptr; } } return mrb_cstr_to_inum(mrb, s, base, badcheck); } /* 15.2.10.5.38 */ /* * call-seq: * str.to_i(base=10) => 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_value *argv; int argc; int base; mrb_get_args(mrb, "*", &argv, &argc); if (argc == 0) base = 10; else base = mrb_fixnum(argv[0]); if (base < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); } return mrb_str_to_inum(mrb, self, base, 0/*Qfalse*/); } double mrb_cstr_to_dbl(mrb_state *mrb, const char * p, int badcheck) { char *end; double d; #if !defined(DBL_DIG) # define DBL_DIG 16 #endif enum {max_width = 20}; #define OutOfRange() (((w = end - p) > max_width) ? \ (w = max_width, ellipsis = "...") : \ (w = (int)(end - p), ellipsis = "")) 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 buf[DBL_DIG * 4 + 10]; 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; } double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, int badcheck) { char *s; 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 = temp_str->ptr; } } 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, 0/*Qfalse*/)); } /* 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; int modify = 0; mrb_str_modify(mrb, s); p = RSTRING_PTR(str); pend = RSTRING_END(str); while (p < pend) { if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = 1; } 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; } /* * 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 = result->ptr; *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_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { if ((mrb_int)len < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative string size (or size too big)"); } str_buf_cat(mrb, mrb_str_ptr(str), ptr, len); return str; } 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_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2) { str2 = mrb_str_to_str(mrb, str2); return mrb_str_buf_append(mrb, str, 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(mrb, "\"", 1); p = RSTRING_PTR(str); pend = RSTRING_END(str); for (;p < pend; p++) { unsigned int c, cc; c = *p; if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) { buf[0] = '\\'; buf[1] = c; mrb_str_buf_cat(mrb, result, buf, 2); continue; } if (ISPRINT(c)) { buf[0] = c; mrb_str_buf_cat(mrb, result, buf, 1); continue; } switch (c) { case '\n': cc = 'n'; break; case '\r': cc = 'r'; break; case '\t': cc = 't'; break; case '\f': cc = 'f'; break; case '\013': cc = 'v'; break; case '\010': cc = 'b'; break; case '\007': cc = 'a'; break; case 033: cc = 'e'; break; default: cc = 0; break; } if (cc) { buf[0] = '\\'; buf[1] = (char)cc; mrb_str_buf_cat(mrb, result, buf, 2); continue; } else { buf[0] = '\\'; buf[3] = '0' + c % 8; c /= 8; buf[2] = '0' + c % 8; c /= 8; buf[1] = '0' + c % 8; mrb_str_buf_cat(mrb, result, buf, 4); continue; } } mrb_str_buf_cat(mrb, result, "\"", 1); return result; } /* * call-seq: * str.bytes -> 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, s->len); unsigned char *p = (unsigned char *)(s->ptr), *pend = p + s->len; 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; s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); mrb_include_module(mrb, s, mrb_class_get(mrb, "Comparable")); 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_REQ(1)); /* 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_REQ(1)); /* 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_m, 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_m, 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_REQ(1)); /* 15.2.10.5.42 */ mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_REQ(1)); /* 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()); } mruby-0.0.0~20131214+git882afdea/src/symbol.c000066400000000000000000000242031225163307400201270ustar00rootroot00000000000000/* ** symbol.c - Symbol class ** ** See Copyright Notice in mruby.h */ #include #include #include #include "mruby.h" #include "mruby/khash.h" #include "mruby/string.h" /* ------------------------------------------------------ */ typedef struct symbol_name { size_t len; const char *name; } symbol_name; static inline khint_t sym_hash_func(mrb_state *mrb, const symbol_name s) { khint_t h = 0; size_t i; const char *p = s.name; for (i=0; iname2sym; symbol_name sname; khiter_t k; mrb_sym sym; char *p; sname.len = len; sname.name = name; k = kh_get(n2s, h, sname); if (k != kh_end(h)) return kh_value(h, k); sym = ++mrb->symidx; p = (char *)mrb_malloc(mrb, len+1); memcpy(p, name, len); p[len] = 0; sname.name = (const char*)p; k = kh_put(n2s, h, sname); kh_value(h, k) = sym; return sym; } mrb_sym mrb_intern_cstr(mrb_state *mrb, const char *name) { return mrb_intern(mrb, name, strlen(name)); } mrb_sym mrb_intern_str(mrb_state *mrb, mrb_value str) { return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } mrb_value mrb_check_intern(mrb_state *mrb, const char *name, size_t len) { khash_t(n2s) *h = mrb->name2sym; symbol_name sname; khiter_t k; sname.len = len; sname.name = name; k = kh_get(n2s, h, sname); if (k != kh_end(h)) { return mrb_symbol_value(kh_value(h, k)); } return mrb_nil_value(); } mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { return mrb_check_intern(mrb, name, strlen(name)); } 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 */ const char* mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, size_t *lenp) { khash_t(n2s) *h = mrb->name2sym; khiter_t k; symbol_name sname; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { if (kh_value(h, k) == sym) { sname = kh_key(h, k); *lenp = sname.len; return sname.name; } } } *lenp = 0; return NULL; /* missing */ } void mrb_free_symtbl(mrb_state *mrb) { khash_t(n2s) *h = mrb->name2sym; khiter_t k; for (k = kh_begin(h); k != kh_end(h); k++) if (kh_exist(h, k)) mrb_free(mrb, (char*)kh_key(h, k).name); kh_destroy(n2s,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_bool equal_p; mrb_get_args(mrb, "o", &sym2); equal_p = mrb_obj_equal(mrb, sym1, sym2); return mrb_bool_value(equal_p); } /* 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" */ mrb_value mrb_sym_to_s(mrb_state *mrb, mrb_value sym) { mrb_sym id = mrb_symbol(sym); const char *p; size_t 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; int 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 '!': if (*++m == '=') ++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; size_t len; mrb_sym id = mrb_symbol(sym); name = mrb_sym2name_len(mrb, id, &len); str = mrb_str_new(mrb, 0, len+1); RSTRING(str)->ptr[0] = ':'; memcpy(RSTRING(str)->ptr+1, name, len); if (!symname_p(name) || strlen(name) != len) { str = mrb_str_dump(mrb, str); memcpy(RSTRING(str)->ptr, ":\"", 2); } return str; } mrb_value mrb_sym2str(mrb_state *mrb, mrb_sym sym) { size_t len; const char *name = mrb_sym2name_len(mrb, sym, &len); mrb_value str; if (!name) return mrb_undef_value(); /* can't happen */ str = mrb_str_new_static(mrb, name, len); if (symname_p(name) && strlen(name) == len) { return str; } return mrb_str_dump(mrb, str); } const char* mrb_sym2name(mrb_state *mrb, mrb_sym sym) { size_t len; const char *name = mrb_sym2name_len(mrb, sym, &len); if (!name) return NULL; if (symname_p(name) && strlen(name) == len) { return name; } else { mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len)); return RSTRING(str)->ptr; } } #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; size_t 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; sym = mrb->symbol_class = mrb_define_class(mrb, "Symbol", mrb->object_class); 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-0.0.0~20131214+git882afdea/src/value_array.h000066400000000000000000000006421225163307400211420ustar00rootroot00000000000000#ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ #include "mruby.h" 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-0.0.0~20131214+git882afdea/src/variable.c000066400000000000000000000532361225163307400204170ustar00rootroot00000000000000/* ** variable.c - mruby variables ** ** See Copyright Notice in mruby.h */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/proc.h" #include "mruby/string.h" #include "mruby/variable.h" #include "error.h" #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)); if (t) { 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 the variable table. * * Parameters * mrb * t the variable table to be searched. * sym the symbol to be used as the key. * vp the value pointer. Recieves the value if the specified symbol contains * in the instance variable table. * Returns * true if the specfiyed symbol contains 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. Recieve the deleted value if the symbol contans * in the instance varible table. * Returns * true if the specfied symbol contains 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 "mruby/khash.h" #ifndef MRB_IVHASH_INIT_SIZE #define MRB_IVHASH_INIT_SIZE 8 #endif KHASH_DECLARE(iv, mrb_sym, mrb_value, 1) KHASH_DEFINE(iv, mrb_sym, mrb_value, 1, 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, 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, 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, h, sym); if (k != kh_end(h)) { mrb_value val = kh_value(h, k); kh_del(iv, 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, h, k); } } } } return TRUE; } static size_t iv_size(mrb_state *mrb, iv_tbl *t) { khash_t(iv) *h = &t->h; if (!h) return 0; return kh_size(h); } 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, &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: return TRUE; default: return FALSE; } } 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_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(); } 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); } 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); } 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_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_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); } void mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src) { struct RObject *d = mrb_obj_ptr(dest); struct RObject *s = mrb_obj_ptr(src); if (d->iv) { iv_free(mrb, d->iv); d->iv = 0; } if (s->iv) { 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; size_t len; mrb_value ins; /* need not to show internal data */ if (RSTRING_PTR(str)[0] == '-') { /* first element */ RSTRING_PTR(str)[0] = '#'; mrb_str_cat(mrb, str, " ", 1); } else { mrb_str_cat(mrb, str, ", ", 2); } s = mrb_sym2name_len(mrb, sym, &len); mrb_str_cat(mrb, str, s, len); mrb_str_cat(mrb, str, "=", 1); if (mrb_type(v) == MRB_TT_OBJECT) { ins = mrb_any_to_s(mrb, v); } else { ins = mrb_inspect(mrb, v); } mrb_str_append(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_buf_cat(mrb, str, "-<", 2); mrb_str_cat2(mrb, str, cn); mrb_str_cat(mrb, str, ":", 1); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj)); iv_foreach(mrb, t, inspect_i, &str); mrb_str_cat(mrb, str, ">", 1); return str; } return mrb_any_to_s(mrb, mrb_obj_value(obj)); } 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; size_t 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; size_t 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_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym) { struct RClass * cls = c; while (c) { if (c->iv) { iv_tbl *t = c->iv; mrb_value v; if (iv_get(mrb, t, 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_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym); } 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); } 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_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_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; 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; } c = mrb->c->ci->target_class; if (!c->iv) { c->iv = iv_new(mrb); } mrb_write_barrier(mrb, (struct RBasic*)c); iv_put(mrb, c->iv, sym, v); } mrb_bool mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) { struct RClass *m = mrb_class_ptr(mod); iv_tbl *t = m->iv; if (!t) return FALSE; return iv_get(mrb, t, sym, NULL); } 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 = 0; 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 = 1; 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_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; } 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); } 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); } void mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); mrb_iv_remove(mrb, mod, sym); } 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); } 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; size_t 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; struct RClass *c = mrb_class_ptr(mod); ary = mrb_ary_new(mrb); while (c) { if (c->iv) { iv_foreach(mrb, c->iv, const_i, &ary); } c = c->super; if (c == mrb->object_class) break; } return ary; } 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(); } 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); } 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_lit(mrb, buf))); } return ary; } static mrb_bool mrb_const_defined_0(mrb_state *mrb, struct RClass *klass, mrb_sym id, mrb_bool exclude, mrb_bool recurse) { 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; } int mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id) { return mrb_const_defined_0(mrb, klass, id, TRUE, FALSE); } 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-0.0.0~20131214+git882afdea/src/vm.c000066400000000000000000001540151225163307400172510ustar00rootroot00000000000000 /* ** vm.c - virtual machine for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/hash.h" #include "mruby/irep.h" #include "mruby/numeric.h" #include "mruby/proc.h" #include "mruby/range.h" #include "mruby/string.h" #include "mruby/variable.h" #include "error.h" #include "opcode.h" #include "value_array.h" #ifndef ENABLE_STDIO #if defined(__cplusplus) extern "C" { #endif void abort(void); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif #define SET_TRUE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_FALSE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_NIL_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_INT_VALUE(r,n) MRB_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_SYM_VALUE(r,v) MRB_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) MRB_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #ifdef MRB_NAN_BOXING #define SET_FLT_VALUE(mrb,r,v) r.f = (v) #elif defined(MRB_WORD_BOXING) #define SET_FLT_VALUE(mrb,r,v) r = mrb_float_value(mrb, (v)) #else #define SET_FLT_VALUE(mrb,r,v) MRB_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) #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 TO_STR(x) TO_STR_(x) #define TO_STR_(x) #x #define ARENA_RESTORE(mrb,ai) (mrb)->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; } static inline void envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) { mrb_callinfo *ci = mrb->c->cibase; while (ci <= mrb->c->ci) { struct REnv *e = ci->env; if (e && e->cioff >= 0) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; } ci++; } } /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ static void stack_extend_alloc(mrb_state *mrb, int room) { mrb_value *oldbase = mrb->c->stbase; int size = mrb->c->stend - mrb->c->stbase; int off = mrb->c->stack - mrb->c->stbase; /* Use linear stack growth. It is slightly slower than doubling the stack space, but it saves memory on small devices. */ if (room <= size) size += MRB_STACK_GROWTH; else size += room; 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) { mrb_raise(mrb, E_RUNTIME_ERROR, "stack level too deep. (limit=" TO_STR(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); } if (room > keep) { /* do not leave uninitialized malloc region */ stack_clear(&(mrb->c->stack[keep]), 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 (cioff >= 0 && 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 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) { size_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->nregs = 2; /* protect method_missing arg and block */ ci->eidx = eidx; ci->ridx = ridx; ci->env = 0; ci->pc = 0; return ci; } static void cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; if (c->ci->env) { struct REnv *e = c->ci->env; size_t len = (size_t)e->flags; mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); e->cioff = -1; stack_copy(p, e->stack, len); e->stack = p; } c->ci--; } static void ecall(mrb_state *mrb, int i) { struct RProc *p; mrb_callinfo *ci; mrb_value *self = mrb->c->stack; struct RObject *exc; p = mrb->c->ensure[i]; if (!p) return; ci = cipush(mrb); ci->stackidx = mrb->c->stack - mrb->c->stbase; 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_value mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, int argc, ...) { mrb_sym mid = mrb_intern_cstr(mrb, name); if (argc == 0) { return mrb_funcall_argv(mrb, self, mid, 0, 0); } else if (argc == 1) { mrb_value v; va_list ap; va_start(ap, argc); v = va_arg(ap, mrb_value); va_end(ap); return mrb_funcall_argv(mrb, self, mid, 1, &v); } else { mrb_value argv[MRB_FUNCALL_ARGC_MAX]; va_list ap; int i; if (argc > MRB_FUNCALL_ARGC_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" TO_STR(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_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, int argc, mrb_value *argv, mrb_value blk) { mrb_value val; if (!mrb->jmp) { jmp_buf c_jmp; mrb_callinfo *old_ci = mrb->c->ci; if (setjmp(c_jmp) != 0) { /* error */ while (old_ci != mrb->c->ci) { mrb->c->stack = mrb->c->stbase + mrb->c->ci->stackidx; cipop(mrb); } mrb->jmp = 0; val = mrb_obj_value(mrb->exc); } else { mrb->jmp = &c_jmp; /* recursive call */ val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk); mrb->jmp = 0; } } else { struct RProc *p; struct RClass *c; mrb_sym undef = 0; mrb_callinfo *ci; int n; 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->stackidx = mrb->c->stack - mrb->c->stbase; ci->argc = argc; ci->target_class = c; if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; } else { ci->nregs = p->body.irep->nregs + n; } mrb->c->stack = mrb->c->stack + n; stack_extend(mrb, ci->nregs, 0); mrb->c->stack[0] = self; if (undef) { mrb->c->stack[1] = mrb_symbol_value(undef); 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->stbase + mrb->c->ci->stackidx; 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_value mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, int argc, mrb_value *argv) { return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } mrb_value mrb_yield_internal(mrb_state *mrb, mrb_value b, int argc, 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->stackidx = mrb->c->stack - mrb->c->stbase; ci->argc = argc; ci->target_class = c; if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; } else { ci->nregs = p->body.irep->nregs + 1; } ci->acc = CI_ACC_SKIP; mrb->c->stack = mrb->c->stack + n; stack_extend(mrb, ci->nregs, 0); 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->stbase + mrb->c->ci->stackidx; cipop(mrb); } else { val = mrb_run(mrb, p, self); } return val; } mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_internal(mrb, b, argc, argv, p->env->stack[0], p->target_class); } mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_internal(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_buf_cat(mrb, msg, lead, sizeof(lead) - 1); mrb_str_buf_cat(mrb, msg, kind_str[kind], kind_str_len[kind]); exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); mrb->exc = mrb_obj_ptr(exc); } static void argnum_error(mrb_state *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 = mrb_obj_ptr(exc); } #define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; #define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; #ifdef ENABLE_DEBUG #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 #ifdef __GNUC__ #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 mrb_value mrb_gv_val_get(mrb_state *mrb, mrb_sym sym); void mrb_gv_val_set(mrb_state *mrb, mrb_sym sym, mrb_value val); #define CALL_MAXARGS 127 mrb_value mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { /* mrb_assert(mrb_proc_cfunc_p(proc)) */ mrb_irep *irep = proc->body.irep; mrb_code *pc = irep->iseq; mrb_value *pool = irep->pool; mrb_sym *syms = irep->syms; mrb_value *regs = NULL; mrb_code i; int ai = mrb_gc_arena_save(mrb); jmp_buf *prev_jmp = (jmp_buf *)mrb->jmp; jmp_buf 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 if (setjmp(c_jmp) == 0) { mrb->jmp = &c_jmp; } else { goto L_RAISE; } if (!mrb->c->stack) { stack_init(mrb); } stack_extend(mrb, irep->nregs, stack_keep); mrb->c->ci->err = pc; mrb->c->ci->proc = proc; mrb->c->ci->nregs = irep->nregs + 1; regs = mrb->c->stack; regs[0] = self; 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 Bx R(A) := sBx */ SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i)); NEXT; } CASE(OP_LOADSYM) { /* A B R(A) := Sym(B) */ 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 B R(A) := getglobal(Sym(B)) */ regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]); NEXT; } CASE(OP_SETGLOBAL) { /* setglobal(Sym(b), 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(Sym(B),R(A)) */ mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETCV) { /* A B R(A) := ivget(Sym(B)) */ 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) { /* ivset(Sym(B),R(A)) */ mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETCONST) { /* A B R(A) := constget(Sym(B)) */ 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 B constset(Sym(B),R(A)) */ mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); NEXT; } CASE(OP_GETMCNST) { /* A B C R(A) := R(C)::Sym(B) */ 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 B C R(A+1)::Sym(B) := 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)) */ /* A B C R(A) := uvget(B,C) */ 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) { int a = GETARG_A(i); while (a--) { mrb->c->ci->ridx--; } NEXT; } CASE(OP_RAISE) { /* A raise(R(A)) */ mrb->exc = mrb_obj_ptr(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 n; int a = GETARG_A(i); for (n=0; nc->ci->eidx); } ARENA_RESTORE(mrb, ai); NEXT; } CASE(OP_LOADNIL) { /* A B R(A) := nil */ int a = GETARG_A(i); SET_NIL_VALUE(regs[a]); NEXT; } CASE(OP_SENDB) { /* fall through */ }; L_SEND: CASE(OP_SEND) { /* A B C R(A) := call(R(A),Sym(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, 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); 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; } } /* push callinfo */ ci = cipush(mrb); ci->mid = mid; ci->proc = m; ci->stackidx = mrb->c->stack - mrb->c->stbase; if (n == CALL_MAXARGS) { ci->argc = -1; } else { ci->argc = n; } if (c->tt == MRB_TT_ICLASS) { ci->target_class = c->c; } else { 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->nregs = 3; } else { ci->nregs = n + 2; } result = m->body.func(mrb, recv); mrb->c->stack[0] = result; 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 (!MRB_PROC_CFUNC_P(ci[-1].proc)) { irep = ci[-1].proc->body.irep; pool = irep->pool; syms = irep->syms; } } regs = mrb->c->stack = mrb->c->stbase + ci->stackidx; 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 (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_FSEND) { /* A B C R(A) := fcall(R(A),Sym(B),R(A+1),... ,R(A+C)) */ 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 = mrb->c->stbase + ci->stackidx; 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 = m->body.irep->iseq; JUMP; } } CASE(OP_SUPER) { /* A B 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); 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->stackidx = mrb->c->stack - mrb->c->stbase; 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)) { 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->stbase + mrb->c->ci->stackidx; 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; static const char m[] = "super called outside of method"; exc = mrb_exc_new(mrb, E_NOMETHOD_ERROR, m, sizeof(m) - 1); mrb->exc = mrb_obj_ptr(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]); 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 (24=5:5:1:5:5:1:1) */ /* number of optional arguments times OP_JMP should follow */ mrb_aspec ax = GETARG_Ax(i); int m1 = (ax>>18)&0x1f; int o = (ax>>13)&0x1f; int r = (ax>>12)&0x1; int m2 = (ax>>7)&0x1f; /* unused int k = (ax>>2)&0x1f; int kd = (ax>>1)&0x1; int b = (ax>>0)& 0x1; */ 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 (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])) { argc = mrb_ary_ptr(argv[0])->len; argv = mrb_ary_ptr(argv[0])->ptr; } mrb->c->ci->argc = len; if (argc < len) { regs[len+1] = *blk; /* move block */ if (argv0 != argv) { value_move(®s[1], argv, argc-m2); /* m1 + o */ } if (m2) { int mlen = m2; if (argc-m2 <= m1) { mlen = argc - m1; } 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) pc++; else pc += argc - m1 - m2 + 1; } else { if (argv0 != argv) { regs[len+1] = *blk; /* move block */ value_move(®s[1], argv, m1+o); } if (r) { regs[m1+o+1] = mrb_ary_new_from_values(mrb, argc-m1-o-m2, argv+m1+o); } if (m2) { if (argc-m2 > m1) { value_move(®s[m1+o+r+1], &argv[argc-m2], m2); } } if (argv0 == argv) { regs[len+1] = *blk; /* move block */ } pc += o + 1; } JUMP; } CASE(OP_KARG) { /* A B C R(A) := kdict[Sym(B)]; if C kdict.rm(Sym(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 return R(A) */ 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 (eidx > ci[-1].eidx) { ecall(mrb, --eidx); } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); ci = mrb->c->ci; mrb->c->stack = mrb->c->stbase + ci[1].stackidx; if (ci[1].acc == CI_ACC_SKIP && prev_jmp) { mrb->jmp = prev_jmp; mrb_longjmp(mrb); } while (eidx > ci[-1].eidx) { ecall(mrb, --eidx); } if (ci == mrb->c->cibase) { if (ci->ridx == 0) { regs = mrb->c->stack = mrb->c->stbase; goto L_STOP; } break; } } L_RESCUE: irep = ci->proc->body.irep; pool = irep->pool; syms = irep->syms; regs = mrb->c->stack = mrb->c->stbase + ci[1].stackidx; 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 (e->cioff < 0) { 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->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(mrb, E_RUNTIME_ERROR, mrb_str_new(mrb, "double resume", 13)); mrb->exc = mrb_obj_ptr(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->cioff < 0) { localjump_error(mrb, LOCALJUMP_ERROR_BREAK); goto L_RAISE; } ci = mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; break; default: /* cannot happen */ break; } while (eidx > mrb->c->ci[-1].eidx) { ecall(mrb, --eidx); } cipop(mrb); acc = ci->acc; pc = ci->pc; regs = mrb->c->stack = mrb->c->stbase + ci->stackidx; if (acc == CI_ACC_SKIP) { mrb->jmp = prev_jmp; return v; } 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),Sym(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 attr_i value.i #ifdef MRB_NAN_BOXING #define attr_f f #elif defined(MRB_WORD_BOXING) #define attr_f value.fp->f #else #define attr_f value.f #endif #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) #define OP_MATH_BODY(op,v1,v2) do {\ regs[a].v1 = regs[a].v1 op regs[a+1].v2;\ } 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]); z = x + y; #ifdef MRB_WORD_BOXING z = (z << MRB_FIXNUM_SHIFT) / (1 << MRB_FIXNUM_SHIFT); #endif if ((x < 0) != (z < 0) && ((x < 0) ^ (y < 0)) == 0) { /* integer overflow */ SET_FLT_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_FLT_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_FLT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,attr_f,attr_i); #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_FLT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,attr_f,attr_f); #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]); z = x - y; #ifdef MRB_WORD_BOXING z = (z << MRB_FIXNUM_SHIFT) / (1 << MRB_FIXNUM_SHIFT); #endif if (((x < 0) ^ (y < 0)) != 0 && (x < 0) != (z < 0)) { /* integer overflow */ SET_FLT_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_FLT_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_FLT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,attr_f,attr_i); #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_FLT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,attr_f,attr_f); #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_int x, y, z; x = mrb_fixnum(regs[a]); y = mrb_fixnum(regs[a+1]); z = x * y; #ifdef MRB_WORD_BOXING z = (z << MRB_FIXNUM_SHIFT) / (1 << MRB_FIXNUM_SHIFT); #endif if (x != 0 && z/x != y) { SET_FLT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y); } else { 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_FLT_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_FLT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,attr_f,attr_i); #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_FLT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,attr_f,attr_f); #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_FLT_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_FLT_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_FLT_VALUE(mrb, regs[a], x / y); } #else OP_MATH_BODY(/,attr_f,attr_i); #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_FLT_VALUE(mrb, regs[a], x / y); } #else OP_MATH_BODY(/,attr_f,attr_f); #endif break; default: goto L_SEND; } 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 = regs[a].attr_i; mrb_int y = GETARG_C(i); mrb_int z = x + y; if (((x < 0) ^ (y < 0)) == 0 && (x < 0) != (z < 0)) { /* integer overflow */ SET_FLT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); break; } regs[a].attr_i = z; } break; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLT_VALUE(mrb, regs[a], x + GETARG_C(i)); } #else regs[a].attr_f += 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 = regs_a[0].attr_i; mrb_int y = GETARG_C(i); mrb_int z = x - y; if ((x < 0) != (z < 0) && ((x < 0) ^ (y < 0)) != 0) { /* integer overflow */ SET_FLT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); } else { regs_a[0].attr_i = z; } } break; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLT_VALUE(mrb, regs[a], x - GETARG_C(i)); } #else regs_a[0].attr_f -= 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) do {\ if (regs[a].v1 op regs[a+1].v2) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) #define OP_CMP(op) do {\ int a = GETARG_A(i);\ /* 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):\ OP_CMP_BODY(op,attr_i,attr_i);\ break;\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ OP_CMP_BODY(op,attr_i,attr_f);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ OP_CMP_BODY(op,attr_f,attr_i);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ OP_CMP_BODY(op,attr_f,attr_f);\ break;\ default:\ goto L_SEND;\ }\ } while(0) CASE(OP_EQ) { /* A B C R(A) := R(A)); NEXT; } CASE(OP_GE) { /* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/ 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); if (!mrb_array_p(v)) { regs[a++] = mrb_ary_new_capa(mrb, 0); while (post--) { SET_NIL_VALUE(regs[a]); a++; } } else { struct RArray *ary = mrb_ary_ptr(v); int len = ary->len; int i; 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 (i=0; i+preptr[pre+i]; } while (i < post) { SET_NIL_VALUE(regs[a+i]); i++; } } } 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)]); 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),Sym(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),Sym(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->stackidx = mrb->c->stack - mrb->c->stbase; 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)) { 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->stbase + mrb->c->ci->stackidx; 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(Sym(B),R(A+1)) */ int a = GETARG_A(i); struct RClass *c = mrb_class_ptr(regs[a]); mrb_define_method_vm(mrb, c, syms[GETARG_B(i)], regs[a+1]); 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 B R(A) := target_class */ if (!mrb->c->ci->target_class) { static const char msg[] = "no target class or module"; mrb_value exc = mrb_exc_new(mrb, E_TYPE_ERROR, msg, sizeof(msg) - 1); mrb->exc = mrb_obj_ptr(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 debug print R(A),R(B),R(C) */ #ifdef ENABLE_STDIO printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i)); #else abort(); #endif NEXT; } CASE(OP_STOP) { /* stop VM */ L_STOP: { int n = mrb->c->ci->eidx; while (n--) { ecall(mrb, n); } } mrb->c->ci->err = 0; 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 = mrb_obj_ptr(exc); goto L_RAISE; } } END_DISPATCH; } mrb_value mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) { return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ } void mrb_longjmp(mrb_state *mrb) { longjmp(*(jmp_buf*)mrb->jmp, 1); } mruby-0.0.0~20131214+git882afdea/tasks/000077500000000000000000000000001225163307400170135ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/tasks/libmruby.rake000066400000000000000000000015461225163307400215120ustar00rootroot00000000000000MRuby.each_target do file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t| archiver.run t.name, t.prerequisites open("#{build_dir}/lib/libmruby.flags.mak", 'w') do |f| f.puts 'MRUBY_CFLAGS = %s' % 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 = %s' % linker.all_flags(gem_library_paths, gem_flags).gsub('"', '\\"') gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } f.puts 'MRUBY_LDFLAGS_BEFORE_LIBS = %s' % [linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ').gsub('"', '\\"') gem_libraries = gems.map { |g| g.linker.libraries } f.puts 'MRUBY_LIBS = %s' % linker.library_flags(gem_libraries).gsub('"', '\\"') end end end mruby-0.0.0~20131214+git882afdea/tasks/mrbgem_spec.rake000066400000000000000000000206571225163307400221540ustar00rootroot00000000000000require 'pathname' require 'forwardable' 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 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 @build.compilers.each do |compiler| compiler.include_paths << "#{dir}/include" end 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,m,asm,S}").map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end @objs << objfile("#{build_dir}/gem_init") @test_rbfiles = Dir.glob("#{dir}/test/*.rb") @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,m,asm,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end @test_preload = 'test/assert.rb' @test_args = {} @bins = [] @requirements = [] @dependencies = [] 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}" end define_gem_init_builder end def add_dependency(name, *requirements) requirements = ['>= 0.0.0'] if requirements.empty? requirements.flatten! @dependencies << {:gem => name, :requirements => requirements} 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 "#{build_dir}/gem_init.c" => [build.mrbcfile] + [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_init_header(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[ */] f.puts %Q[#include ] f.puts %Q[#include "mruby.h"] f.puts %Q[#include "mruby/irep.h"] f.puts %Q[#include "mruby/dump.h"] f.puts %Q[#include "mruby/string.h"] f.puts %Q[#include "mruby/proc.h"] f.puts %Q[#include "mruby/variable.h"] f.puts %Q[#include "mruby/array.h"] f.puts %Q[#include "mruby/hash.h"] 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 check each do |g| g.dependencies.each do |dep| name = dep[:gem] req_versions = dep[:requirements] # check each GEM dependency against all available GEMs found_dep_gem = false each do |dep_g| if name == dep_g.name unless dep_g.version_ok?(req_versions) fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end found_dep_gem = true break end end fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found" unless found_dep_gem 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-0.0.0~20131214+git882afdea/tasks/mrbgems.rake000066400000000000000000000063201225163307400213140ustar00rootroot00000000000000MRuby.each_target do if enable_gems? # set up all gems gems.each(&:setup) gems.check # 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] do |t| FileUtils.mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| 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 "mruby.h"] f.puts %Q[] f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_init(mrb_state* mrb);" % g.funcname}.join("\n")}] f.puts %Q[] f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_final(mrb_state* mrb);" % g.funcname}.join("\n")}] f.puts %Q[] f.puts %Q[void] f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}] f.puts %Q[}] f.puts %Q[] f.puts %Q[void] f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}] f.puts %Q[}] end end end # legal documents file "#{build_dir}/LEGAL" => [MRUBY_CONFIG] do |t| open(t.name, 'w+') do |f| f.puts < g.test_rbireps file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile, libfile("#{build_dir}/lib/libmruby")] do |t| open(t.name, 'w') do |f| g.print_gem_init_header(f) test_preload = [g.dir, MRUBY_ROOT].map {|dir| File.expand_path(g.test_preload, dir) }.find {|file| File.exists?(file) } g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload" 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? 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;] if g.test_args.empty? f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2;] else f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2, 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();] f.puts %Q[ val3 = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_verbose"));] f.puts %Q[ if (mrb_test(val3)) {] f.puts %Q[ mrb_gv_set(mrb2, mrb_intern_cstr(mrb2, "$mrbtest_verbose"), val3);] f.puts %Q[ }] f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);] f.puts %Q[ if (mrb2->exc) {] f.puts %Q[ mrb_p(mrb2, mrb_obj_value(mrb2->exc));] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_cstr(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_cstr(mrb2, "TEST_ARGS"), test_args_hash); ] end f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] unless g.test_objs.empty? f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});] f.puts %Q[ if (mrb2->exc) {] f.puts %Q[ mrb_p(mrb2, mrb_obj_value(mrb2->exc));] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] f.puts %Q[ ] %w(ok_test ko_test kill_test).each do |vname| f.puts %Q[ val2 = mrb_gv_get(mrb2, mrb_intern_cstr(mrb2, "$#{vname}"));] f.puts %Q[ if (mrb_fixnum_p(val2)) {] f.puts %Q[ val1 = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$#{vname}"));] f.puts %Q[ mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$#{vname}"), mrb_fixnum_value(mrb_fixnum(val1) + mrb_fixnum(val2)));] f.puts %Q[ }\n] end f.puts %Q[ ary2 = mrb_gv_get(mrb2, mrb_intern_cstr(mrb2, "$asserts"));] f.puts %Q[ if (mrb_test(ary2)) {] f.puts %Q[ ary1 = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$asserts"));] f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);] f.puts %Q[ ] f.puts %Q[ while (mrb_test(val2)) {] f.puts %Q[ char *str = mrb_string_value_cstr(mrb2, &val2);] f.puts %Q[ mrb_ary_push(mrb, ary1, mrb_str_new_cstr(mrb, str));] f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);] f.puts %Q[ }] f.puts %Q[ }] f.puts %Q[ mrb_close(mrb2);] f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] end end f.puts %Q[}] end end end end mruby-0.0.0~20131214+git882afdea/tasks/mruby_build.rake000066400000000000000000000127621225163307400222040ustar00rootroot00000000000000load "#{MRUBY_ROOT}/tasks/mruby_build_gem.rake" load "#{MRUBY_ROOT}/tasks/mruby_build_commands.rake" module MRuby class << self def targets @targets ||= {} end def each_target(&block) @targets.each do |key, target| target.instance_eval(&block) end end end class Toolchain class << self attr_accessor :toolchains end def initialize(name, &block) @name, @initializer = name.to_s, block MRuby::Toolchain.toolchains ||= {} MRuby::Toolchain.toolchains[@name] = self end def setup(conf) conf.instance_eval(&@initializer) end def self.load Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file| Kernel.load file end end end Toolchain.load class Build class << self attr_accessor :current end include Rake::DSL include LoadGems attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir attr_reader :libmruby, :gems COMPILERS = %w(cc cxx objc asm) COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc) attr_block MRuby::Build::COMMANDS Exts = Struct.new(:object, :executable, :library) def initialize(name='host', &block) @name = name.to_s unless MRuby.targets[@name] if ENV['OS'] == 'Windows_NT' @exts = Exts.new('.o', '.exe', '.a') else @exts = Exts.new('.o', '', '.a') end @file_separator = '/' @build_dir = "#{MRUBY_ROOT}/build/#{@name}" @gem_clone_dir = "#{MRUBY_ROOT}/build/mrbgems" @cc = Command::Compiler.new(self, %w(.c)) @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp)) @objc = Command::Compiler.new(self, %w(.m)) @asm = Command::Compiler.new(self, %w(.S .asm)) @linker = Command::Linker.new(self) @archiver = Command::Archiver.new(self) @yacc = Command::Yacc.new(self) @gperf = Command::Gperf.new(self) @git = Command::Git.new(self) @mrbc = Command::Mrbc.new(self) @bins = %w(mrbc) @gems, @libmruby = MRuby::Gem::List.new, [] @build_mrbtest_lib_only = false MRuby.targets[@name] = self end MRuby::Build.current = MRuby.targets[@name] MRuby.targets[@name].instance_eval(&block) end def enable_debug compilers.each { |c| c.defines += %w(MRB_DEBUG) } @mrbc.compile_options += ' -g' end def toolchain(name) tc = Toolchain.toolchains[name.to_s] fail "Unknown #{name} toolchain" unless tc tc.setup(self) end def root MRUBY_ROOT end def mrbcfile MRuby.targets[@name].exefile("#{MRuby.targets[@name].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}/test/mrbtest") sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}" puts 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 puts " #{gem.name} #{gem_version}" puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty? end end puts "================================================" puts end end # Build class CrossBuild < Build attr_block %w(test_runner) def initialize(name, &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}/test/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-0.0.0~20131214+git882afdea/tasks/mruby_build_commands.rake000066400000000000000000000224741225163307400240660ustar00rootroot00000000000000require '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 private def _run(options, params={}) sh build.filename(command) + ' ' + ( options % params ) 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 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 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) } ] 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) } ] 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.exists?(file) File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten else [] end 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 def initialize(build) super @command = 'git' @flags = [] @clone_options = "clone %{flags} %{url} %{dir}" @pull_options = "pull" 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 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 unless $?.exitstatus 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-0.0.0~20131214+git882afdea/tasks/mruby_build_gem.rake000066400000000000000000000040631225163307400230270ustar00rootroot00000000000000module MRuby module LoadGems def gembox(gemboxfile) gembox = File.expand_path("#{gemboxfile}.gembox", "#{MRUBY_ROOT}/mrbgems") fail "Can't find gembox '#{gembox}'" unless File.exists?(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.exists?(gemrake) Gem.current = nil load gemrake return nil unless Gem.current Gem.current.dir = gemdir Gem.current.build = MRuby::Build.current Gem.current.build_config_initializer = block gems << Gem.current Gem.current end def load_special_path_gem(params) if params[:github] params[:git] = "https://github.com/#{params[:github]}.git" elsif params[:bitbucket] params[:git] = "https://bitbucket.org/#{params[:bitbucket]}.git" end if params[:core] gemdir = "#{root}/mrbgems/#{params[:core]}" elsif params[:git] url = params[:git] gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}" if File.exists?(gemdir) if $pull_gems git.run_pull gemdir, url else gemdir end else options = [params[:options]] || [] options << "--branch \"#{params[:branch]}\"" if params[:branch] FileUtils.mkdir_p "#{gem_clone_dir}" git.run_clone gemdir, url, options end else fail "unknown gem option #{params}" end gemdir end def enable_gems? !@gems.empty? end end # LoadGems end # MRuby mruby-0.0.0~20131214+git882afdea/tasks/ruby_ext.rake000066400000000000000000000026331225163307400215240ustar00rootroot00000000000000class 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 $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-0.0.0~20131214+git882afdea/tasks/toolchains/000077500000000000000000000000001225163307400211565ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/tasks/toolchains/androideabi.rake000066400000000000000000000114721225163307400242700ustar00rootroot00000000000000# Download and unarchive latest Android NDK from https://developer.android.com/tools/sdk/ndk/index.html # Make custom standalone toolchain as described here (android_ndk/docs/STANDALONE-TOOLCHAIN.html) # Please export custom standalone toolchain path # export ANDROID_STANDALONE_TOOLCHAIN=/tmp/android-14-toolchain # Add to your build_config.rb # MRuby::CrossBuild.new('androideabi') do |conf| # toolchain :androideabi # end MRuby::Toolchain.new(:androideabi) do |conf| toolchain :gcc DEFAULT_ANDROID_TOOLCHAIN = 'gcc' DEFAULT_ANDROID_TARGET_ARCH = 'arm' DEFAULT_ANDROID_TARGET_ARCH_ABI = 'armeabi' DEFAULT_ANDROID_TARGET_PLATFORM = 'android-14' DEFAULT_GCC_VERSION = '4.6' DEFAULT_CLANG_VERSION = '3.1' GCC_COMMON_CFLAGS = %W(-ffunction-sections -funwind-tables -fstack-protector) GCC_COMMON_LDFLAGS = %W() # 'ANDROID_STANDALONE_TOOLCHAIN' or 'ANDROID_NDK_HOME' must be set. ANDROID_STANDALONE_TOOLCHAIN = ENV['ANDROID_STANDALONE_TOOLCHAIN'] ANDROID_NDK_HOME = ENV['ANDROID_NDK_HOME'] ANDROID_TARGET_ARCH = ENV['ANDROID_TARGET_ARCH'] || DEFAULT_ANDROID_TARGET_ARCH ANDROID_TARGET_ARCH_ABI = ENV['ANDROID_TARGET_ARCH_ABI'] || DEFAULT_ANDROID_TARGET_ARCH_ABI ANDROID_TOOLCHAIN = ENV['ANDROID_TOOLCHAIN'] || DEFAULT_ANDROID_TOOLCHAIN case ANDROID_TARGET_ARCH.downcase when 'arch-arm', 'arm' then toolchain_prefix = 'arm-linux-androideabi-' when 'arch-x86', 'x86' then toolchain_prefix = 'i686-linux-android-' when 'arch-mips', 'mips' then toolchain_prefix = 'mipsel-linux-android-' else # Any other architectures are not supported by Android NDK. # Notify error. end if ANDROID_STANDALONE_TOOLCHAIN == nil then case RUBY_PLATFORM when /cygwin|mswin|mingw|bccwin|wince|emx/i HOST_PLATFORM = 'windows' when /x86_64-darwin/i HOST_PLATFORM = 'darwin-x86_64' when /darwin/i HOST_PLATFORM = 'darwin-x86' when /x86_64-linux/i HOST_PLATFORM = 'linux-x86_64' when /linux/i HOST_PLATFORM = 'linux-x86' else # Unknown host platform end ANDROID_TARGET_PLATFORM = ENV['ANDROID_TARGET_PLATFORM'] || DEFAULT_ANDROID_TARGET_PLATFORM path_to_toolchain = ANDROID_NDK_HOME + '/toolchains/' path_to_sysroot = ANDROID_NDK_HOME + '/platforms/' + ANDROID_TARGET_PLATFORM if ANDROID_TOOLCHAIN.downcase == 'gcc' then case ANDROID_TARGET_ARCH.downcase when 'arch-arm', 'arm' then path_to_toolchain += 'arm-linux-androideabi-' path_to_sysroot += '/arch-arm' when 'arch-x86', 'x86' then path_to_toolchain += 'x86-' path_to_sysroot += '/arch-x86' when 'arch-mips', 'mips' then path_to_toolchain += 'mipsel-linux-android-' path_to_sysroot += '/arch-mips' else # Any other architecture are not supported by Android NDK. end path_to_toolchain += DEFAULT_GCC_VERSION + '/prebuilt/' + HOST_PLATFORM else path_to_toolchain += 'llvm-' + DEFAULT_CLANG_VERSION + '/prebuilt/' + HOST_PLATFORM end else path_to_toolchain = ANDROID_STANDALONE_TOOLCHAIN path_to_sysroot = ANDROID_STANDALONE_TOOLCHAIN + '/sysroot' end SYSROOT = path_to_sysroot case ANDROID_TARGET_ARCH.downcase when 'arch-arm', 'arm' then if ANDROID_TARGET_ARCH_ABI.downcase == 'armeabi-v7a' then ARCH_CFLAGS = %W(-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16) ARCH_LDFLAGS = %W(-march=armv7-a -Wl,--fix-cortex-a8) else ARCH_CFLAGS = %W(-march=armv5te -mtune=xscale -msoft-float) ARCH_LDFLAGS = %W() end when 'arch-x86', 'x86' then ARCH_CFLAGS = %W() ARCH_LDFLAGS = %W() when 'arch-mips', 'mips' then ARCH_CFLAGS = %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers) ARCH_LDFLAGS = %W() else # Notify error end case ANDROID_TOOLCHAIN.downcase when 'gcc' then ANDROID_CC = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc' ANDROID_LD = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc' ANDROID_AR = path_to_toolchain + '/bin/' + toolchain_prefix + 'ar' ANDROID_CFLAGS = GCC_COMMON_CFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_CFLAGS ANDROID_LDFLAGS = GCC_COMMON_LDFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_LDFLAGS when 'clang' then # clang is not supported yet. when 'clang31', 'clang3.1' then # clang is not supported yet. else # Any other toolchains are not supported by Android NDK. # Notify error. end [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || ANDROID_CC cc.flags = [ENV['CFLAGS'] || ANDROID_CFLAGS] end conf.linker.command = ENV['LD'] || ANDROID_LD conf.linker.flags = [ENV['LDFLAGS'] || ANDROID_LDFLAGS] conf.archiver.command = ENV['AR'] || ANDROID_AR end mruby-0.0.0~20131214+git882afdea/tasks/toolchains/clang.rake000066400000000000000000000003161225163307400231060ustar00rootroot00000000000000MRuby::Toolchain.new(:clang) do |conf| toolchain :gcc [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'clang' end conf.linker.command = ENV['LD'] || 'clang' end mruby-0.0.0~20131214+git882afdea/tasks/toolchains/gcc.rake000066400000000000000000000023021225163307400225530ustar00rootroot00000000000000MRuby::Toolchain.new(:gcc) do |conf| [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)] 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['CXX'] || 'g++' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)] 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['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 end mruby-0.0.0~20131214+git882afdea/tasks/toolchains/visualcpp.rake000066400000000000000000000026751225163307400240420ustar00rootroot00000000000000MRuby::Toolchain.new(:visualcpp) do |conf| [conf.cc, conf.cxx].each do |cc| cc.command = ENV['CC'] || 'cl.exe' cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /D_DEBUG /MDd /Zi /Od /RTC1 /DHAVE_STRING_H /DNO_GETTIMEOFDAY /D_CRT_SECURE_NO_WARNINGS)] 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} /Fo%{outfile} %{infile}" end conf.linker do |linker| linker.command = ENV['LD'] || 'link.exe' linker.flags = [ENV['LDFLAGS'] || %w(/nologo)] 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-0.0.0~20131214+git882afdea/test/000077500000000000000000000000001225163307400166455ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/test/README.md000066400000000000000000000001721225163307400201240ustar00rootroot00000000000000Running Tests ============= To run the tests, execute the following from the project's root directory. $ make test mruby-0.0.0~20131214+git882afdea/test/assert.rb000066400000000000000000000134461225163307400205030ustar00rootroot00000000000000$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 begin __printstr__ args[i].to_s rescue NoMethodError __t_printstr__ args[i].to_s end i += 1 end end ## # Create the assertion in a readable way def assertion_string(err, str, iso=nil, e=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 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 if e.class.to_s == 'MRubyTestSkip' $asserts.push "Skip: #{str} #{iso} #{e.cause}" t_print('?') else $asserts.push(assertion_string('Error: ', str, iso, e)) $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 if !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 ## # 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) 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_print(" Time: #{Time.now - $test_start} 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-0.0.0~20131214+git882afdea/test/driver.c000066400000000000000000000045701225163307400203120ustar00rootroot00000000000000/* ** 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 void mrb_init_mrbtest(mrb_state *); /* Print a short remark for the user */ static void print_hint(void) { printf("mrbtest - Embeddable Ruby Test\n"); printf("\nThis is a very early version, please test and report errors.\n"); printf("Thanks :)\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(mrb, "$ko_test", 8)); mrb_value kill_test = mrb_gv_get(mrb, mrb_intern(mrb, "$kill_test", 10)); 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) { mrb_value return_value; const char *prog = "report()"; /* evaluate the test */ return_value = mrb_load_string(mrb, prog); /* did an exception occur? */ if (mrb->exc) { mrb_p(mrb, return_value); 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) { struct RString *str; char *s; int len; if (mrb_string_p(obj)) { str = mrb_str_ptr(obj); s = str->ptr; len = str->len; 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; } int main(int argc, char **argv) { mrb_state *mrb; struct RClass *krn; int ret; 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"); mrb_gv_set(mrb, mrb_intern(mrb, "$mrbtest_verbose", 16), mrb_true_value()); } krn = mrb->kernel_module; mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1)); mrb_init_mrbtest(mrb); ret = eval_test(mrb); mrb_close(mrb); return ret; } mruby-0.0.0~20131214+git882afdea/test/init_mrbtest.c000066400000000000000000000006611225163307400215170ustar00rootroot00000000000000#include #include "mruby.h" #include "mruby/irep.h" #include "mruby/dump.h" #include "mruby/string.h" #include "mruby/proc.h" extern const uint8_t mrbtest_irep[]; void mrbgemtest_init(mrb_state* mrb); void mrb_init_mrbtest(mrb_state *mrb) { mrb_load_irep(mrb, mrbtest_irep); #ifndef DISABLE_GEMS mrbgemtest_init(mrb); #endif if (mrb->exc) { mrb_p(mrb, mrb_obj_value(mrb->exc)); exit(EXIT_FAILURE); } } mruby-0.0.0~20131214+git882afdea/test/mrbtest.rake000066400000000000000000000035741225163307400212020ustar00rootroot00000000000000MRuby.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}" exec = exefile("#{current_build_dir}/mrbtest") clib = "#{current_build_dir}/mrbtest.c" mlib = clib.ext(exts.object) mrbs = Dir.glob("#{current_dir}/t/*.rb") init = "#{current_dir}/init_mrbtest.c" asslib = "#{current_dir}/assert.rb" mrbtest_lib = libfile("#{current_build_dir}/mrbtest") file mrbtest_lib => [mlib, gems.map(&:test_objs), gems.map { |g| g.test_rbireps.ext(exts.object) }].flatten do |t| archiver.run t.name, t.prerequisites end unless build_mrbtest_lib_only? driver_obj = objfile("#{current_build_dir}/driver") file exec => [driver_obj, mrbtest_lib, 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 end end file mlib => [clib] file clib => [mrbcfile, init, asslib] + mrbs do |t| _pp "GEN", "*.rb", "#{clib.relative_path}" FileUtils.mkdir_p File.dirname(clib) open(clib, 'w') do |f| f.puts IO.read(init) mrbc.run f, [asslib] + mrbs, 'mrbtest_irep' 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) {] gems.each do |g| f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] end f.puts %Q[}] end end end mruby-0.0.0~20131214+git882afdea/test/report.rb000066400000000000000000000001541225163307400205050ustar00rootroot00000000000000report if $ko_test > 0 or $kill_test > 0 raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})" end mruby-0.0.0~20131214+git882afdea/test/t/000077500000000000000000000000001225163307400171105ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/test/t/argumenterror.rb000066400000000000000000000006171225163307400223350ustar00rootroot00000000000000## # 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 assert('ArgumentError superclass', '15.2.24.2') do assert_equal(StandardError, ArgumentError.superclass) end mruby-0.0.0~20131214+git882afdea/test/t/array.rb000066400000000000000000000203201225163307400205500ustar00rootroot00000000000000## # Array ISO Test assert('Array', '15.2.12') do assert_equal(Class, Array.class) end assert('Array superclass', '15.2.12.2') do assert_equal(Object, Array.superclass) 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)) 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_equal(4, [1,2,3].[]=(1,4)) assert_equal(3, [1,2,3].[]=(1,2,3)) 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] a.delete_at(1) assert_equal([1,3], 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)) 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)) 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) 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-0.0.0~20131214+git882afdea/test/t/basicobject.rb000066400000000000000000000002471225163307400217100ustar00rootroot00000000000000## # BasicObject assert('BasicObject') do assert_equal(Class, BasicObject.class) end assert('BasicObject superclass') do assert_nil(BasicObject.superclass) end mruby-0.0.0~20131214+git882afdea/test/t/bs_block.rb000066400000000000000000000142441225163307400212200ustar00rootroot00000000000000## # 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 mruby-0.0.0~20131214+git882afdea/test/t/bs_literal.rb000066400000000000000000000011171225163307400215550ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/test/t/class.rb000066400000000000000000000105611225163307400205450ustar00rootroot00000000000000## # Class ISO Test assert('Class', '15.2.3') do assert_equal(Class, Class.class) end assert('Class superclass', '15.2.3.2') do assert_equal(Module, Class.superclass) 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) end assert('class to return the last value') do m = class C; :m end assert_equal(m, :m) 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 mruby-0.0.0~20131214+git882afdea/test/t/comparable.rb000066400000000000000000000020621225163307400215420ustar00rootroot00000000000000 assert('Comparable#<', '15.3.3.2.1') do class Foo include Comparable def <=>(x) 0 end end assert_false(Foo.new < Foo.new) end assert('Comparable#<=', '15.3.3.2.2') do class Foo include Comparable def <=>(x) 0 end end assert_true(Foo.new <= Foo.new) 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) 0 end end assert_false(Foo.new > Foo.new) end assert('Comparable#>=', '15.3.3.2.5') do class Foo include Comparable def <=>(x) 0 end end assert_true(Foo.new >= Foo.new) 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-0.0.0~20131214+git882afdea/test/t/enumerable.rb000066400000000000000000000052141225163307400215560ustar00rootroot00000000000000## # 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?) end assert('Enumerable#any?', '15.3.2.2.2') do assert_true([false,true,false].any?) assert_false([false,false,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_true [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_true [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#partion', '15.3.2.2.16') do [0,1,2,3,4,5,6,7,8,9].partition do |i| i % 2 == 0 end == [[0,2,4,6,8], [1,3,5,7,9]] end assert('Enumerable#reject', '15.3.2.2.17') do [0,1,2,3,4,5,6,7,8,9].reject do |i| i % 2 == 0 end == [1,3,5,7,9] 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-0.0.0~20131214+git882afdea/test/t/exception.rb000066400000000000000000000104541225163307400214370ustar00rootroot00000000000000## # Exception ISO Test assert('Exception', '15.2.22') do assert_equal Class, Exception.class end assert('Exception superclass', '15.2.22.2') do assert_equal Object, Exception.superclass 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 e1 = Exception.exception() e2 = Exception.exception('b') assert_equal Exception, e1.class assert_equal Exception, e2.class 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('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 begin 1+1 ensure 2+2 end == 2 end assert('Exception 2') do begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 end == 4 end assert('Exception 3') do begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 begin 5+5 ensure 6+6 end end == 4 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 begin 1 rescue 2 else 3 end == 3 end assert('Exception 9') do begin 1+1 rescue 2+2 else 3+3 ensure 4+4 end == 6 end assert('Exception 10') do begin 1+1 begin 2+2 rescue 3+3 else 4+4 end rescue 5+5 else 6+6 ensure 7+7 end == 12 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 e.message == "foo" end end assert('Exception 17') do begin raise "a" # StandardError rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end == 2 end assert('Exception 18') do begin 0 rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end == 3 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 Exception.new.inspect end assert('Exception#backtrace') do begin raise "get backtrace" rescue => e e.backtrace end true end mruby-0.0.0~20131214+git882afdea/test/t/false.rb000066400000000000000000000012251225163307400205270ustar00rootroot00000000000000## # FalseClass ISO Test assert('FalseClass', '15.2.6') do assert_equal Class, FalseClass.class end assert('FalseClass superclass', '15.2.6.2') do assert_equal Object, FalseClass.superclass end assert('FalseClass false', '15.2.6.1') do assert_false false 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-0.0.0~20131214+git882afdea/test/t/float.rb000066400000000000000000000053201225163307400205420ustar00rootroot00000000000000## # Float ISO Test assert('Float', '15.2.9') do assert_equal Class, Float.class end assert('Float superclass', '15.2.9.2') do assert_equal Numeric, Float.superclass 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) 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) 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 mruby-0.0.0~20131214+git882afdea/test/t/gc.rb000066400000000000000000000015011225163307400200230ustar00rootroot00000000000000# 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-0.0.0~20131214+git882afdea/test/t/hash.rb000066400000000000000000000144461225163307400203710ustar00rootroot00000000000000## # Hash ISO Test assert('Hash', '15.2.13') do assert_equal Class, Hash.class end assert('Hash superclass', '15.2.13.2') do assert_equal Object, Hash.superclass end assert('Hash#==', '15.2.13.4.1') do assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' }) assert_false({ 'abc' => 'abc' } == { 'cba' => 'cba' }) 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#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) 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) end assert('Hash#shift', '15.2.13.4.24') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = a.shift assert_equal({ 'abc_key' => 'abc_value' }, a) assert_equal [ 'cba_key', 'cba_value' ], b 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#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 mruby-0.0.0~20131214+git882afdea/test/t/indexerror.rb000066400000000000000000000003211225163307400216120ustar00rootroot00000000000000## # IndexError ISO Test assert('IndexError', '15.2.33') do assert_equal Class, IndexError.class end assert('IndexError superclass', '15.2.33.2') do assert_equal StandardError, IndexError.superclass end mruby-0.0.0~20131214+git882afdea/test/t/integer.rb000066400000000000000000000066561225163307400211070ustar00rootroot00000000000000## # Integer ISO Test assert('Integer', '15.2.8') do assert_equal Class, Integer.class end assert('Integer superclass', '15.2.8.2') do assert_equal Numeric, Integer.superclass 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 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 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 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 assert_equal 0, a assert_equal 0.0, b assert_equal 2, c end assert('Integer#<=>', '15.2.8.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 # Raise when shift is too large assert_raise(RangeError) do 2 << 128 end 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 # Raise when shift is too large assert_raise(RangeError) do 2 >> -128 end 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 # 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-0.0.0~20131214+git882afdea/test/t/kernel.rb000066400000000000000000000177221225163307400207260ustar00rootroot00000000000000## # 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_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#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 assert_equal Fixnum, object_id.class 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 # 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_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#!=') do str1 = "hello" str2 = str1 str3 = "world" assert_false (str1[1] != 'e') assert_true (str1 != str3) assert_false (str2 != str1) 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 mruby-0.0.0~20131214+git882afdea/test/t/literals.rb000066400000000000000000000136301225163307400212570ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/test/t/localjumperror.rb000066400000000000000000000004711225163307400224770ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/test/t/methods.rb000066400000000000000000000054251225163307400211060ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/test/t/module.rb000066400000000000000000000267531225163307400207370ustar00rootroot00000000000000## # Module ISO Test assert('Module', '15.2.2') do assert_equal Class, Module.class end assert('Module superclass', '15.2.2.2') do assert_equal Object, Module.superclass 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?(:vattr=) 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#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) end # Not ISO specified 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 mruby-0.0.0~20131214+git882afdea/test/t/nameerror.rb000066400000000000000000000012701225163307400214270ustar00rootroot00000000000000## # NameError ISO Test assert('NameError', '15.2.31') do assert_equal Class, NameError.class end assert('NameError superclass', '15.2.31.2') do assert_equal StandardError, NameError.superclass 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-0.0.0~20131214+git882afdea/test/t/nil.rb000066400000000000000000000010221225163307400202120ustar00rootroot00000000000000## # NilClass ISO Test assert('NilClass', '15.2.4') do assert_equal Class, NilClass.class 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-0.0.0~20131214+git882afdea/test/t/nomethoderror.rb000066400000000000000000000004441225163307400223260ustar00rootroot00000000000000## # NoMethodError ISO Test assert('NoMethodError', '15.2.32') do NoMethodError.class == Class assert_raise NoMethodError do doesNotExistAsAMethodNameForVerySure("") end end assert('NoMethodError superclass', '15.2.32.2') do assert_equal NameError, NoMethodError.superclass end mruby-0.0.0~20131214+git882afdea/test/t/numeric.rb000066400000000000000000000007531225163307400211040ustar00rootroot00000000000000## # Numeric ISO Test assert('Numeric', '15.2.7') do assert_equal Class, Numeric.class end assert('Numeric superclass', '15.2.7.2') do assert_equal Object, Numeric.superclass 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 # Not ISO specified assert('Numeric#**') do assert_equal 8.0, 2.0**3 end mruby-0.0.0~20131214+git882afdea/test/t/object.rb000066400000000000000000000002721225163307400207040ustar00rootroot00000000000000## # 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-0.0.0~20131214+git882afdea/test/t/proc.rb000066400000000000000000000032151225163307400204010ustar00rootroot00000000000000## # Proc ISO Test assert('Proc', '15.2.17') do assert_equal Class, Proc.class end assert('Proc superclass', '15.2.17.2') do assert_equal Object, Proc.superclass 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 a, 1 assert_equal a2, 5 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 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#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 mruby-0.0.0~20131214+git882afdea/test/t/range.rb000066400000000000000000000031461225163307400205350ustar00rootroot00000000000000## # Range ISO Test assert('Range', '15.2.14') do assert_equal Class, Range.class end assert('Range superclass', '15.2.14.2') do assert_equal Object, Range.superclass 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#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-0.0.0~20131214+git882afdea/test/t/rangeerror.rb000066400000000000000000000003211225163307400215770ustar00rootroot00000000000000## # RangeError ISO Test assert('RangeError', '15.2.26') do assert_equal Class, RangeError.class end assert('RangeError superclass', '15.2.26.2') do assert_equal StandardError, RangeError.superclass end mruby-0.0.0~20131214+git882afdea/test/t/regexperror.rb000066400000000000000000000001211225163307400217730ustar00rootroot00000000000000## # RegexpError ISO Test # TODO broken ATM assert('RegexpError', '15.2.27') do mruby-0.0.0~20131214+git882afdea/test/t/runtimeerror.rb000066400000000000000000000001561225163307400221740ustar00rootroot00000000000000## # RuntimeError ISO Test assert('RuntimeError', '15.2.28') do assert_equal Class, RuntimeError.class end mruby-0.0.0~20131214+git882afdea/test/t/standarderror.rb000066400000000000000000000003341225163307400223070ustar00rootroot00000000000000## # StandardError ISO Test assert('StandardError', '15.2.23') do assert_equal Class, StandardError.class end assert('StandardError superclass', '15.2.23.2') do assert_equal Exception, StandardError.superclass end mruby-0.0.0~20131214+git882afdea/test/t/string.rb000066400000000000000000000223161225163307400207470ustar00rootroot00000000000000## # String ISO Test assert('String', '15.2.10') do assert_equal Class, String.class end assert('String superclass', '15.2.10.2') do assert_equal Object, String.superclass 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 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 end # 'String#=~', '15.2.10.5.5' will be tested in mrbgems. 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] # 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_nil a1 assert_nil b1 assert_nil c1 assert_equal '', d1 assert_equal 'bc', e1 assert_equal 'bc', a3 assert_nil b3 end 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] 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] 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 '', 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 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 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!', '15.2.10.5.12') do a = '' b = 'abc' a.chop! b.chop! assert_equal a, '' assert_equal b, 'ab' end 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 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!', '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 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 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!', '15.2.10.5.30') do a = 'abc' a.reverse! assert_equal 'cba', a assert_equal 'cba', 'abc'.reverse! end 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 # '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#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#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!', '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) assert_equal 0, a assert_equal 32143, b assert_equal 10, c assert_equal 4, d 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 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#inspect') do ("\1" * 100).inspect # should not raise an exception - regress #1210 assert_equal "\"\\000\"", "\0".inspect end mruby-0.0.0~20131214+git882afdea/test/t/symbol.rb000066400000000000000000000010121225163307400207340ustar00rootroot00000000000000## # Symbol ISO Test assert('Symbol', '15.2.11') do assert_equal Class, Symbol.class end assert('Symbol superclass', '15.2.11.2') do assert_equal Object, Symbol.superclass 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-0.0.0~20131214+git882afdea/test/t/syntax.rb000066400000000000000000000023311225163307400207620ustar00rootroot00000000000000assert('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('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 mass 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 end mruby-0.0.0~20131214+git882afdea/test/t/true.rb000066400000000000000000000011761225163307400204210ustar00rootroot00000000000000## # TrueClass ISO Test assert('TrueClass', '15.2.5') do assert_equal Class, TrueClass.class end assert('TrueClass superclass', '15.2.5.2') do assert_equal Object, TrueClass.superclass end assert('TrueClass true', '15.2.5.1') do assert_true true 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-0.0.0~20131214+git882afdea/test/t/typeerror.rb000066400000000000000000000003151225163307400214670ustar00rootroot00000000000000## # TypeError ISO Test assert('TypeError', '15.2.29') do assert_equal Class, TypeError.class end assert('TypeError superclass', '15.2.29.2') do assert_equal StandardError, TypeError.superclass end mruby-0.0.0~20131214+git882afdea/tools/000077500000000000000000000000001225163307400170265ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/tools/mrbc/000077500000000000000000000000001225163307400177515ustar00rootroot00000000000000mruby-0.0.0~20131214+git882afdea/tools/mrbc/mrbc.c000066400000000000000000000164451225163307400210520ustar00rootroot00000000000000#include #include #include #include "mruby.h" #include "mruby/compile.h" #include "mruby/dump.h" #include "mruby/proc.h" #define RITEBIN_EXT ".mrb" #define C_EXT ".c" void mrb_show_version(mrb_state *); void mrb_show_copyright(mrb_state *); 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; mrb_bool debug_info : 1; }; 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", "--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, 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 = 1; break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = 1; break; case 'g': args->debug_info = 1; 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 = 1; break; } else if (strcmp(argv[i] + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } return -1; default: return i; } } else { break; } } return i; } static void cleanup(mrb_state *mrb, struct mrbc_args *args) { if (args->outfile) 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; int need_close = FALSE; c = mrbc_context_new(mrb); if (args->verbose) c->dump_result = 1; c->no_exec = 1; 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->debug_info, 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->debug_info, 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) { 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-0.0.0~20131214+git882afdea/tools/mrbc/mrbc.rake000066400000000000000000000010501225163307400215340ustar00rootroot00000000000000MRuby.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}" if bins.find { |s| s.to_s == 'mrbc' } exec = exefile("#{build_dir}/bin/mrbc") objs = Dir.glob("#{current_dir}/*.c").map { |f| objfile(f.pathmap("#{current_build_dir}/%n")) }.flatten file exec => objs + [libfile("#{build_dir}/lib/libmruby_core")] do |t| linker.run t.name, t.prerequisites end end end mruby-0.0.0~20131214+git882afdea/travis_config.rb000066400000000000000000000004731225163307400210540ustar00rootroot00000000000000MRuby::Build.new('debug') do |conf| toolchain :gcc enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.defines += %w(MRB_GC_FIXED_ARENA) end MRuby::Build.new do |conf| toolchain :gcc # include all core GEMs conf.gembox 'full-core' conf.cc.defines += %w(MRB_GC_FIXED_ARENA) end