├── images ├── book_cover.jpg ├── ch_gc_cycle.jpg ├── ch_gc_cycle.png ├── ch_gc_gengc.jpg ├── ch_gc_gengc.png ├── ch_gc_heaps.jpg ├── ch_gc_heaps.png ├── ch_gc_objid.jpg ├── ch_gc_objid.png ├── ch_gc_stop2.jpg ├── ch_gc_stop2.png ├── ch_gc_stop3.jpg ├── ch_gc_stop3.png ├── ch_class_mmm.png ├── ch_class_real.png ├── ch_gc_calloca.jpg ├── ch_gc_calloca.png ├── ch_gc_gcimage.jpg ├── ch_gc_gcimage.png ├── ch_gc_objects.jpg ├── ch_gc_objects.png ├── ch_gc_refcnt.jpg ├── ch_gc_refcnt.png ├── ch_load_link.jpg ├── ch_name_array.png ├── ch_name_aset.png ├── ch_name_chain.png ├── ch_name_nexti.png ├── ch_yacc_build.jpg ├── somerights20.png ├── ch_abstract_ci.jpg ├── ch_abstract_ci.png ├── ch_abstract_repo.jpg ├── ch_abstract_repo.png ├── ch_class_boot1.png ├── ch_class_include.png ├── ch_class_infloop.png ├── ch_class_metaobj.png ├── ch_class_multi.png ├── ch_class_reqlink.png ├── ch_gc_heapitems.jpg ├── ch_gc_heapitems.png ├── ch_gc_macstack.jpg ├── ch_gc_macstack.png ├── ch_gc_mostcopy.jpg ├── ch_gc_mostcopy.png ├── ch_iterator_dst.jpg ├── ch_load_loadwait.jpg ├── ch_method_anchor.jpg ├── ch_method_mhash.jpg ├── ch_minimum_ccc.jpg ├── ch_minimum_ccc.png ├── ch_minimum_const.jpg ├── ch_minimum_const.png ├── ch_module_cbase.jpg ├── ch_module_stack.jpg ├── ch_module_vars.jpg ├── ch_name_sttable.png ├── ch_object_class.png ├── ch_object_flags.png ├── ch_object_rbasic.png ├── ch_object_rdata.png ├── ch_object_string.png ├── ch_object_value.png ├── ch_parser_ablist.jpg ├── ch_parser_alist.jpg ├── ch_parser_build.jpg ├── ch_parser_interf.jpg ├── ch_syntree_flags.jpg ├── ch_syntree_lmask.jpg ├── ch_syntree_stree.jpg ├── ch_syntree_tbl.jpg ├── ch_thread_fdset.jpg ├── ch_thread_thread.jpg ├── ch_variable_gvar.png ├── ch_yacc_yaccvars.jpg ├── visual_language.png ├── ch_abstract_build.jpg ├── ch_abstract_build.png ├── ch_class_addsclass.png ├── ch_class_metaclass.png ├── ch_class_metatree.png ├── ch_class_simulate.png ├── ch_class_symbolic.png ├── ch_iterator_insert.jpg ├── ch_iterator_stacks.jpg ├── ch_method_msearch.jpg ├── ch_minimum_Kernel.jpg ├── ch_minimum_Kernel.png ├── ch_module_massign.jpg ├── ch_object_givtable.png ├── ch_parser_exprloop.jpg ├── ch_parser_heredoc.jpg ├── ch_parser_ibuffer.jpg ├── ch_parser_progloop.jpg ├── ch_parser_scanner.jpg ├── ch_parser_tbuffer.jpg ├── ch_syntree_append.jpg ├── ch_abstract_syntree.jpg ├── ch_abstract_syntree.png ├── ch_anyeval_dynavars.jpg ├── ch_anyeval_speceval.jpg ├── ch_contextual_condp.jpg ├── ch_contextual_trees.jpg ├── ch_evaluator_jumptag.jpg ├── ch_evaluator_rbeval.jpg ├── ch_evaluator_setjmp.jpg ├── ch_evaluator_tagstack.jpg ├── ch_evaluator_usetag.jpg ├── ch_evaluator_whilejmp.jpg ├── ch_iterator_framepush.jpg ├── ch_iterator_itertrans.jpg ├── ch_minimum_classclass.jpg ├── ch_minimum_classclass.png ├── ch_minimum_classtree.jpg ├── ch_minimum_classtree.png ├── ch_minimum_constref.jpg ├── ch_minimum_constref.png ├── ch_minimum_modclass.jpg ├── ch_minimum_modclass.png ├── ch_minimum_modclass2.jpg ├── ch_minimum_modclass2.png ├── ch_minimum_modinherit.jpg ├── ch_minimum_modinherit.png ├── ch_minimum_objimage.jpg ├── ch_minimum_objimage.png ├── ch_minimum_reference.jpg ├── ch_minimum_reference.png ├── ch_minimum_supersub.jpg ├── ch_minimum_supersub.png ├── ch_module_crefstack.jpg ├── ch_module_framestack.jpg ├── ch_module_localvars.jpg ├── ch_module_scopestack.jpg ├── ch_object_classtree.png ├── ch_parser_lexparams.jpg ├── ch_syntree_blocklist.jpg ├── ch_syntree_callgraph.jpg ├── ch_syntree_dynavars.jpg ├── ch_syntree_flagUsage.jpg ├── ch_syntree_localtbl.jpg ├── ch_syntree_localvars.jpg ├── ch_syntree_lvtbltbl.jpg ├── ch_thread_setjmploop.jpg ├── ch_variable_gaccess.png ├── ch_iterator_flaginfect.jpg ├── ch_minimum_metaobjects.jpg ├── ch_minimum_metaobjects.png ├── ch_minimum_multiinherit.jpg ├── ch_minimum_multiinherit.png ├── ch_module_tmpprotecttmp.jpg ├── ch_thread_twodirection.jpg ├── ch_contextual_transittobeg.jpg └── ch_iterator_dynavarseval.jpg ├── .gitignore ├── Gemfile ├── _layouts ├── page.html ├── default.html └── post.html ├── _includes ├── head.html ├── header.html └── footer.html ├── zh ├── rename.rb ├── r-preface.md ├── 0-0-preface.md ├── 07-security.md ├── misc.md ├── markdown.md ├── r-bnf.md ├── r-options.md ├── liquid.md ├── 03-name.md ├── 06-variable.md └── 20-fin.md ├── about.md ├── _config.yml ├── feed.xml ├── README.md ├── index.md ├── en ├── index.textile ├── security.textile ├── preface.textile └── fin.textile └── css ├── style.css └── main.css /images/book_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/book_cover.jpg -------------------------------------------------------------------------------- /images/ch_gc_cycle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_cycle.jpg -------------------------------------------------------------------------------- /images/ch_gc_cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_cycle.png -------------------------------------------------------------------------------- /images/ch_gc_gengc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_gengc.jpg -------------------------------------------------------------------------------- /images/ch_gc_gengc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_gengc.png -------------------------------------------------------------------------------- /images/ch_gc_heaps.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_heaps.jpg -------------------------------------------------------------------------------- /images/ch_gc_heaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_heaps.png -------------------------------------------------------------------------------- /images/ch_gc_objid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_objid.jpg -------------------------------------------------------------------------------- /images/ch_gc_objid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_objid.png -------------------------------------------------------------------------------- /images/ch_gc_stop2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_stop2.jpg -------------------------------------------------------------------------------- /images/ch_gc_stop2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_stop2.png -------------------------------------------------------------------------------- /images/ch_gc_stop3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_stop3.jpg -------------------------------------------------------------------------------- /images/ch_gc_stop3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_stop3.png -------------------------------------------------------------------------------- /images/ch_class_mmm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_mmm.png -------------------------------------------------------------------------------- /images/ch_class_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_real.png -------------------------------------------------------------------------------- /images/ch_gc_calloca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_calloca.jpg -------------------------------------------------------------------------------- /images/ch_gc_calloca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_calloca.png -------------------------------------------------------------------------------- /images/ch_gc_gcimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_gcimage.jpg -------------------------------------------------------------------------------- /images/ch_gc_gcimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_gcimage.png -------------------------------------------------------------------------------- /images/ch_gc_objects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_objects.jpg -------------------------------------------------------------------------------- /images/ch_gc_objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_objects.png -------------------------------------------------------------------------------- /images/ch_gc_refcnt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_refcnt.jpg -------------------------------------------------------------------------------- /images/ch_gc_refcnt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_refcnt.png -------------------------------------------------------------------------------- /images/ch_load_link.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_load_link.jpg -------------------------------------------------------------------------------- /images/ch_name_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_name_array.png -------------------------------------------------------------------------------- /images/ch_name_aset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_name_aset.png -------------------------------------------------------------------------------- /images/ch_name_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_name_chain.png -------------------------------------------------------------------------------- /images/ch_name_nexti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_name_nexti.png -------------------------------------------------------------------------------- /images/ch_yacc_build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_yacc_build.jpg -------------------------------------------------------------------------------- /images/somerights20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/somerights20.png -------------------------------------------------------------------------------- /images/ch_abstract_ci.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_ci.jpg -------------------------------------------------------------------------------- /images/ch_abstract_ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_ci.png -------------------------------------------------------------------------------- /images/ch_abstract_repo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_repo.jpg -------------------------------------------------------------------------------- /images/ch_abstract_repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_repo.png -------------------------------------------------------------------------------- /images/ch_class_boot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_boot1.png -------------------------------------------------------------------------------- /images/ch_class_include.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_include.png -------------------------------------------------------------------------------- /images/ch_class_infloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_infloop.png -------------------------------------------------------------------------------- /images/ch_class_metaobj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_metaobj.png -------------------------------------------------------------------------------- /images/ch_class_multi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_multi.png -------------------------------------------------------------------------------- /images/ch_class_reqlink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_reqlink.png -------------------------------------------------------------------------------- /images/ch_gc_heapitems.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_heapitems.jpg -------------------------------------------------------------------------------- /images/ch_gc_heapitems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_heapitems.png -------------------------------------------------------------------------------- /images/ch_gc_macstack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_macstack.jpg -------------------------------------------------------------------------------- /images/ch_gc_macstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_macstack.png -------------------------------------------------------------------------------- /images/ch_gc_mostcopy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_mostcopy.jpg -------------------------------------------------------------------------------- /images/ch_gc_mostcopy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_gc_mostcopy.png -------------------------------------------------------------------------------- /images/ch_iterator_dst.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_dst.jpg -------------------------------------------------------------------------------- /images/ch_load_loadwait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_load_loadwait.jpg -------------------------------------------------------------------------------- /images/ch_method_anchor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_method_anchor.jpg -------------------------------------------------------------------------------- /images/ch_method_mhash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_method_mhash.jpg -------------------------------------------------------------------------------- /images/ch_minimum_ccc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_ccc.jpg -------------------------------------------------------------------------------- /images/ch_minimum_ccc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_ccc.png -------------------------------------------------------------------------------- /images/ch_minimum_const.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_const.jpg -------------------------------------------------------------------------------- /images/ch_minimum_const.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_const.png -------------------------------------------------------------------------------- /images/ch_module_cbase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_cbase.jpg -------------------------------------------------------------------------------- /images/ch_module_stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_stack.jpg -------------------------------------------------------------------------------- /images/ch_module_vars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_vars.jpg -------------------------------------------------------------------------------- /images/ch_name_sttable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_name_sttable.png -------------------------------------------------------------------------------- /images/ch_object_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_class.png -------------------------------------------------------------------------------- /images/ch_object_flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_flags.png -------------------------------------------------------------------------------- /images/ch_object_rbasic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_rbasic.png -------------------------------------------------------------------------------- /images/ch_object_rdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_rdata.png -------------------------------------------------------------------------------- /images/ch_object_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_string.png -------------------------------------------------------------------------------- /images/ch_object_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_value.png -------------------------------------------------------------------------------- /images/ch_parser_ablist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_ablist.jpg -------------------------------------------------------------------------------- /images/ch_parser_alist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_alist.jpg -------------------------------------------------------------------------------- /images/ch_parser_build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_build.jpg -------------------------------------------------------------------------------- /images/ch_parser_interf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_interf.jpg -------------------------------------------------------------------------------- /images/ch_syntree_flags.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_flags.jpg -------------------------------------------------------------------------------- /images/ch_syntree_lmask.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_lmask.jpg -------------------------------------------------------------------------------- /images/ch_syntree_stree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_stree.jpg -------------------------------------------------------------------------------- /images/ch_syntree_tbl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_tbl.jpg -------------------------------------------------------------------------------- /images/ch_thread_fdset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_thread_fdset.jpg -------------------------------------------------------------------------------- /images/ch_thread_thread.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_thread_thread.jpg -------------------------------------------------------------------------------- /images/ch_variable_gvar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_variable_gvar.png -------------------------------------------------------------------------------- /images/ch_yacc_yaccvars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_yacc_yaccvars.jpg -------------------------------------------------------------------------------- /images/visual_language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/visual_language.png -------------------------------------------------------------------------------- /images/ch_abstract_build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_build.jpg -------------------------------------------------------------------------------- /images/ch_abstract_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_build.png -------------------------------------------------------------------------------- /images/ch_class_addsclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_addsclass.png -------------------------------------------------------------------------------- /images/ch_class_metaclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_metaclass.png -------------------------------------------------------------------------------- /images/ch_class_metatree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_metatree.png -------------------------------------------------------------------------------- /images/ch_class_simulate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_simulate.png -------------------------------------------------------------------------------- /images/ch_class_symbolic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_class_symbolic.png -------------------------------------------------------------------------------- /images/ch_iterator_insert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_insert.jpg -------------------------------------------------------------------------------- /images/ch_iterator_stacks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_stacks.jpg -------------------------------------------------------------------------------- /images/ch_method_msearch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_method_msearch.jpg -------------------------------------------------------------------------------- /images/ch_minimum_Kernel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_Kernel.jpg -------------------------------------------------------------------------------- /images/ch_minimum_Kernel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_Kernel.png -------------------------------------------------------------------------------- /images/ch_module_massign.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_massign.jpg -------------------------------------------------------------------------------- /images/ch_object_givtable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_givtable.png -------------------------------------------------------------------------------- /images/ch_parser_exprloop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_exprloop.jpg -------------------------------------------------------------------------------- /images/ch_parser_heredoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_heredoc.jpg -------------------------------------------------------------------------------- /images/ch_parser_ibuffer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_ibuffer.jpg -------------------------------------------------------------------------------- /images/ch_parser_progloop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_progloop.jpg -------------------------------------------------------------------------------- /images/ch_parser_scanner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_scanner.jpg -------------------------------------------------------------------------------- /images/ch_parser_tbuffer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_tbuffer.jpg -------------------------------------------------------------------------------- /images/ch_syntree_append.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_append.jpg -------------------------------------------------------------------------------- /images/ch_abstract_syntree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_syntree.jpg -------------------------------------------------------------------------------- /images/ch_abstract_syntree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_abstract_syntree.png -------------------------------------------------------------------------------- /images/ch_anyeval_dynavars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_anyeval_dynavars.jpg -------------------------------------------------------------------------------- /images/ch_anyeval_speceval.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_anyeval_speceval.jpg -------------------------------------------------------------------------------- /images/ch_contextual_condp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_contextual_condp.jpg -------------------------------------------------------------------------------- /images/ch_contextual_trees.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_contextual_trees.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_jumptag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_jumptag.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_rbeval.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_rbeval.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_setjmp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_setjmp.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_tagstack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_tagstack.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_usetag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_usetag.jpg -------------------------------------------------------------------------------- /images/ch_evaluator_whilejmp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_evaluator_whilejmp.jpg -------------------------------------------------------------------------------- /images/ch_iterator_framepush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_framepush.jpg -------------------------------------------------------------------------------- /images/ch_iterator_itertrans.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_itertrans.jpg -------------------------------------------------------------------------------- /images/ch_minimum_classclass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_classclass.jpg -------------------------------------------------------------------------------- /images/ch_minimum_classclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_classclass.png -------------------------------------------------------------------------------- /images/ch_minimum_classtree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_classtree.jpg -------------------------------------------------------------------------------- /images/ch_minimum_classtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_classtree.png -------------------------------------------------------------------------------- /images/ch_minimum_constref.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_constref.jpg -------------------------------------------------------------------------------- /images/ch_minimum_constref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_constref.png -------------------------------------------------------------------------------- /images/ch_minimum_modclass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modclass.jpg -------------------------------------------------------------------------------- /images/ch_minimum_modclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modclass.png -------------------------------------------------------------------------------- /images/ch_minimum_modclass2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modclass2.jpg -------------------------------------------------------------------------------- /images/ch_minimum_modclass2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modclass2.png -------------------------------------------------------------------------------- /images/ch_minimum_modinherit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modinherit.jpg -------------------------------------------------------------------------------- /images/ch_minimum_modinherit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_modinherit.png -------------------------------------------------------------------------------- /images/ch_minimum_objimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_objimage.jpg -------------------------------------------------------------------------------- /images/ch_minimum_objimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_objimage.png -------------------------------------------------------------------------------- /images/ch_minimum_reference.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_reference.jpg -------------------------------------------------------------------------------- /images/ch_minimum_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_reference.png -------------------------------------------------------------------------------- /images/ch_minimum_supersub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_supersub.jpg -------------------------------------------------------------------------------- /images/ch_minimum_supersub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_supersub.png -------------------------------------------------------------------------------- /images/ch_module_crefstack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_crefstack.jpg -------------------------------------------------------------------------------- /images/ch_module_framestack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_framestack.jpg -------------------------------------------------------------------------------- /images/ch_module_localvars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_localvars.jpg -------------------------------------------------------------------------------- /images/ch_module_scopestack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_scopestack.jpg -------------------------------------------------------------------------------- /images/ch_object_classtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_object_classtree.png -------------------------------------------------------------------------------- /images/ch_parser_lexparams.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_parser_lexparams.jpg -------------------------------------------------------------------------------- /images/ch_syntree_blocklist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_blocklist.jpg -------------------------------------------------------------------------------- /images/ch_syntree_callgraph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_callgraph.jpg -------------------------------------------------------------------------------- /images/ch_syntree_dynavars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_dynavars.jpg -------------------------------------------------------------------------------- /images/ch_syntree_flagUsage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_flagUsage.jpg -------------------------------------------------------------------------------- /images/ch_syntree_localtbl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_localtbl.jpg -------------------------------------------------------------------------------- /images/ch_syntree_localvars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_localvars.jpg -------------------------------------------------------------------------------- /images/ch_syntree_lvtbltbl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_syntree_lvtbltbl.jpg -------------------------------------------------------------------------------- /images/ch_thread_setjmploop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_thread_setjmploop.jpg -------------------------------------------------------------------------------- /images/ch_variable_gaccess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_variable_gaccess.png -------------------------------------------------------------------------------- /images/ch_iterator_flaginfect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_flaginfect.jpg -------------------------------------------------------------------------------- /images/ch_minimum_metaobjects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_metaobjects.jpg -------------------------------------------------------------------------------- /images/ch_minimum_metaobjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_metaobjects.png -------------------------------------------------------------------------------- /images/ch_minimum_multiinherit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_multiinherit.jpg -------------------------------------------------------------------------------- /images/ch_minimum_multiinherit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_minimum_multiinherit.png -------------------------------------------------------------------------------- /images/ch_module_tmpprotecttmp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_module_tmpprotecttmp.jpg -------------------------------------------------------------------------------- /images/ch_thread_twodirection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_thread_twodirection.jpg -------------------------------------------------------------------------------- /images/ch_contextual_transittobeg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_contextual_transittobeg.jpg -------------------------------------------------------------------------------- /images/ch_iterator_dynavarseval.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiajian/rhg-zh/gh-pages/images/ch_iterator_dynavarseval.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .DS_Store 3 | .jekyll 4 | .bundle 5 | .sass-cache 6 | Gemfile.lock 7 | node_modules 8 | package.json 9 | *.swp 10 | *.swo 11 | .ruby-version 12 | .ruby-gemset 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | #备注:精确的版本控制在大型项目中很重要,但是,我觉得自己搞的Jekyll的站点还是别精确控制了,一来我懒,二来世界变化很快,版本更迭很快。 2 | source 'http://ruby.taobao.org' 3 | 4 | gem 'jekyll' 5 | gem 'RedCloth' # textile格式的处理器 6 | gem 'jekyll-sitemap' # 7 | gem 'jemoji' # GitHub风格的表情符号插件 8 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
这是一个普通段落:
61 |这是一个代码区块。
62 |
63 | 7.粗体斜体:
64 | **粗体**, *斜体*
65 |
66 | 8.表格:
67 |
68 | | Tables | Are | Cool |
69 | | ------------- |:-------------:| -----:|
70 | | col 3 is | right-aligned | $1600 |
71 | | col 2 is | centered | $12 |
72 | | zebra stripes | are neat | $1 |
73 |
74 | 9.水平分隔线:
75 | 几种形式如下:
76 | * * *
77 | ***
78 | *****
79 | ----
80 | - - -
81 | ---------------------------------------
82 | 效果大多相同:
83 | -----
84 |
85 | 10.在markdown文件中使用html标签:
86 |
87 | Markdown 语法的目标是:成为一种适用于网络的书写语言。
88 |
89 | Markdown 不是想要取代 HTML,甚至也没有要和它相近,它的语法种类很少,只对应 HTML 标记的一小部分。Markdown 的构想不是要使得 HTML 文档更容易书写。在我看来, HTML 已经很容易写了。Markdown 的理念是,能让文档更容易读、写和随意改。HTML 是一种发布的格式,Markdown 是一种书写的格式。
90 |
91 | HTML 区块元素――比如 | Foo | 100 |
”,只要在行首用右尖括号>就行了。如果要嵌套引用,那就多打几个>。 141 | 原文输出:如果不想Markdown解释某些内容,有两种方式: 142 | 143 | 第一个是转义为html的””标签:要在要原样输出的内容前面加入至少4个空格或者1个tab的宽度; 144 | 第二个是转义为””标签:给要输出的内容加上”`”号(就是esc下头那个键)。 145 | 146 | 无序列表:在文字前面使用星号”*”、加号”+”、减号”-”中的任意一个,注意在这些符号后面要留一个空格。 147 | 148 | 有序列表:在第一条前添加一个数字,后跟一个英文句点”.”。无论第一个条目前加的数字是什么,列表都会从1开始计数。 149 | 150 | 转义符:”\”。只要给不希望被转义的字符前面加上\就可以了。 151 | 152 | HTML实体:如果要在内容中输出”<”或者”&”符号,那么必须用[HTML实体][]代替。Markdown会把HTML的特殊符号直接翻译为HTML实体。 153 | 154 | 换行符:Markdown中在一行的末尾使用两个以上的空格标示HTML中的一个换行符”
”。 155 | 156 | 段落:只要两行之间有一个空行,Markdown就会把它识别为一个段落。 157 | (在列表之前一定要加入一个空行,要不然Markdown是不会把”*”识别为列表的。) 158 | 159 | 水平线:使用三个以上的”*”或”-”来表示。这些星号跟减号之间可以用空格,如果减号没有空格,那它必须在单独的一个段落里,要不它会被识别为标题的。 160 | 161 | 直接使用HTML标记:有些Markdown不支持的标签可以直接写HTML标记,例如表格。 162 | 163 | ## 语法速查 164 | ---- 165 | 166 | Markdown 语法速查表 167 | 168 | 1 标题与文字格式 169 | 标题 170 | 171 | # 这是 H1 <一级标题> 172 | ## 这是 H2 <二级标题> 173 | ###### 这是 H6 <六级标题> 174 | 175 | 文字格式 176 | 177 | **这是文字粗体格式** 178 | *这是文字斜体格式* 179 | ~~在文字上添加删除线~~ 180 | 181 | 2 列表 182 | 无序列表 183 | 184 | * 项目1 185 | * 项目2 186 | * 项目3 187 | 188 | 有序列表 189 | 190 | 1. 项目1 191 | 2. 项目2 192 | 3. 项目3 193 | * 项目1 194 | * 项目2 195 | 196 | 3 其它 197 | 图片 198 | 199 |  200 | 201 | 链接 202 | 203 | [链接名称](http://gitcafe.com) 204 | 205 | 引用 206 | 207 | > 第一行引用文字 208 | > 第二行引用文字 209 | 210 | 水平线 211 | 212 | *** 213 | 214 | 代码 215 | 216 | `` 217 | 218 | 代码块高亮 219 | 220 | ```ruby 221 | def add(a, b) 222 | return a + b 223 | end 224 | ``` 225 | 226 | 表格 227 | 228 | 表头 | 表头 229 | ------------- | ------------- 230 | 单元格内容 | 单元格内容 231 | 单元格内容l | 单元格内容 232 | 233 | ## 参考文献 234 | ---- 235 | 236 | 1. [MarkDown语法小结 ](http://www.myexception.cn/program/1629426.html) 237 | -------------------------------------------------------------------------------- /zh/r-bnf.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Ruby的伪BNF语法 4 | --- 5 | 6 | Here is the syntax of Ruby in pseudo BNF. For more detail, see parse.y in Ruby distribution. 7 | 8 | 下面是Ruby的BNF语法描述,更多细节,参考Ruby分发包中的parse.y文件。 9 | 10 | > BNF是语言的形式化描述,是最能体现设计和品味的地方,同样也是yacc的输入文件。之前,个人曾经想要自动生成一个c--的编译器,结果一直卡在语法解释器上,也不知道如何处理,最后,由于当时还有其他的事情想做,就放弃了。 11 | 12 | {% highlight ruby %} 13 | PROGRAM : COMPSTMT 14 | 15 | COMPSTMT : STMT (TERM EXPR)* [TERM] 16 | 17 | STMT : CALL do [`|' [BLOCK_VAR] `|'] COMPSTMT end 18 | | undef FNAME 19 | | alias FNAME FNAME 20 | | STMT if EXPR 21 | | STMT while EXPR 22 | | STMT unless EXPR 23 | | STMT until EXPR 24 | | `BEGIN' `{' COMPSTMT `}' 25 | | `END' `{' COMPSTMT `}' 26 | | LHS `=' COMMAND [do [`|' [BLOCK_VAR] `|'] COMPSTMT end] 27 | | EXPR 28 | 29 | EXPR : MLHS `=' MRHS 30 | | return CALL_ARGS 31 | | yield CALL_ARGS 32 | | EXPR and EXPR 33 | | EXPR or EXPR 34 | | not EXPR 35 | | COMMAND 36 | | `!' COMMAND 37 | | ARG 38 | 39 | CALL : FUNCTION 40 | | COMMAND 41 | 42 | COMMAND : OPERATION CALL_ARGS 43 | | PRIMARY `.' OPERATION CALL_ARGS 44 | | PRIMARY `::' OPERATION CALL_ARGS 45 | | super CALL_ARGS 46 | 47 | FUNCTION : OPERATION [`(' [CALL_ARGS] `)'] 48 | | PRIMARY `.' OPERATION `(' [CALL_ARGS] `)' 49 | | PRIMARY `::' OPERATION `(' [CALL_ARGS] `)' 50 | | PRIMARY `.' OPERATION 51 | | PRIMARY `::' OPERATION 52 | | super `(' [CALL_ARGS] `)' 53 | | super 54 | 55 | ARG : LHS `=' ARG 56 | | LHS OP_ASGN ARG 57 | | ARG `..' ARG 58 | | ARG `...' ARG 59 | | ARG `+' ARG 60 | | ARG `-' ARG 61 | | ARG `*' ARG 62 | | ARG `/' ARG 63 | | ARG `%' ARG 64 | | ARG `**' ARG 65 | | `+' ARG 66 | | `-' ARG 67 | | ARG `|' ARG 68 | | ARG `^' ARG 69 | | ARG `&' ARG 70 | | ARG `<=>' ARG 71 | | ARG `>' ARG 72 | | ARG `>=' ARG 73 | | ARG `<' ARG 74 | | ARG `<=' ARG 75 | | ARG `==' ARG 76 | | ARG `===' ARG 77 | | ARG `!=' ARG 78 | | ARG `=~' ARG 79 | | ARG `!~' ARG 80 | | `!' ARG 81 | | `~' ARG 82 | | ARG `<<' ARG 83 | | ARG `>>' ARG 84 | | ARG `&&' ARG 85 | | ARG `||' ARG 86 | | defined? ARG 87 | | PRIMARY 88 | 89 | PRIMARY : `(' COMPSTMT `)' 90 | | LITERAL 91 | | VARIABLE 92 | | PRIMARY `::' IDENTIFIER 93 | | `::' IDENTIFIER 94 | | PRIMARY `[' [ARGS] `]' 95 | | `[' [ARGS [`,']] `]' 96 | | `{' [(ARGS|ASSOCS) [`,']] `}' 97 | | return [`(' [CALL_ARGS] `)'] 98 | | yield [`(' [CALL_ARGS] `)'] 99 | | defined? `(' ARG `)' 100 | | FUNCTION 101 | | FUNCTION `{' [`|' [BLOCK_VAR] `|'] COMPSTMT `}' 102 | | if EXPR THEN 103 | COMPSTMT 104 | (elsif EXPR THEN COMPSTMT)* 105 | [else COMPSTMT] 106 | end 107 | | unless EXPR THEN 108 | COMPSTMT 109 | [else COMPSTMT] 110 | end 111 | | while EXPR DO COMPSTMT end 112 | | until EXPR DO COMPSTMT end 113 | | case COMPSTMT 114 | (when WHEN_ARGS THEN COMPSTMT)+ 115 | [else COMPSTMT] 116 | end 117 | | for BLOCK_VAR in EXPR DO 118 | COMPSTMT 119 | end 120 | | begin 121 | COMPSTMT 122 | [rescue [ARGS] DO COMPSTMT]+ 123 | [else COMPSTMT] 124 | [ensure COMPSTMT] 125 | end 126 | | class IDENTIFIER [`<' IDENTIFIER] 127 | COMPSTMT 128 | end 129 | | module IDENTIFIER 130 | COMPSTMT 131 | end 132 | | def FNAME ARGDECL 133 | COMPSTMT 134 | end 135 | | def SINGLETON (`.'|`::') FNAME ARGDECL 136 | COMPSTMT 137 | end 138 | 139 | WHEN_ARGS : ARGS [`,' `*' ARG] 140 | | `*' ARG 141 | 142 | THEN : TERM 143 | | then 144 | | TERM then 145 | 146 | DO : TERM 147 | | do 148 | | TERM do 149 | 150 | BLOCK_VAR : LHS 151 | | MLHS 152 | 153 | MLHS : MLHS_ITEM `,' [MLHS_ITEM (`,' MLHS_ITEM)*] [`*' [LHS]] 154 | | `*' LHS 155 | 156 | MLHS_ITEM : LHS 157 | | '(' MLHS ')' 158 | 159 | LHS : VARIABLE 160 | | PRIMARY `[' [ARGS] `]' 161 | | PRIMARY `.' IDENTIFIER 162 | 163 | MRHS : ARGS [`,' `*' ARG] 164 | | `*' ARG 165 | 166 | CALL_ARGS : ARGS 167 | | ARGS [`,' ASSOCS] [`,' `*' ARG] [`,' `&' ARG] 168 | | ASSOCS [`,' `*' ARG] [`,' `&' ARG] 169 | | `*' ARG [`,' `&' ARG] 170 | | `&' ARG 171 | | COMMAND 172 | 173 | ARGS : ARG (`,' ARG)* 174 | 175 | ARGDECL : `(' ARGLIST `)' 176 | | ARGLIST TERM 177 | 178 | ARGLIST : IDENTIFIER(`,'IDENTIFIER)*[`,'`*'[IDENTIFIER]][`,'`&'IDENTIFIER] 179 | | `*'IDENTIFIER[`,'`&'IDENTIFIER] 180 | | [`&'IDENTIFIER] 181 | 182 | SINGLETON : VARIABLE 183 | | `(' EXPR `)' 184 | 185 | ASSOCS : ASSOC (`,' ASSOC)* 186 | 187 | ASSOC : ARG `=>' ARG 188 | 189 | VARIABLE : VARNAME 190 | | nil 191 | | self 192 | 193 | LITERAL : numeric 194 | | SYMBOL 195 | | STRING 196 | | STRING2 197 | | HERE_DOC 198 | | REGEXP 199 | 200 | TERM : `;' 201 | | `\n' 202 | 203 | The followings are recognized by lexical analizer. 204 | 205 | 206 | OP_ASGN : `+=' | `-=' | `*=' | `/=' | `%=' | `**=' 207 | | `&=' | `|=' | `^=' | `<<=' | `>>=' 208 | | `&&=' | `||=' 209 | 210 | SYMBOL : `:'FNAME 211 | | `:'VARNAME 212 | 213 | FNAME : IDENTIFIER | `..' | `|' | `^' | `&' 214 | | `<=>' | `==' | `===' | `=~' 215 | | `>' | `>=' | `<' | `<=' 216 | | `+' | `-' | `*' | `/' | `%' | `**' 217 | | `<<' | `>>' | `~' 218 | | `+@' | `-@' | `[]' | `[]=' 219 | 220 | OPERATION : IDENTIFIER 221 | | IDENTIFIER'!' 222 | | IDENTIFIER'?' 223 | 224 | VARNAME : GLOBAL 225 | | `@'IDENTIFIER 226 | | IDENTIFIER 227 | 228 | GLOBAL : `$'IDENTIFIER 229 | | `$'any_char 230 | | `$''-'any_char 231 | 232 | STRING : `"' any_char* `"' 233 | | `'' any_char* `'' 234 | | ``' any_char* ``' 235 | 236 | STRING2 : `%'(`Q'|`q'|`x')char any_char* char 237 | 238 | HERE_DOC : `<<'(IDENTIFIER|STRING) 239 | any_char* 240 | IDENTIFIER 241 | 242 | REGEXP : `/' any_char* `/'[`i'|`o'|`p'] 243 | | `%'`r' char any_char* char 244 | {% endhighlight %} 245 | 246 | IDENTIFIER is the sqeunce of characters in the pattern of /[a-zA-Z_][a-zA-Z0-9_]*/. 247 | 248 | IDENTIFIER是/[a-zA-Z_][a-zA-Z0-9_]*/模式的字符序列。 249 | -------------------------------------------------------------------------------- /zh/r-options.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 命令行选项 4 | --- 5 | 6 | Ruby interpreter accepts following command-line options (switches). Basically they are quite similar to those of Perl. 7 | 8 | Ruby解释器接受如下的命令行选项(开关),内容基本和Perl相似。 9 | 10 | **-0digit** 11 | 12 | specifies the input record separator ($/) as an octal number. If no digits given, the null character is the separator. Other switches may follow the digits. -00 turns Ruby into paragraph mode. -0777 makes Ruby read whole file at once as a single string, since there is no legal character with that value. 13 | 以八进制数指定输入记录符($/)。如果没有给定digits,默认使用null字符作为分隔符。其他的开关也会跟随该数字。 -00表明Ruby进入段落模式,-0777使得Ruby将整个文件看作单个字符串,这是因为没有值为777的非法字符 14 | 15 | **-a** 16 | 17 | turns on auto-split mode when used with -n or -p. In auto-split mode, Ruby executes `$F = $_.split` at beginning of each loop. 18 | 当指定-n或者-p选项时,打开自动分割模式。在自动分隔模式中,ruby在每个循环开始处执行$F = $_.split 19 | 20 | **-c** 21 | 22 | causes Ruby to check the syntax of the script and exit without executing. If there is no syntax error, Ruby will print "Syntax OK" to the standard output. 23 | 仅检查脚本的语法而不执行脚本。如果没有语法错误,Ruby将向标准输出打印"Syntax OK"。 24 | 25 | **-Kc** 26 | 27 | specifies KANJI (Japanese character) code-set. 28 | 指定KANJI(日本字符集)编码集。 29 | 30 | **-d --debug** 31 | 32 | turns on debug mode. $DEBUG will set true. 33 | 打开debug模式,设置$DEBUG为真。 34 | 35 | **-e script** 36 | 37 | specifies script from command-line. if -e switch specified, Ruby will not look for a script filename in the arguments. 38 | 通过命令行指定脚本,如果设置-e选项,Ruby将不会在参数中寻找脚本的文件名。 39 | 40 | **-F regexp** 41 | 42 | specifies input field separator ($;). 43 | 指定输入文件的分隔符($;). 44 | 45 | **-h --help** 46 | 47 | prints a summary of the options. 48 | 打印选项的综述。 49 | 50 | **-i extension** 51 | 52 | specifies in-place-edit mode. The extension, if specified, is added to old filename to make a backup copy. 53 | 指定参考编辑模式,如果指定该参数,将会对旧的文件制作相应后缀的备份文件。 54 | 55 | example: 56 | 57 | % echo matz > /tmp/junk 58 | % cat /tmp/junk 59 | matz 60 | % ruby -p -i.bak -e '$_.upcase!' /tmp/junk 61 | % cat /tmp/junk 62 | MATZ 63 | % cat /tmp/junk.bak 64 | matz 65 | 66 | **-I directory** 67 | 68 | used to tell Ruby where to load the library scripts. Directory path will be added to the load-path variable (`$:'). 69 | 该选项告诉Ruby从哪里加载库脚本。目录参数将会被添加到加载路径变量(`$:`). 70 | 71 | **-l** 72 | 73 | enables automatic line-ending processing, which means firstly set $\ to the value of $/, and secondly chops every line read using chop!, when used with -n or -p. 74 | 启动自动的行结束符处理,这意味着:在设置-n或者-p选项时,1.将$\设置为$/的值 2.使用chop!切割每行。 75 | 76 | **-n** 77 | 78 | causes Ruby to assume the following loop around your script, which makes it iterate over filename arguments somewhat like sed -n or awk. 79 | 设置Ruby假设脚本被如下的循环包围,使得其像sed -n或awk那样迭代文件名参数。 80 | 81 | while gets 82 | ... 83 | end 84 | 85 | **-p** 86 | 87 | acts mostly same as -n switch, but print the value of variable $_ at the each end of the loop. 88 | 同-n选项类似,但是,在循环的结束处打印变量$_的值。 89 | 90 | example: 91 | 92 | % echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"' 93 | MATZ 94 | 95 | > 以$开头的奇怪的变量名到底是个啥,看起来还相当的常见的。 96 | 97 | **-r filename** 98 | 99 | causes Ruby to load the file using require. It is useful with switches -n or -p. 100 | 触发Ruby通过require加载文件。这在-n和-p选项打开时非常的有用。 101 | 102 | **-s** 103 | 104 | enables some switch parsing for switches after script name but before any filename arguments (or before a --). Any switches found there is removed from ARGV and set the corresponding variable in the script. 105 | 为开关启动一些转换解析。在ARGV中找到的开关将被移除,然后在脚本中设置对应的变量。 106 | 107 | example: 108 | 109 | #! /usr/local/bin/ruby -s 110 | # prints "true" if invoked with `-xyz' switch. 111 | print "true\n" if $xyz 112 | 113 | **-S** 114 | 115 | makes Ruby uses the PATH environment variable to search for script, unless if its name begins with a slash. This is used to emulate #! on machines that don't support it, in the following manner: 116 | 使得Ruby使用PATH环境变量搜索文件,除非名字以斜杠开头。这用来在不支持#!的机器上模仿#!,以如下的方式: 117 | 118 | #!/bin/sh 119 | exec ruby -S -x $0 "$@" 120 | #! ruby 121 | 122 | On some systems $0 does not always contain the full pathname, so you need -S switch to tell Ruby to search for the script if necessary. 123 | 在一些系统中,$0并不总是包含全路径名,所以需要-S选项告诉Ruby搜索需要的脚本。 124 | 125 | **-T [level]** 126 | 127 | Forces "taint" checks to be turned on so you can test them. If level is specified, $SAFE to be set to that level. It's a good idea to turn them on explicitly for programs run on another's behalf, such as CGI programs. 128 | 强制打开污染检查,使得其可以测试。如果level被指定,$SAFE将被设置为对应的等级。在其他环境中运行的程序(比如CGI程序)中,显式打开污染检查是必要的。 129 | 130 | **-v --verbose** 131 | 132 | enables verbose mode. Ruby will prints its version at the beginning, and set the variable `$VERBOSE' to true. Some methods prints extra messages if this variable is true. If this switch is given, and no other switches present, Ruby quits after printing its version. 133 | 启动verbose模式。Ruby将在开头打印版本号,并设置$VERBOSE为true。当$VERBOSE设置为true时,一些方法会打印额外的信息。 134 | 135 | **--version** 136 | 137 | prints the version of Ruby executable. 138 | 打印Ruby执行文件的版本。` 139 | 140 | **-w** 141 | 142 | enables verbose mode without printing version message at the beginning. It set the variable `$VERBOSE' to true. 143 | 144 | **-x[directory]** 145 | 146 | tells Ruby that the script is embedded in a message. Leading garbage will be discarded until the first that starts with "#!" and contains string "ruby". Any meaningful switches on that line will applied. The end of script must be specified with either EOF, ^D (control-D), ^Z (control-Z), or reserved word __END__.If the directory name is specified, Ruby will switch to that directory before executing script. 147 | 告诉Ruby脚本嵌入到消息中。 在以#!开头并包含ruby的行开始之前的行都被遗弃。任何在#!行中有意义的选项都会被应用。脚本的结束必须以EOF、^D (control-D)、^Z (control-Z)或者保留字__END__。如果目录名被指定,Ruby将在执行脚本前切换到其他目录中。 148 | 149 | **-X directory** 150 | 151 | causes Ruby to switch to the directory. 152 | 确保Ruby切换目录。 153 | 154 | **-y --yydebug** 155 | 156 | turns on compiler debug mode. Ruby will print bunch of internal state messages during compiling scripts. You don't have to specify this switch, unless you are going to debug the Ruby interpreter itself. 157 | 打开编译器的debug模式。Ruby在编译脚本时将打印内部状态信息。除非调用解释器自生,否则不需要打开该选项。 158 | -------------------------------------------------------------------------------- /en/security.textile: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Security 4 | --- 5 | Translated by Clifford Escobar CAOILE & ocha- 6 | 7 | h1. Chapter 7: Security 8 | 9 | h3. Fundamentals 10 | 11 | I say security but I don't mean passwords or encryption. The Ruby security 12 | feature is used for handling untrusted objects in a environment like CGI 13 | programming. 14 | 15 | For example, when you want to convert a string representing a number into a 16 | integer, you can use the `eval` method. However. `eval` is a method that "runs 17 | a string as a Ruby program." If you `eval` a string from a unknown person from 18 | the network, it is very dangerous. However for the programmer to fully 19 | differentiate between safe and unsafe things is very tiresome and cumbersome. 20 | Therefore, it is for certain that a mistake will be made. So, let us make it 21 | part of the language, was reasoning for this feature. 22 | 23 | So then, how Ruby protect us from that sort of danger? Causes of dangerous 24 | operations, for example, opening unintended files, are roughly divided into two 25 | groups: 26 | 27 | * Dangerous data 28 | * Dangerous code 29 | 30 | For the former, the code that handles these values is created by the 31 | programmers themselves, so therefore it is (relatively) safe. For the latter, 32 | the program code absolutely cannot be trusted. 33 | 34 | Because the solution is vastly different between the two causes, it is important to 35 | differentiate them by level. This are called security levels. The Ruby security 36 | level is represented by the `$SAFE` global variable. The value ranges from 37 | minimum value 0 to maximum value 4. When the variable is assigned, the level 38 | increases. Once the level is raised it can never be lowered. And for each 39 | level, the operations are limited. 40 | 41 | I will not explain level 1 or 3. 42 | Level 0 is the normal program environment and the security system is not 43 | running. Level 2 handles dangerous values. Level 4 handles dangerous code. 44 | We can skip 0 and move on to explain in detail levels 2 and 4. 45 | 46 | ((errata: Level 1 handles dangerous values. 47 | "Level 2 has no use currently" is right.)) 48 | 49 | 50 | h4. Level 1 51 | 52 | This level is for dangerous data, for example, in normal CGI 53 | applications, etc. 54 | 55 | A per-object "tainted mark" serves as the basis for the Level 1 56 | implementation. All objects read in externally are marked tainted, and 57 | any attempt to `eval` or `File.open` with a tainted object will cause an 58 | exception to be raised and the attempt will be stopped. 59 | 60 | This tainted mark is "infectious". For example, when taking a part of a 61 | tainted string, that part is also tainted. 62 | 63 | h4. Level 4 64 | 65 | This level is for dangerous programs, for example, running external 66 | (unknown) programs, etc. 67 | 68 | At level 1, operations and the data it uses are checked, but at level 69 | 4, operations themselves are restricted. For example, `exit`, file 70 | I/O, thread manipulation, redefining methods, etc. Of course, the 71 | tainted mark information is used, but basically the operations are the 72 | criteria. 73 | 74 | h4. Unit of Security 75 | 76 | `$SAFE` looks like a global variable but is in actuality a thread 77 | local variable. In other words, Ruby's security system works on units 78 | of thread. In Java and .NET, rights can be set per component (object), 79 | but Ruby does not implement that. The assumed main target was probably 80 | CGI. 81 | 82 | Therefore, if one wants to raise the security level of one part of the 83 | program, then it should be made into a different thread and have its 84 | security level raised. I haven't yet explained how to create a thread, 85 | but I will show an example here: 86 | 87 | 88 | # Raise the security level in a different thread 89 | p($SAFE) # 0 is the default 90 | Thread.fork { # Start a different thread 91 | $SAFE = 4 # Raise the level 92 | eval(str) # Run the dangerous program 93 | } 94 | p($SAFE) # Outside of the block, the level is still 0 95 |96 | 97 | h4. Reliability of `$SAFE` 98 | 99 | Even with implementing the spreading of tainted marks, or restricting 100 | operations, ultimately it is still handled manually. In other words, 101 | internal libraries and external libraries must be completely 102 | compatible and if they don't, then the partway the "tainted" operations 103 | will not spread and the security will be lost. And actually this kind 104 | of hole is often reported. For this reason, this writer does not 105 | wholly trust it. 106 | 107 | That is not to say, of course, that all Ruby programs are dangerous. 108 | Even at `$SAFE=0` it is possible to write a secure program, and even 109 | at `$SAFE=4` it is possible to write a program that fits your whim. 110 | However, one cannot put too much confidence on `$SAFE` (yet). 111 | 112 | In the first place, functionality and security do not go together. It 113 | is common sense that adding new features can make holes easier to 114 | open. Therefore it is prudent to think that `ruby` can probably be 115 | dangerous. 116 | 117 | 118 | h3. Implementation 119 | 120 | 121 | From now on, we'll start to look into its implementation. 122 | In order to wholly grasp the security system of `ruby`, 123 | we have to look at "where is being checked" rather than its mechanism. 124 | However, this time we don't have enough pages to do it, 125 | and just listing them up is not interesting. 126 | Therefore, in this chapter, I'll only describe about the 127 | mechanism used for security checks. 128 | The APIs to check are mainly these below two: 129 | 130 | 131 | * `rb_secure(n)` : If more than or equal to level n, it would raise `SecurityError`. 132 | * `SafeStringValue()` : 133 | If more than or equal to level 1 and a string is tainted, 134 | then it would raise an exception. 135 | 136 | 137 | We won't read `SafeStringValue()` here. 138 | 139 | 140 | h4. Tainted Mark 141 | 142 | 143 | The taint mark is, to be concrete, the `FL_TAINT` flag, which is set to 144 | `basic->flags`, and what is used to infect it is the `OBJ_INFECT()` macro. 145 | Here is its usage. 146 | 147 | 148 |149 | OBJ_TAINT(obj) /* set FL_TAINT to obj */ 150 | OBJ_TAINTED(obj) /* check if FL_TAINT is set to obj */ 151 | OBJ_INFECT(dest, src) /* infect FL_TAINT from src to dest */ 152 |153 | 154 | 155 | Since `OBJ_TAINT()` and `OBJ_TAINTED()` can be assumed not important, 156 | let's briefly look over only `OBJ_INFECT()`. 157 | 158 | 159 |▼ `OBJ_INFECT`
160 |161 | 441 #define OBJ_INFECT(x,s) do { \ 162 | if (FL_ABLE(x) && FL_ABLE(s)) \ 163 | RBASIC(x)->flags |= RBASIC(s)->flags & FL_TAINT; \ 164 | } while (0) 165 | 166 | (ruby.h) 167 |168 | 169 | 170 | `FL_ABLE()` checks if the argument `VALUE` is a pointer or not. 171 | If the both objects are pointers (it means each of them has its `flags` member), 172 | it would propagate the flag. 173 | 174 | 175 | 176 | 177 | h4. $SAFE 178 | 179 | 180 |▼ `ruby_safe_level`
181 |182 | 124 int ruby_safe_level = 0; 183 | 184 | 7401 static void 185 | 7402 safe_setter(val) 186 | 7403 VALUE val; 187 | 7404 { 188 | 7405 int level = NUM2INT(val); 189 | 7406 190 | 7407 if (level < ruby_safe_level) { 191 | 7408 rb_raise(rb_eSecurityError, "tried to downgrade safe level from %d to %d", 192 | 7409 ruby_safe_level, level); 193 | 7410 } 194 | 7411 ruby_safe_level = level; 195 | 7412 curr_thread->safe = level; 196 | 7413 } 197 | 198 | (eval.c) 199 |200 | 201 | 202 | The substance of `$SAFE` is `ruby_safe_level` in `eval.c`. 203 | As I previously wrote, `$SAFE` is local to each thread, 204 | It needs to be written in `eval.c` where the implementation of threads is located. 205 | In other words, it is in `eval.c` only because of the restrictions of C, 206 | but it can essentially be located in another place. 207 | 208 | 209 | `safe_setter()` is the `setter` of the `$SAFE` global variable. 210 | It means, because this function is the only way to access it from Ruby level, 211 | the security level cannot be lowered. 212 | 213 | 214 | However, as you can see, from C level, 215 | because `static` is not attached to `ruby_safe_level`, 216 | you can ignore the interface and modify the security level. 217 | 218 | 219 | 220 | 221 | 222 | h4. `rb_secure()` 223 | 224 | 225 |▼ `rb_secure()`
226 |227 | 136 void 228 | 137 rb_secure(level) 229 | 138 int level; 230 | 139 { 231 | 140 if (level <= ruby_safe_level) { 232 | 141 rb_raise(rb_eSecurityError, "Insecure operation `%s' at level %d", 233 | 142 rb_id2name(ruby_frame->last_func), ruby_safe_level); 234 | 143 } 235 | 144 } 236 | 237 | (eval.c) 238 |239 | 240 | 241 | If the current safe level is more than or equal to `level`, 242 | this would raise `SecurityError`. It's simple. 243 | -------------------------------------------------------------------------------- /zh/liquid.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: liquid模板语言 4 | --- 5 | 来自: [Liquid for Designers][liquid] 6 | 7 | ##缘起 8 | ---- 9 | Jekyll使用Liquid模板,因此,想要用好Jekyll,需要了解一些关于Liquid相关的知识。本文翻译自[Liquid for Designers][liquid],用作自己学习参考。 10 | 11 | 12 | Liquid中存在两种类型的标签(markup): Output and Tag 13 | - Output markup (which may resolve to text) is surrounded by 14 | 15 | > {% raw %} {{ 匹配成对出现的大括号 }} {% endraw %} 16 | 17 | - Tag markup (which cannot resolve to text) is surrounded by 18 | 19 | > {% raw %} {% 匹配成对出现的大括号以及百分号 %} {% endraw %} 20 | 21 | ## Output 22 | ---- 23 | 24 | Here is a simple example of Output: 25 | 下面是一些Output标签的简单例子: 26 | Hello {{name}} 27 | Hello {{user.name}} 28 | Hello {{ 'tobi' }} 29 | Hello ,this is {{site.title}} 30 | 31 | ### 高级Output标签:过滤器(Filters) 32 | 33 | Output markup takes filters. Filters are simple methods. The first parameter is always the output of the left side of the filter. The return value of the filter will be the new left value when the next filter is run. When there are no more filters, the template will receive the resulting string. 34 | 35 | Output标签中可以使用过滤器。所谓的过滤器,其实就是一些简单的方法。过滤器总是将左边输出作为第一个参数,其返回值将作为下一个过滤器的输入。一旦处理结束,即没有更多的过滤器时,模板处理器将接受最后的处理结果(字符串)。 36 | 37 | 示例如下: 38 | {% raw %} 39 | Hello {{ 'tobi' | upcase }} 40 | Hello tobi has {{ 'tobi' | size }} letters! 41 | Hello {{ '*tobi*' | textilize | upcase }} 42 | Hello {{ 'now' | date: "%Y %h" }} 43 | {% endraw %} 44 | 45 | 处理结果为: 46 | 47 | Hello {{ 'tobi' | upcase }} 48 | Hello tobi has {{ 'tobi' | size }} letters! 49 | Hello {{ '*tobi*' | textilize | upcase }} 50 | Hello {{ 'now' | date: "%Y %h" }} 51 | 52 | ### 标准过滤器(Standard Filters) 53 | 54 | date - 格式化日期 (syntax reference) 55 | capitalize - 将输入句子中的单词大写 56 | downcase - 将输入字符串转为小写 57 | upcase - 将输入字符串转为大写 58 | first - 获取传递的数组中的第一个元素get the first element of the passed in array 59 | last - 获取传递的数组中的最后一个元素 60 | join - join elements of the array with certain character between them 61 | sort - 数组元素排序 62 | map - map/collect an array on a given property 63 | size - 返回字符串或者数组的大小 64 | escape - 转义字符串 65 | escape_once - 返回转义版的html,并且不影响已存在的转义实体(escaped entities) 66 | strip_html - 剔除字符串中的html(strip html from string) 67 | strip_newlines - 剔除字符串中所有的换行符(\n) 68 | newline_to_br - 将字符串中换行符替换成html断行(
) 69 | replace - replace each occurrence e.g. {{ 'foofoo' | replace:'foo','bar' }} #=> 'barbar' 70 | replace_first - replace the first occurrence e.g. {{ 'barbar' | replace_first:'bar','foo' }} #=> 'foobar' 71 | remove - remove each occurrence e.g. {{ 'foobarfoobar' | remove:'foo' }} #=> 'barbar' 72 | remove_first - remove the first occurrence e.g. {{ 'barbar' | remove_first:'bar' }} #=> 'bar' 73 | truncate - 将字符串截断为x个字符 74 | truncatewords - 将字符串截断为x个字(主要用来处理多字符的字,比如CKJ的字符集) 75 | prepend - 向前追加一个字符串 例如: {{ 'bar' | prepend:'foo' }} #=> 'foobar' 76 | append - 向后追加一个字符串 e.g. {{ 'foo' | append:'bar' }} #=> 'foobar' 77 | minus - 减subtraction e.g. {{ 4 | minus:2 }} #=> 2 78 | plus - 加addition e.g. {{ '1' | plus:'1' }} #=> '11', {{ 1 | plus:1 }} #=> 2 79 | times - 乘multiplication e.g {{ 5 | times:4 }} #=> 20 80 | divided_by - 除division e.g. {{ 10 | divided_by:2 }} #=> 5 81 | split - 以给定的模式分隔字符串 e.g. {{ "a~b" | split:"~" }} #=> ['a','b'] 82 | modulo - 求余remainder, e.g. {{ 3 | modulo:2 }} #=> 1 83 | 84 | ## Tags 85 | ---- 86 | 87 | Tags are used for the logic in your template. New tags are very easy to code, so I hope to get many contributions to the standard tag library after releasing this code. 88 | 89 | Tags标签用作模板的逻辑。Tag实现很简单,所以很容易扩展出新的Tag。所以,原作者希望在发布Liquid之后,能有更多贡献者的编写tag并添加到tab库中。 90 | 91 | Here is a list of currently supported tags: 92 | 如下是当前支持的一些标签: 93 | 94 | assign - 赋值给某个变量Assigns some value to a variable 95 | capture - 可江文本捕获到变量中的块标签 Block tag that captures text into a variable 96 | case - Block tag, its the standard case...when block 97 | comment - Block tag, comments out the text in the block 98 | cycle - Cycle is usually used within a loop to alternate between values, like colors or DOM classes. 99 | for - For loop 100 | if - Standard if/else block 101 | include - Includes another template; useful for partials 102 | raw - temporarily disable tag processing to avoid syntax conflicts. 103 | unless - Mirror of if statement 104 | 105 | ### Comments 106 | 107 | Comment is the simplest tag. It just swallows content. 108 | {% raw %} 109 | We made 1 million dollars {% comment %} in losses {% endcomment %} this year 110 | {% endraw %} 111 | 112 | We made 1 million dollars {% comment %} in losses {% endcomment %} this year 113 | 114 | ### Raw 115 | 116 | Raw temporarily disables tag processing. This is useful for generating content (eg, Mustache, Handlebars) which uses conflicting syntax. 117 | 118 | >{% raw %} 119 | > In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not. 120 | >{% endraw %} 121 | 122 | ### If / Else 123 | 124 | if / else should be well-known from any other programming language. Liquid allows you to write simple expressions in the if or unless (and optionally, elsif and else) clause: 125 | 126 | {% raw %} 127 | {% if user %} 128 | Hello {{ user.name }} 129 | {% endif %} 130 | 131 | # Same as above 132 | {% if user != null %} 133 | Hello {{ user.name }} 134 | {% endif %} 135 | 136 | {% if user.name == 'tobi' %} 137 | Hello tobi 138 | {% elsif user.name == 'bob' %} 139 | Hello bob 140 | {% endif %} 141 | 142 | {% if user.name == 'tobi' or user.name == 'bob' %} 143 | Hello tobi or bob 144 | {% endif %} 145 | 146 | {% if user.name == 'bob' and user.age > 45 %} 147 | Hello old bob 148 | {% endif %} 149 | 150 | {% if user.name != 'tobi' %} 151 | Hello non-tobi 152 | {% endif %} 153 | 154 | # Same as above 155 | {% unless user.name == 'tobi' %} 156 | Hello non-tobi 157 | {% endunless %} 158 | 159 | # Check for the size of an array 160 | {% if user.payments == empty %} 161 | you never paid ! 162 | {% endif %} 163 | 164 | {% if user.payments.size > 0 %} 165 | you paid ! 166 | {% endif %} 167 | 168 | {% if user.age > 18 %} 169 | Login here 170 | {% else %} 171 | Sorry, you are too young 172 | {% endif %} 173 | 174 | # array = 1,2,3 175 | {% if array contains 2 %} 176 | array includes 2 177 | {% endif %} 178 | 179 | # string = 'hello world' 180 | {% if string contains 'hello' %} 181 | string includes 'hello' 182 | {% endif %} 183 | {% endraw %} 184 | 185 | ### Case Statement 186 | 187 | If you need more conditions, you can use the case statement: 188 | 如果需要更多的条件,可以使用case语句: 189 | 190 | {% raw %} 191 | {% case condition %} 192 | {% when 1 %} 193 | hit 1 194 | {% when 2 or 3 %} 195 | hit 2 or 3 196 | {% else %} 197 | ... else ... 198 | {% endcase %} 199 | {% endraw %} 200 | 201 | Example: 202 | 203 | {% raw %} 204 | {% case template %} 205 | 206 | {% when 'label' %} 207 | // {{ label.title }} 208 | {% when 'product' %} 209 | // {{ product.vendor | link_to_vendor }} / {{ product.title }} 210 | {% else %} 211 | // {{page_title}} 212 | {% endcase %} 213 | {% endraw %} 214 | 215 | ### Cycle 216 | 217 | Often you have to alternate between different colors or similar tasks. Liquid has built-in support for such operations, using the cycle tag. 218 | 219 | {% raw %} 220 | {% cycle 'one', 'two', 'three' %} 221 | {% cycle 'one', 'two', 'three' %} 222 | {% cycle 'one', 'two', 'three' %} 223 | {% cycle 'one', 'two', 'three' %} 224 | {% endraw %} 225 | 226 | will result in 227 | 228 | one 229 | two 230 | three 231 | one 232 | 233 | If no name is supplied for the cycle group, then it's assumed that multiple calls with the same parameters are one group. 234 | 235 | If you want to have total control over cycle groups, you can optionally specify the name of the group. This can even be a variable. 236 | 237 | {% raw %} 238 | {% cycle 'group 1': 'one', 'two', 'three' %} 239 | {% cycle 'group 1': 'one', 'two', 'three' %} 240 | {% cycle 'group 2': 'one', 'two', 'three' %} 241 | {% cycle 'group 2': 'one', 'two', 'three' %} 242 | {% endraw %} 243 | 244 | will result in 245 | 246 | one 247 | two 248 | one 249 | two 250 | 251 | ### For loops 252 | 253 | Liquid allows for loops over collections: 254 | Liquid同样允许对集合应用循环: 255 | 256 | {% raw %} 257 | {% for item in array %} 258 | {{ item }} 259 | {% endfor %} 260 | {% endraw %} 261 | When iterating a hash, item[0] contains the key, and item[1] contains the value: 262 | 如果对哈希表进行迭代,item[0]表示键,item[1]表示键所对应的值: 263 | 264 | {% raw %} 265 | {% for item in hash %} 266 | {{ item[0] }}: {{ item[1] }} 267 | {% endfor %} 268 | {% endraw %} 269 | During every for loop, the following helper variables are available for extra styling needs: 270 | 在每次循环中,提供如下的辅助变量以供特殊的样式需求: 271 | 272 | forloop.length # => length of the entire for loop 273 | forloop.index # => index of the current iteration 274 | forloop.index0 # => index of the current iteration (zero based) 275 | forloop.rindex # => how many items are still left? 276 | forloop.rindex0 # => how many items are still left? (zero based) 277 | forloop.first # => is this the first iteration? 278 | forloop.last # => is this the last iteration? 279 | 280 | There are several attributes you can use to influence which items you receive in your loop 281 | 282 | limit:int lets you restrict how many items you get. offset:int lets you start the collection with the nth item. 283 | 284 | {% raw %} 285 | # array = [1,2,3,4,5,6] 286 | {% for item in array limit:2 offset:2 %} 287 | {{ item }} 288 | {% endfor %} 289 | # results in 3,4 290 | {% endraw %} 291 | 292 | ### Reversing the loop 293 | 294 | {% for item in collection reversed %} {{item}} {% endfor %} 295 | 296 | Instead of looping over an existing collection, you can define a range of numbers to loop through. The range can be defined by both literal and variable numbers: 297 | 298 | {% raw %} 299 | # if item.quantity is 4... 300 | {% for i in (1..item.quantity) %} 301 | {{ i }} 302 | {% endfor %} 303 | # results in 1,2,3,4 304 | {% endraw %} 305 | 306 | ### Variable Assignment 307 | 308 | You can store data in your own variables, to be used in output or other tags as desired. The simplest way to create a variable is with the assign tag, which has a pretty straightforward syntax: 309 | 310 | 可以将数据存放在自己设定的变量里,然后output标签或者tags标签中使用。最简单的创建变量的方法是通过`assign`标签,其语法简洁明了。 311 | 312 | {% raw %} 313 | {% assign name = 'freestyle' %} 314 | {% for t in collections.tags %}{% if t == name %} 315 |Freestyle!
316 | {% endif %}{% endfor %} 317 | {% endraw %} 318 | 319 | Another way of doing this would be to assign true / false values to the variable: 320 | 上述代码的另一种处理方式是通过给变量赋 `true/false` 值: 321 | 322 | {% raw %} 323 | {% assign freestyle = false %} 324 | 325 | {% for t in collections.tags %}{% if t == 'freestyle' %} 326 | {% assign freestyle = true %} 327 | {% endif %}{% endfor %} 328 | 329 | {% if freestyle %} 330 |Freestyle!
331 | {% endif %} 332 | {% endraw %} 333 | If you want to combine a number of strings into a single string and save it to a variable, you can do that with the `capture` tag. This tag is a block which "captures" whatever is rendered inside it, then assigns the captured value to the given variable instead of rendering it to the screen. 334 | 335 | 如果想要将一组字符串组合成单个字符串并将其包存到变量中,可以使用capture标签,该标签是一个捕获任何在其内部渲染的内容代码块,然后将其赋值到给定的变量而不是渲染到屏幕上。 336 | 337 | {% raw %} 338 | {% capture attribute_name %}{{ item.title | handleize }}-{{ i }}-color{% endcapture %} 339 | 340 | 341 | 346 | {% endraw %} 347 | 处理结果如下: 348 | {% capture attribute_name %}{{ item.title | handleize }}-{{ i }}-color{% endcapture %} 349 | 350 | 351 | 356 | 357 | ## 后记 358 | ---- 359 | Liquid模板还是相当的简洁好学的,剩下的就剩如何使用了。 360 | 361 | [liquid]: https://github.com/shopify/liquid/wiki/liquid-for-designers 362 | -------------------------------------------------------------------------------- /en/preface.textile: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | --- 4 | 5 | h2. Preface 6 | 7 | 8 | This book explores several themes with the following goals in mind: 9 | 10 | 11 | * To have knowledge of the structure of @ruby@ 12 | * To gain knowledge about language processing systems in general 13 | * To acquire skills in reading source code 14 | 15 | 16 | Ruby is an object-oriented language developed by Yukihiro Matsumoto. The 17 | official implementation of the Ruby language is called @ruby@. It is actively 18 | developed and maintained by the open source community. Our first goal is to 19 | understand the inner-workings of the @ruby@ implementation. This book is going 20 | to investigate @ruby@ as a whole. 21 | 22 | 23 | Secondly, by knowing about the implementation of Ruby, we will be able to know 24 | about other language processing systems. I tried to cover all topics necessary 25 | for implementing a language, such as hash table, scanner and parser, evaluation 26 | procedure, and many others. Because this book is not intended as a text book, 27 | going through entire areas and ideas without any lack was not reasonable. 28 | However the parts relating to the essential structures of a language 29 | implementation are adequately explained. 30 | And a brief summary of Ruby language itself is also included 31 | so that readers who don't know about Ruby can read this book. 32 | 33 | 34 | The main themes of this book are the first and the second point above. Though, 35 | what I want to emphasize the most is the third one: To acquire skill in reading 36 | source code. I dare to say it's a "hidden" theme. I will explain why I thought 37 | it is necessary. 38 | 39 | 40 | It is often said "To be a skilled programmer, you should read source code 41 | written by others." This is certainly true. But I haven't found a book that 42 | explains how you can actually do it. There are many books that explain OS 43 | kernels and the interior of language processing systems by showing the concrete 44 | structure or "the answer," but they don't explain the way to reach that answer. 45 | It's clearly one-sided. 46 | 47 | 48 | Can you, perhaps, naturally read code just because you know how to write a 49 | program? Is it true that reading codes is so easy that all people in this world 50 | can read code written by others with no sweat? I don't think so. 51 | Reading programs is certainly as difficult as writing programs. 52 | 53 | 54 | Therefore, this book does not simply explain @ruby@ as something already known, 55 | rather demonstrate the analyzing process as graphic as possible. 56 | Though I think I'm a reasonably seasoned Ruby programmer, 57 | I did not fully understand the inner structure of @ruby@ at the time when I 58 | started to write this book. 59 | In other words, regarding the content of @ruby@, 60 | I started from the position as close as possible to readers. 61 | This book is the summary of both the analyzing process started from that point 62 | and its result. 63 | 64 | 65 | I asked Yukihiro Matsumoto, the author of @ruby@, for supervision. But I 66 | thought the spirit of this book would be lost if each analysis was monitored by 67 | the author of the language himself. Therefore I limited his review to the final 68 | stage of writing. In this way, 69 | without loosing the sense of actually reading the source codes, 70 | I think I could also assure the correctness of the contents. 71 | 72 | 73 | To be honest, this book is not easy. In the very least, it is limited in its 74 | simplicity by the inherent complexity of its aim. However, this complexity may 75 | be what makes the book interesting to you. Do you find it interesting to be 76 | chattering around a piece of cake? Do you take to your desk to solve a puzzle 77 | that you know the answer to in a heartbeat? How about a suspense novel whose 78 | criminal you can guess halfway through? If you really want to come to new 79 | knowledge, you need to solve a problem engaging all your capacities. This is 80 | the book that lets you practice such idealism exhaustively. 81 | "It's interesting because it's difficult." I'm glad if the number of people 82 | who think so will increase because of this book. 83 | 84 | h2. Target audience 85 | 86 | Firstly, knowledge about the Ruby language isn't required. However, since the 87 | knowledge of the Ruby language is absolutely necessary to understand certain 88 | explanations of its structure, supplementary explanations of the language are 89 | inserted here and there. 90 | 91 | 92 | Knowledge about the C language is required, to some extent. I assume you can 93 | allocate some structs with @malloc()@ at runtime to create a list or a stack 94 | and you have experience of using function pointers at least a few times. 95 | 96 | 97 | Also, since the basics of object-oriented programming will not be explained so 98 | seriously, without having any experience of using at least one of 99 | object-oriented languages, you will probably have a difficult time. 100 | In this book, I tried to use many examples in Java and C++. 101 | 102 | h2. Structure of this book 103 | 104 | This book has four main parts: 105 | 106 | 107 | | Part 1: Objects | 108 | | Part 2: Syntactic analysis | 109 | | Part 3: Evaluation | 110 | | Part 4: Peripheral around the evaluator | 111 | 112 | 113 | Supplementary chapters are included at the beginning of each part when 114 | necessary. These provide a basic introduction for those who are not familiar 115 | with Ruby and the general mechanism of a language processing system. 116 | 117 | 118 | Now, we are going through the overview of the four main parts. The symbol in 119 | parentheses after the explanation indicates the difficulty gauge. They are ==(C)==, 120 | (B), (A) in order of easy to hard, (S) being the highest. 121 | 122 | h4. Part 1: Object 123 | 124 | | Chapter1 | Focuses the basics of Ruby to get ready to accomplish Part 1. ==(C)== | 125 | | Chapter2 | Gives concrete inner structure of Ruby objects. ==(C)== | 126 | | Chapter3 | States about hash table. ==(C)== | 127 | | Chapter4 | Writes about Ruby class system. You may read through this chapter quickly at first, because it tells plenty of abstract stories. (A) | 128 | | Chapter5 | Shows the garbage collector which is responsible for generating and releasing objects. The first story in low-level series. (B) | 129 | | Chapter6 | Describes the implementation of global variables, class variables, and constants. ==(C)== | 130 | | Chapter7 | Outline of the security features of Ruby. ==(C)== | 131 | 132 | h4. Part 2: Syntactic analysis 133 | 134 | | Chapter8 | Talks about almost complete specification of the Ruby language, in order to prepare for Part 2 and Part 3. ==(C)== | 135 | | Chapter9 | Introduction to @yacc@ required to read the syntax file at least. (B) | 136 | | Chapter10 | Look through the rules and physical structure of the parser. (A) | 137 | | Chapter11 | Explore around the peripherals of @lex_state@, which is the most difficult part of the parser. The most difficult part of this book. (S) | 138 | | Chapter12 | Finalization of Part 2 and connection to Part 3. ==(C)== | 139 | 140 | h4. Part 3: Evaluator 141 | 142 | | Chapter13 | Describe the basic mechanism of the evaluator. ==(C)== | 143 | | Chapter14 | Reads the evaluation stack that creates the main context of Ruby. (A) | 144 | | Chapter15 | Talks about search and initialization of methods. (B) | 145 | | Chapter16 | Defies the implementation of the iterator, the most characteristic feature of Ruby. (A) | 146 | | Chapter17 | Describe the implementation of the eval methods. (B) | 147 | 148 | h4. Part 4: Peripheral around the evaluator 149 | 150 | | Chapter18 | Run-time loading of libraries in C and Ruby. (B) | 151 | | Chapter19 | Describes the implementation of thread at the end of the core part. (A) | 152 | 153 | h2. Environment 154 | 155 | This book describes on @ruby@ 1.7.3 2002-09-12 version. It's attached on the 156 | CD-ROM. Choose any one of @ruby-rhg.tar.gz@, @ruby-rhg.lzh@, or @ruby-rhg.zip@ 157 | according to your convenience. Content is the same for all. Alternatively you 158 | can obtain from the support site (footnote{`http://i.loveruby.net/ja/rhg/`}) of 159 | this book. 160 | 161 | 162 | For the publication of this book, the following build environment was prepared 163 | for confirmation of compiling and testing the basic operation. The details of 164 | this build test are given in @doc/buildtest.html@ in the attached CD-ROM. 165 | However, it doesn't necessarily assume the probability of the execution even 166 | under the same environment listed in the table. The author doesn't guarantee 167 | in any form the execution of @ruby@. 168 | 169 | 170 | * BeOS 5 Personal Edition/i386 171 | * Debian GNU/Linux potato/i386 172 | * Debian GNU/Linux woody/i386 173 | * Debian GNU/Linux sid/i386 174 | * FreeBSD 4.4-RELEASE/Alpha (Requires the local patch for this book) 175 | * FreeBSD 4.5-RELEASE/i386 176 | * FreeBSD 4.5-RELEASE/PC98 177 | * FreeBSD 5-CURRENT/i386 178 | * HP-UX 10.20 179 | * HP-UX 11.00 (32bit mode) 180 | * HP-UX 11.11 (32bit mode) 181 | * Mac OS X 10.2 182 | * NetBSD 1.6F/i386 183 | * OpenBSD 3.1 184 | * Plamo Linux 2.0/i386 185 | * Linux for PlayStation2 Release 1.0 186 | * Redhat Linux 7.3/i386 187 | * Solaris 2.6/Sparc 188 | * Solaris 8/Sparc 189 | * UX/4800 190 | * Vine Linux 2.1.5 191 | * Vine Linux 2.5 192 | * VineSeed 193 | * Windows 98SE (Cygwin, MinGW+Cygwin, MinGW+MSYS) 194 | * Windows Me (Borland C++ Compiler 5.5, Cygwin, MinGW+Cygwin, MinGW+MSYS, Visual C++ 6) 195 | * Windows NT 4.0 (Cygwin, MinGW+Cygwin) 196 | * Windows 2000 (Borland C++ Compiler 5.5, Visual C++ 6, Visual C++.NET) 197 | * Windows XP (Visual C++.NET, MinGW+Cygwin) 198 | 199 | 200 | These numerous tests aren't of a lone effort by the author. Those test build 201 | couldn't be achieved without magnificent cooperations by the people listed 202 | below. 203 | 204 | I'd like to extend warmest thanks from my heart. 205 | 206 | 207 | | Tietew | 208 | | kjana | 209 | | nyasu | 210 | | sakazuki | 211 | | Masahiro Sato | 212 | | Kenichi Tamura | 213 | | Morikyu | 214 | | Yuya Kato | 215 | | Yasuhiro Kubo | 216 | | Kentaro Goto | 217 | | Tomoyuki Shimomura | 218 | | Masaki Sukeda | 219 | | Koji Arai | 220 | | Kazuhiro Nishiyama | 221 | | Shinya Kawaji | 222 | | Tetsuya Watanabe | 223 | | Naokuni Fujimoto | 224 | 225 | 226 | However, the author owes the responsibility for this test. Please refrain from 227 | attempting to contact these people directly. If there's any flaw in execution, 228 | please be advised to contact the author by e-mail: `aamine@loveruby.net`. 229 | 230 | h2. Web site 231 | 232 | The web site for this book is `http://i.loveruby.net/ja/rhg/`. 233 | I will add information about related programs and additional documentation, as 234 | well as errata. In addition, I'm going to publisize the first few chapters of 235 | this book at the same time of the release. I will look for a certain 236 | circumstance to publicize more chapters, and the whole contents of the book 237 | will be at this website at the end. 238 | 239 | h2. Acknowledgment 240 | 241 | First of all, I would like to thank Mr. Yukihiro Matsumoto. He is the author of 242 | Ruby, and he made it in public as an open source software. Not only he 243 | willingly approved me to publish a book about analyzing @ruby@, but also he 244 | agreed to supervise the content of it. In addition, he helped my stay in 245 | Florida with simultaneous translation. There are plenty of things beyond 246 | enumeration I have to say thanks to him. Instead of writing all the things, I 247 | give this book to him. 248 | 249 | 250 | Next, I would like to thank arton, who proposed me to publish this book. The 251 | words of arton always moves me. One of the things I'm currently struggled due 252 | to his words is that I have no reason I don't get a .NET machine. 253 | 254 | 255 | Koji Arai, the 'captain' of documentation in the Ruby society, conducted a 256 | scrutiny review as if he became the official editor of this book while I was 257 | not told so. I thank all his review. 258 | 259 | 260 | Also I'd like to mention those who gave me comments, pointed out mistakes and 261 | submitted proposals about the construction of the book throughout all my work. 262 | 263 | Tietew, 264 | Yuya, 265 | Kawaji, 266 | Gotoken, 267 | Tamura, 268 | Funaba, 269 | Morikyu, 270 | Ishizuka, 271 | Shimomura, 272 | Kubo, 273 | Sukeda, 274 | Nishiyama, 275 | Fujimoto, 276 | Yanagawa, 277 | (I'm sorry if there's any people missing), 278 | I thank all those people contributed. 279 | 280 | 281 | As a final note, I thank Otsuka , Haruta, and Kanemitsu who you for arranging 282 | everything despite my broke deadline as much as four times, and that the 283 | manuscript exceeded 200 pages than originally planned. 284 | 285 | 286 | I cannot expand the full list here to mention the name of all people 287 | contributed to this book, but I say that I couldn't successfully publish this 288 | book without such assistance. Let me take this place to express my 289 | appreciation. Thank you very much. 290 | 291 | p(right). Minero Aoki 292 | 293 | 294 | If you want to send remarks, suggestions and reports of typographcal errors, 295 | please address to "Minero Aoki <aamine@loveruby.net>":mailto:aamine@loveruby.net . 296 | 297 | 298 | "Rubyソースコード完全解説" can be reserved/ordered at ImpressDirect. 299 | "(Jump to the introduction page)":http://direct.ips.co.jp/directsys/go_x_TempChoice.cfm?sh_id=EE0040&spm_id=1&GM_ID=1721 300 | 301 | Copyright (c) 2002-2004 Minero Aoki, All rights reserved. 302 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* Base */ 2 | /* ----------------------------------------------------------*/ 3 | 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | html, body { height: 100%; } 10 | 11 | body { 12 | font-family: Helvetica, Arial, sans-serif; 13 | font-size: 16px; 14 | line-height: 1.5; 15 | font-weight: 300; 16 | background-color: #fdfdfd; 17 | } 18 | 19 | h1, h2, h3, h4, h5, h6 { font-size: 100%; font-weight: 400; } 20 | 21 | a { color: #2a7ae2; text-decoration: none; } 22 | a:hover { color: #000; text-decoration: underline; } 23 | a:visited { color: #205caa; } 24 | 25 | /* Utility */ 26 | 27 | .wrap:before, 28 | .wrap:after { content:""; display:table; } 29 | .wrap:after { clear: both; } 30 | .wrap { 31 | max-width: 800px; 32 | padding: 0 30px; 33 | margin: 0 auto; 34 | zoom: 1; 35 | } 36 | 37 | 38 | /* Layout Styles */ 39 | /* ----------------------------------------------------------*/ 40 | 41 | /* Site header */ 42 | 43 | .site-header { 44 | border-top: 5px solid #333; 45 | border-bottom: 1px solid #e8e8e8; 46 | min-height: 56px; 47 | background-color: white; 48 | } 49 | 50 | .site-title, 51 | .site-title:hover, 52 | .site-title:visited { 53 | display: block; 54 | color: #333; 55 | font-size: 26px; 56 | letter-spacing: -1px; 57 | float: left; 58 | line-height: 56px; 59 | position: relative; 60 | z-index: 1; 61 | } 62 | 63 | .site-nav { 64 | float: right; 65 | line-height: 56px; 66 | } 67 | 68 | .site-nav .menu-icon { display: none; } 69 | 70 | .site-nav .page-link { 71 | margin-left: 20px; 72 | color: #727272; 73 | letter-spacing: -.5px; 74 | } 75 | 76 | /* Site footer */ 77 | 78 | .site-footer { 79 | border-top: 1px solid #e8e8e8; 80 | padding: 30px 0; 81 | } 82 | 83 | .footer-heading { 84 | font-size: 18px; 85 | font-weight: 300; 86 | letter-spacing: -.5px; 87 | margin-bottom: 15px; 88 | } 89 | 90 | .site-footer .column { float: left; margin-bottom: 15px; } 91 | 92 | .footer-col-1 { 93 | width: 270px; /*fallback*/ 94 | width: -webkit-calc(35% - 10px); 95 | width: -moz-calc(35% - 10px); 96 | width: -o-calc(35% - 10px); 97 | width: calc(35% - 10px); 98 | margin-right: 10px 99 | } 100 | .footer-col-2 { 101 | width: 175px; /*fallback*/ 102 | width: -webkit-calc(23.125% - 10px); 103 | width: -moz-calc(23.125% - 10px); 104 | width: -o-calc(23.125% - 10px); 105 | width: calc(23.125% - 10px); 106 | margin-right: 10px 107 | } 108 | .footer-col-3 { 109 | width: 335px; /*fallback*/ 110 | width: -webkit-calc(41.875%); 111 | width: -moz-calc(41.875%); 112 | width: -o-calc(41.875%); 113 | width: calc(41.875%); 114 | } 115 | 116 | .site-footer ul { list-style: none; } 117 | 118 | .site-footer li, 119 | .site-footer p { 120 | font-size: 15px; 121 | letter-spacing: -.3px; 122 | color: #828282; 123 | } 124 | 125 | .github-icon-svg, 126 | .twitter-icon-svg { 127 | display: inline-block; 128 | width: 16px; 129 | height: 16px; 130 | position: relative; 131 | top: 3px; 132 | } 133 | 134 | 135 | /* Page Content styles */ 136 | /* ----------------------------------------------------------*/ 137 | 138 | .page-content { 139 | padding: 30px 0; 140 | background-color: #fff; 141 | } 142 | 143 | 144 | /* Home styles */ 145 | /* ----------------------------------------------------------*/ 146 | 147 | .home h1 { margin-bottom: 25px; } 148 | 149 | .posts { list-style-type: none; } 150 | 151 | .posts li { margin-bottom: 30px; } 152 | 153 | .posts .post-link { 154 | font-size: 24px; 155 | letter-spacing: -1px; 156 | line-height: 1; 157 | } 158 | 159 | .posts .post-date { 160 | display: block; 161 | font-size: 15px; 162 | color: #818181; 163 | } 164 | 165 | 166 | /* Post styles */ 167 | /* ----------------------------------------------------------*/ 168 | 169 | .post-header { margin: 10px 0 30px; } 170 | 171 | .post-header h1 { 172 | font-size: 42px; 173 | letter-spacing: -1.75px; 174 | line-height: 1; 175 | font-weight: 300; 176 | } 177 | 178 | .post-header .meta { 179 | font-size: 15px; 180 | color: #818181; 181 | margin-top: 5px; 182 | } 183 | 184 | .post-content { margin: 0 0 30px; } 185 | 186 | .post-content > * { margin: 20px 0; } 187 | 188 | 189 | .post-content h1, 190 | .post-content h2, 191 | .post-content h3, 192 | .post-content h4, 193 | .post-content h5, 194 | .post-content h6 { 195 | line-height: 1; 196 | font-weight: 300; 197 | margin: 40px 0 20px; 198 | } 199 | 200 | .post-content h2 { 201 | font-size: 32px; 202 | letter-spacing: -1.25px; 203 | } 204 | 205 | .post-content h3 { 206 | font-size: 26px; 207 | letter-spacing: -1px; 208 | } 209 | 210 | .post-content h4 { 211 | font-size: 20px; 212 | letter-spacing: -1px; 213 | } 214 | 215 | .post-content blockquote { 216 | border-left: 4px solid #e8e8e8; 217 | padding-left: 20px; 218 | font-size: 18px; 219 | opacity: .6; 220 | letter-spacing: -1px; 221 | font-style: italic; 222 | margin: 30px 0; 223 | } 224 | 225 | .post-content ul, 226 | .post-content ol { padding-left: 20px; } 227 | 228 | .post pre, 229 | .post code { 230 | border: 1px solid #d5d5e9; 231 | background-color: #eef; 232 | padding: 8px 12px; 233 | -webkit-border-radius: 3px; 234 | -moz-border-radius: 3px; 235 | border-radius: 3px; 236 | font-size: 15px; 237 | overflow:scroll; 238 | } 239 | 240 | .post code { padding: 1px 5px; } 241 | 242 | .post ul, 243 | .post ol { margin-left: 1.35em; } 244 | 245 | .post pre code { 246 | border: 0; 247 | padding-right: 0; 248 | padding-left: 0; 249 | } 250 | 251 | /* terminal */ 252 | .post pre.terminal { 253 | border: 1px solid #000; 254 | background-color: #333; 255 | color: #FFF; 256 | -webkit-border-radius: 3px; 257 | -moz-border-radius: 3px; 258 | border-radius: 3px; 259 | } 260 | 261 | .post pre.terminal code { background-color: #333; } 262 | 263 | /* Syntax highlighting styles */ 264 | /* ----------------------------------------------------------*/ 265 | 266 | .highlight { background: #ffffff; } 267 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 268 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 269 | .highlight .k { font-weight: bold } /* Keyword */ 270 | .highlight .o { font-weight: bold } /* Operator */ 271 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 272 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 273 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 274 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 275 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 276 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 277 | .highlight .ge { font-style: italic } /* Generic.Emph */ 278 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 279 | .highlight .gh { color: #999999 } /* Generic.Heading */ 280 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 281 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 282 | .highlight .go { color: #888888 } /* Generic.Output */ 283 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 284 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 285 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 286 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 287 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 288 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 289 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 290 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 291 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 292 | .highlight .m { color: #009999 } /* Literal.Number */ 293 | .highlight .s { color: #d14 } /* Literal.String */ 294 | .highlight .na { color: #008080 } /* Name.Attribute */ 295 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 296 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 297 | .highlight .no { color: #008080 } /* Name.Constant */ 298 | .highlight .ni { color: #800080 } /* Name.Entity */ 299 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 300 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 301 | .highlight .nn { color: #555555 } /* Name.Namespace */ 302 | .highlight .nt { color: #000080 } /* Name.Tag */ 303 | .highlight .nv { color: #008080 } /* Name.Variable */ 304 | .highlight .ow { font-weight: bold } /* Operator.Word */ 305 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 306 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 307 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 308 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 309 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 310 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 311 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 312 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 313 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 314 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 315 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 316 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 317 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 318 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 319 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 320 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 321 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 322 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 323 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 324 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 325 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 326 | 327 | 328 | /* media queries */ 329 | /* ----------------------------------------------------------*/ 330 | 331 | 332 | @media screen and (max-width: 750px) { 333 | 334 | .footer-col-1 { width: 50%; } 335 | 336 | .footer-col-2 { 337 | width: 45%; /*fallback*/ 338 | width: -webkit-calc(50% - 10px); 339 | width: -moz-calc(50% - 10px); 340 | width: -o-calc(50% - 10px); 341 | width: calc(50% - 10px); 342 | margin-right: 0; 343 | } 344 | 345 | .site-footer .column.footer-col-3 { 346 | width: auto; 347 | float: none; 348 | clear: both; 349 | } 350 | 351 | } 352 | 353 | @media screen and (max-width: 600px) { 354 | 355 | .wrap { padding: 0 12px; } 356 | 357 | .site-nav { 358 | position: fixed; 359 | z-index: 10; 360 | top: 14px; right: 8px; 361 | background-color: white; 362 | -webkit-border-radius: 5px; 363 | -moz-border-radius: 5px; 364 | border-radius: 5px; 365 | border: 1px solid #e8e8e8; 366 | } 367 | 368 | .site-nav .menu-icon { 369 | display: block; 370 | font-size: 24px; 371 | color: #505050; 372 | float: right; 373 | width: 36px; 374 | text-align: center; 375 | line-height: 36px; 376 | } 377 | 378 | .site-nav .menu-icon svg { width: 18px; height: 16px; } 379 | 380 | .site-nav .trigger { 381 | clear: both; 382 | margin-bottom: 5px; 383 | display: none; 384 | } 385 | 386 | .site-nav:hover .trigger { display: block; } 387 | 388 | .site-nav .page-link { 389 | display: block; 390 | text-align: right; 391 | line-height: 1.25; 392 | padding: 5px 10px; 393 | margin: 0; 394 | } 395 | 396 | .post-header h1 { font-size: 36px; } 397 | .post-content h2 { font-size: 28px; } 398 | .post-content h3 { font-size: 22px; } 399 | .post-content h4 { font-size: 18px; } 400 | .post-content blockquote { padding-left: 10px; } 401 | .post-content ul, 402 | .post-content ol { padding-left: 10px; } 403 | 404 | .site-footer .column { 405 | float: none; 406 | clear: both; 407 | width: auto; 408 | margin: 0 0 15px; } 409 | 410 | } 411 | 412 | /* 表格样式控制 */ 413 | table { 414 | margin-bottom: 20px; 415 | max-width: 100%; 416 | border-collapse: collapse; 417 | border: 1px solid rgba(50,118,177,0.167); 418 | transition: all 0.3s 419 | } 420 | 421 | table:hover { 422 | border: 1px solid rgba(50,118,177,0.35); 423 | box-shadow: 0 0 3 rgba(50,118,177,0.267); 424 | transition: all 0.3s 425 | } 426 | 427 | table thead tr th { 428 | border: 1px solid rgba(50,118,177,0.167); 429 | border-top: 0 none; 430 | border-bottom-width: 2px; 431 | vertical-align: bottom; 432 | padding: 8px 433 | } 434 | 435 | table tbody tr td { 436 | border: 1px solid rgba(50,118,177,0.167); 437 | vertical-align: middle; 438 | padding: 8px 439 | } 440 | 441 | table thead>tr { 442 | background-color: rgba(249,249,249,0.9) 443 | } 444 | 445 | table tbody>tr:nth-child(2n+1) { 446 | background-color: rgba(249,249,249,0.5) 447 | } 448 | 449 | table tbody tr:hover { 450 | background-color: rgba(235,235,235,0.7); 451 | transition: all 0.3s 452 | } 453 | 454 | table li { 455 | margin: 5px 0 456 | } 457 | 458 | table img { 459 | max-width: 85%; 460 | margin: 0 auto; 461 | display: inherit; 462 | border-radius: 6px; 463 | padding: 4px; 464 | line-height: 1.429 465 | } 466 | 467 | table code { 468 | background-color: rgba(255,204,204,0.3); 469 | box-shadow: 0 0 1px rgba(0,0,0,0.167); 470 | border: 1px solid rgba(102,102,102,0.167); 471 | font-family: Monaco, Consolas, Terminal, monospace 472 | } 473 | -------------------------------------------------------------------------------- /zh/03-name.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 第三章:名称与名称表 4 | --- 5 | 6 | ## st_table 7 | ---- 8 | 9 | 作为方法表和实例表,st_table在前面提到过。在本章中,让我们详细看一下st_table的机制。 10 | 11 | ### 概要 12 | 13 | 我之前提到过,st_table是一个hash表。什么是hash表呢?它是一种数据结构,记录一对一的关系,比如变量名与其值,函数名与其体,等等。 14 | 15 | 然而,除hash表外,当然还有其它的数据结构,可以记录一对一的关系。比如下面的list数据结构也能满足这个目的。 16 | 17 | struct entry { 18 | ID key; 19 | VALUE val; 20 | struct entry *next; /* 指向下一项 */ 21 | }; 22 | 23 | 然而,这种方法缓慢。如果list中包含上千项,在最差的情况下,必需穿越上千次的链。 换句话说,搜索时间随元素数量的比例而增长。这很糟糕。从远古时代开始, 就酝酿出许多不同的提速方法。hash表就是提速方法之一。换句话说,这里的要点不在于是否需要hash表,而是因为它可以更快一些。 24 | 25 | 那么接下来,我们来看看st_table。但首先说一下,这个库并非Matsumoto所创,而是: 26 | ▼ st.c credits 27 | 28 | 1 /* This is a public domain general purpose hash table package 29 | written by Peter Moore @ UCB. */ 30 | 31 | (st.c) 32 | 33 | 如上所示。 34 | 35 | 顺便说一下,当我用Google搜索时,找到了另一个版本,它提到st_table是“STring TABLE”的缩写。 然而,我发现一个矛盾,它有“通用目的”和“字符串”两个方面。 36 | 37 | #### hash表是什么? 38 | 39 | 可以这样理解hash表:让我们把它看作一个拥有n项的数组。比如,假设n=64(图1)。 40 | 41 | 数组 42 | 图1: 数组 43 | 44 | 然后,我们指定一个函数f,用一个键值产生一个0到n-1(0-63)的整数i。我们称这个f为hash函数。 对于给定的相同键值,f总会产生i。比如,如果我们做个假设,把键值限定为正整数,那么如果键值被64整除, 那么,余数总是在0到63之间。这个计算方法就可以作为函数f。 45 | 46 | 记录关系时,给定一个键值,函数f产生i,把它放到我们预备好的数组中索引为i的位置上。 换句话说,通过索引访问数组非常快。因此,基本的想法就是把键值变成整数。 47 | 48 | 数组赋值 49 | 图2: 数组赋值 50 | 51 | 然而,在现实世界中,问题没那么简单。这个想法有一个关键的问题。因为n只是64,如果要记录多于64个的关系, 肯定会有两个不同的键值有相同的索引。即便少于64,也可能会有相同的发生。以前面hash函数“key % 64”为例, 键值65和129的hash值都是1。这称为hash值冲突。有许多办法用来解决冲突。 52 | 53 | 比如,如果发生冲突,把它插入下一个元素的位置。这种方法叫做开放寻址。(图3) 54 | 55 | 开放寻址 56 | 图3: 开放寻址 57 | 58 | 除了像这样使用数组,还有其它的方式,比如使用指针,数组的每个元素都是一个指向各自链表的指针。 那么当发生冲突时,链表随之生长。这称为链接(chaining)。(图4)st_table使用的就是这种链接的方法。 59 | 60 | 链接 61 | 图4: 链接 62 | 63 | 然而,如果确定用到是哪些键值,那么可以想象有一个绝不会产生冲突的hash函数。这种函数称为“完美hash函数”。 实际上,确实有一些工具能够对一套给定的任意字符串创建了一个完美hash函数。GNU gpref就是其中之一。 ruby的解析器实现就使用了GNU gperf,但是……这还不到我们讨论它的时候。我们会在本书的第二部分讨论它。 64 | 数据结构 65 | 66 | 让我们从看源码起步。正如在引介的章节中所写,如果既有数据又有代码,最好先看数据。下面就是st_table的数据类型。 67 | ▼ st_table 68 | 69 | 9 typedef struct st_table st_table; 70 | 71 | 16 struct st_table { 72 | 17 struct st_hash_type *type; 73 | 18 int num_bins; /* slot count */ 74 | 19 int num_entries; /* total number of entries */ 75 | 20 struct st_table_entry **bins; /* slot */ 76 | 21 }; 77 | 78 | (st.h) 79 | 80 | ▼ struct st_table_entry 81 | 82 | 16 struct st_table_entry { 83 | 17 unsigned int hash; 84 | 18 char *key; 85 | 19 char *record; 86 | 20 st_table_entry *next; 87 | 21 }; 88 | 89 | (st.c) 90 | 91 | st_table主要是一个表的数据结构。st_table_entry持有存储的一个值。st_table_entry包含一个称为next的成员, 用于将st_table_entry放入一个链表。这是链接方法链的部分。这里用到了st_hash_type数据类型,稍后解释。 先让我解释其它部分,这样,你就能够对比和理解其角色了。 92 | 93 | st_table数据结构 94 | 图5: st_table数据结构 95 | 96 | 那么让我们来看看st_hash_type。 97 | ▼ struct st_hash_type 98 | 99 | 11 struct st_hash_type { 100 | 12 int (*compare)(); /* comparison function */ 101 | 13 int (*hash)(); /* hash function */ 102 | 14 }; 103 | 104 | (st.h) 105 | 106 | 这还是第三章,那么我们换种方式来看它。 107 | 108 | int (*compare)() 109 | 110 | 这个部分表示compare成员的数据类型是一个函数指针,其返回值为int。hash也具有同样的类型。 这个变量可以用下面的方式替代: 111 | 112 | int 113 | great_function(int n) 114 | { 115 | /* ToDo: 做一些伟大的事情! */ 116 | return n; 117 | } 118 | 119 | { 120 | int (*f)(); 121 | f = great_function; 122 | 123 | 这样调用: 124 | 125 | (*f)(7); 126 | } 127 | 128 | 我们回到st_hash_type。hash和compare两个成员之中,hash就是前面提到的hash函数f。 129 | 130 | 另一方面,compare这个函数的作用是评估值是否相同。在链接方法中,相同hash值的点里可能插入了多个元素。 为了准确知道查找的是哪个元素,必需使用一个我们绝对信任的比较函数。compare就是那个函数。 131 | 132 | 这个st_hash_type是一项很好的泛化的技术。hash表本身并不确定存储键值的数据类型是什么。 比如,在ruby中,st_table的键值是ID或char*或VALUE,但是对每个(数据类型)都使用同样的hash是愚蠢的。 通常,随键值数据类型不同而改变的就是hasn函数之类的东西。对于内存管理和冲突检测之类的东西,通常大部分代码都一样。 只有实现需要随数据类型不同而改变的部分绑定到一个函数中,使用一个函数指针指向那个函数。 这样的话,组成hash表实现的主体代码就可以使用它了。 133 | 134 | 在面向对象的语言中,你可以把过程附着在一个对象上,传递它,因此,不需要这种机制。 也许说这种机制作为一种语言特征内建其中更合适。 135 | st_hash_type的例子 136 | 137 | 使用一个类似于st_hash_type的数据结构是一种很好的抽象,但另一方面,理解它实际传递是何种代码可能就有些困难了。 如果不检查hash或compare用的是什么函数,我们便无法掌握事实。为了理解这点,可能要看一下st_init_numtable()才行, 在前面一章中,我们介绍过它。这个函数创建了一个表,其键值的数据类型为整数。 138 | ▼ st_init_numtable() 139 | 140 | 182 st_table* 141 | 183 st_init_numtable() 142 | 184 { 143 | 185 return st_init_table(&type_numhash); 144 | 186 } 145 | 146 | (st.c) 147 | 148 | st_init_table()是这样一个函数,它完成表内存分配等工作。type_numhash是一个st_hash_type (它是一个名为st_table“类型”的成员)。看看这个type_numhash: 149 | ▼ type_numhash 150 | 151 | 37 static struct st_hash_type type_numhash = { 152 | 38 numcmp, 153 | 39 numhash, 154 | 40 }; 155 | 156 | 552 static int 157 | 553 numcmp(x, y) 158 | 554 long x, y; 159 | 555 { 160 | 556 return x != y; 161 | 557 } 162 | 163 | 559 static int 164 | 560 numhash(n) 165 | 561 long n; 166 | 562 { 167 | 563 return n; 168 | 564 } 169 | 170 | (st.c) 171 | 172 | 非常简单。ruby使用的这个表基本上就是这个type_numhash。 173 | st_lookup() 174 | 175 | 接下来,我们看一下使用这个数据结构的函数。首先,从搜索的函数看起是个好主意。 下面就是搜索hash表的函数st_lookup()。 176 | 177 | ▼ st_lookup() 178 | 179 | 247 int 180 | 248 st_lookup(table, key, value) 181 | 249 st_table *table; 182 | 250 register char *key; 183 | 251 char **value; 184 | 252 { 185 | 253 unsigned int hash_val, bin_pos; 186 | 254 register st_table_entry *ptr; 187 | 255 188 | 256 hash_val = do_hash(key, table); 189 | 257 FIND_ENTRY(table, ptr, hash_val, bin_pos); 190 | 258 191 | 259 if (ptr == 0) { 192 | 260 return 0; 193 | 261 } 194 | 262 else { 195 | 263 if (value != 0) *value = ptr->record; 196 | 264 return 1; 197 | 265 } 198 | 266 } 199 | 200 | (st.c) 201 | 202 | 重要的部分几乎都在do_hash()和FIND_ENTRY()中。让我们按顺序看一下。 203 | 204 | ▼ do_hash() 205 | 206 | 68 #define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key)) 207 | 208 | (st.c) 209 | 210 | 慎重起见,我们记下这个难于理解的宏的主体: 211 | 212 | > (table)->type->hash 213 | 214 | 是一个函数指针,key作为参数传递给它。这是调用函数的语法。*不是用在表上。换句话说,这个宏是一个hash值产生器, 每个数据类型都有事先预备好的hash函数type->hash,用它对键值产生一个hash值。 215 | 216 | 下面,继续来看FIND_ENTRY()。 217 | 218 | ▼ FIND_ENTRY() 219 | 220 | 235 #define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\ 221 | 236 bin_pos = hash_val%(table)->num_bins;\ 222 | 237 ptr = (table)->bins[bin_pos];\ 223 | 238 if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\ 224 | 239 COLLISION;\ 225 | 240 while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\ 226 | 241 ptr = ptr->next;\ 227 | 242 }\ 228 | 243 ptr = ptr->next;\ 229 | 244 }\ 230 | 245 } while (0) 231 | 232 | 227 #define PTR_NOT_EQUAL(table, ptr, hash_val, key) ((ptr) != 0 && \ 233 | (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key))) 234 | 235 | 66 #define EQUAL(table,x,y) \ 236 | ((x)==(y) || (*table->type->compare)((x),(y)) == 0) 237 | 238 | (st.c) 239 | 240 | COLLISION是一个调试宏,所以,我们(应该)忽略它。 241 | 242 | FIND_ENTRY()的参数,从左开始是: 243 | 244 | - st_table 245 | - 这个参数指向的找到项 246 | - hash值 247 | - 临时变量 248 | 249 | 第二个参数会指向找到的`st_table_entry*`。 250 | 251 | 在最外层的,do .. while(0)用于对多表达式的宏进行安全封装。与其说是ruby, 不如说这是C语言预处理器的一种常用手法。在if(1)的情况下,可能需要附加else。 在while(1)的情况下,可能需要在最后添加一个break。 252 | 253 | 还有,while(0)后面没有分号。说到原因 254 | 255 | > FIND_ENTRY(); 256 | 257 | 这样就不会让出现在通常表达式后的逗号成为徒劳。 258 | > st_add_direct() 259 | 260 | 继续,让我们来看看st_add_direct(),它向hash表中添加一个新的关系。 这个函数并不检查键值是否已经存在。它总会添加一个新的项。这就是函数名中direct的含义所在。 261 | 262 | ▼ st_add_direct() 263 | 264 | 308 void 265 | 309 st_add_direct(table, key, value) 266 | 310 st_table *table; 267 | 311 char *key; 268 | 312 char *value; 269 | 313 { 270 | 314 unsigned int hash_val, bin_pos; 271 | 315 272 | 316 hash_val = do_hash(key, table); 273 | 317 bin_pos = hash_val % table->num_bins; 274 | 318 ADD_DIRECT(table, key, value, hash_val, bin_pos); 275 | 319 } 276 | 277 | (st.c) 278 | 279 | 如同前面一样,这里调用do_hash()宏获取一个值。随后,下一个计算等同于FIND_ENTRY()开始的部分, 它以hash值得到真正的索引。 280 | 281 | 插入操作看上去是以ADD_DIRECT()实现的,既然名字全部大写,我们期待它是一个宏。 282 | 283 | ▼ ADD_DIRECT() 284 | 285 | 268 #define ADD_DIRECT(table, key, value, hash_val, bin_pos) \ 286 | 269 do { \ 287 | 270 st_table_entry *entry; \ 288 | 271 if (table->num_entries / (table->num_bins) \ 289 | > ST_DEFAULT_MAX_DENSITY) { \ 290 | 272 rehash(table); \ 291 | 273 bin_pos = hash_val % table->num_bins; \ 292 | 274 } \ 293 | 275 \ 294 | /* (A) */ \ 295 | 276 entry = alloc(st_table_entry); \ 296 | 277 \ 297 | 278 entry->hash = hash_val; \ 298 | 279 entry->key = key; \ 299 | 280 entry->record = value; \ 300 | /* (B) */ \ 301 | 281 entry->next = table->bins[bin_pos]; \ 302 | 282 table->bins[bin_pos] = entry; \ 303 | 283 table->num_entries++; \ 304 | 284 } while (0) 305 | 306 | (st.c) 307 | 308 | 第一个if是一个异常情况,我稍后解释它。 309 | 310 | (A) 分配以及初始化一个st_table_entry。 311 | 312 | (B) 把entry插入到列表的起始位置。这是处理列表的常见手法。换句话说, 313 | 314 | entry->next = list_beg; 315 | list_beg = entry; 316 | 317 | 将一个项插入到列表的前端。这类似于Lisp语言中的“cons-ing”。自己检查一下, 即便list_beg为空,这段代码也是正确的。 318 | 319 | 现在,让我来解释一下我留下的那段代码。 320 | 321 | ▼ ADD_DIRECT()-rehash 322 | 323 | 271 if (table->num_entries / (table->num_bins) \ 324 | > ST_DEFAULT_MAX_DENSITY) { \ 325 | 272 rehash(table); \ 326 | 273 bin_pos = hash_val % table->num_bins; \ 327 | 274 } \ 328 | 329 | (st.c) 330 | 331 | DENSITY is“浓度”。换句话说,这个条件检查hash表是否“拥挤”。在st_table中, 随着使用相同bin_pos的增长,链表会变得更长。换句话说,搜索会变慢。 如果bin中的元素过多,那么就应该增加bin的数量,降低拥挤程度。 332 | 333 | 当前ST_DEFAULT_MAX_DENSITY是 334 | 335 | ▼ ST_DEFAULT_MAX_DENSITY 336 | 337 | 23 #define ST_DEFAULT_MAX_DENSITY 5 338 | 339 | (st.c) 340 | 341 | 因为这个设置,如果在所有的bin_pos都有5个st_table_entries,那么大小就要增加。 342 | st_insert() 343 | 344 | st_insert()只是将st_add_direct()和st_lookup()组合了起来,因此, 如果你理解了那两个,这个就容易了。 345 | 346 | ▼ st_insert() 347 | 348 | 286 int 349 | 287 st_insert(table, key, value) 350 | 288 register st_table *table; 351 | 289 register char *key; 352 | 290 char *value; 353 | 291 { 354 | 292 unsigned int hash_val, bin_pos; 355 | 293 register st_table_entry *ptr; 356 | 294 357 | 295 hash_val = do_hash(key, table); 358 | 296 FIND_ENTRY(table, ptr, hash_val, bin_pos); 359 | 297 360 | 298 if (ptr == 0) { 361 | 299 ADD_DIRECT(table, key, value, hash_val, bin_pos); 362 | 300 return 0; 363 | 301 } 364 | 302 else { 365 | 303 ptr->record = value; 366 | 304 return 1; 367 | 305 } 368 | 306 } 369 | 370 | (st.c) 371 | 372 | 它会检查元素是否已经在表中存在。只有它不存在时,才会添加。如果插入,返回0,否则,返回1。 373 | ID和符号 374 | 375 | 我已经讨论过什么是ID了。它将一个字符串对应为一个值,以它声明不同的名字。 实际的数据类型是unsigned int。 376 | 从char*到ID 377 | 378 | 字符串到ID的转换由rb_intern()完成。这个函数相当长,让我们省略掉中间的部分。 379 | 380 | ▼ rb_intern() (simplified) 381 | 382 | 5451 static st_table *sym_tbl; /* char* to ID */ 383 | 5452 static st_table *sym_rev_tbl; /* ID to char* */ 384 | 385 | 5469 ID 386 | 5470 rb_intern(name) 387 | 5471 const char *name; 388 | 5472 { 389 | 5473 const char *m = name; 390 | 5474 ID id; 391 | 5475 int last; 392 | 5476 393 | /* If for a name, there is a corresponding ID that is already 394 | registered, then return that ID */ 395 | 5477 if (st_lookup(sym_tbl, name, &id)) 396 | 5478 return id; 397 | 398 | /* omitted ... create a new ID */ 399 | 400 | /* register the name and ID relation */ 401 | 5538 id_regist: 402 | 5539 name = strdup(name); 403 | 5540 st_add_direct(sym_tbl, name, id); 404 | 5541 st_add_direct(sym_rev_tbl, id, name); 405 | 5542 return id; 406 | 5543 } 407 | 408 | (parse.y) 409 | 410 | 字符串和ID的对应关系由st_table完成。这里可能没有什么特别难的部分。 411 | 412 | 省略的部分做了些什么呢?它对全局变量名和实例变量名进行特殊处理,给它们设上了标记。 这是因为,在解析器中,根据ID了解变量的分类是必要的。然而,ID的基础部分与此无关, 因此,我就不在这里解释了。 413 | 从ID到char* 414 | 415 | rb_intern()的反向操作是rb_id2name(),它用一个ID产生一个char*。你或许已经知道, id2name中2是“to”。“To”和“Two”同音,因此用“2”表示“to”。这种用法很常见。 416 | 417 | 这个函数也要设置ID分类标志,因此它也很长,让我们简化一下。 418 | 419 | ▼ rb_id2name() (简化版) 420 | 421 | char * 422 | rb_id2name(id) 423 | ID id; 424 | { 425 | char *name; 426 | 427 | if (st_lookup(sym_rev_tbl, id, &name)) 428 | return name; 429 | return 0; 430 | } 431 | 432 | 或许它看上去有些过于简化,但事实上,如果我们去掉了细节,它真的就是这么简单。 433 | 434 | 我想强调的是,找到name之后,并不进行复制。ruby API并不需要(甚至禁止)对返回值free()。 传递参数后,总是要复制来用。换句话说,创建和释放都是由一方完成,用户或是ruby。 435 | 436 | 因此,当创建和释放无法在一个值上完成(传递而不返回时),就要使用Ruby对象。我还没有讨论过它。 当Ruby对象不再使用,即便我们不关心它,它也可以自动释放。 437 | VALUE和ID的转换 438 | 439 | 在Ruby的层次上,ID是一个Symbol类的实例。可以这样得到它:"string".intern。 String#intern的实现是rb_str_intern()。 440 | 441 | ▼ rb_str_intern() 442 | 443 | 2996 static VALUE 444 | 2997 rb_str_intern(str) 445 | 2998 VALUE str; 446 | 2999 { 447 | 3000 ID id; 448 | 3001 449 | 3002 if (!RSTRING(str)->ptr || RSTRING(str)->len == 0) { 450 | 3003 rb_raise(rb_eArgError, "interning empty string"); 451 | 3004 } 452 | 3005 if (strlen(RSTRING(str)->ptr) != RSTRING(str)->len) 453 | 3006 rb_raise(rb_eArgError, "string contains `\\0'"); 454 | 3007 id = rb_intern(RSTRING(str)->ptr); 455 | 3008 return ID2SYM(id); 456 | 3009 } 457 | 458 | (string.c) 459 | 460 | 作为ruby类库代码的样例,这个函数相当合理。注意使用RSTRING()转型的地方,这里访问了数据结构的成员。 461 | 462 | 我们来读读代码。首先,rb_raise()只是一个错误处理,我们暂时忽略它。这里有我们之前看过的rb_intern()。 ID2SYM() 是一个宏,它将ID转换为Symbol。 463 | 464 | 反向操作由Symbol#to_s完成,实现在sym_to_s中。 465 | 466 | ▼ sym_to_s() 467 | 468 | 522 static VALUE 469 | 523 sym_to_s(sym) 470 | 524 VALUE sym; 471 | 525 { 472 | 526 return rb_str_new2(rb_id2name(SYM2ID(sym))); 473 | 527 } 474 | 475 | (object.c) 476 | 477 | SYM2ID()是一个宏,它将Symbol(VALUE)转换为一个ID。 478 | 479 | 看上去,这个函数什么都没做。然而,可能需要注意一下内存处理的部分。`rb_id2name()`返回一个`char*`, 它不能用 free()释放。`rb_str_new2()`复制了参数的char*,使用的是它的拷贝。按照这种方式,如果采用一致的策略,就允许以链的方式编写函数。 480 | -------------------------------------------------------------------------------- /zh/06-variable.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 第六章:变量和常量 4 | --- 5 | 6 | ##本章提纲 7 | ---- 8 | 9 | ## Ruby的变量 10 | 11 | Ruby中有相当多不同类型的变量和常量。让我们列举一下,从范围最大的开始。 12 | 13 | - 全局变量 14 | - 常量 15 | - 类变量 16 | - 实例变量 17 | - 局部变量 18 | 19 | 实例变量已经在第2章《对象》中解释过了。在本章中,我们要讨论的是: 20 | 21 | - 全局变量 22 | - 类变量 23 | - 常量 24 | 25 | 部变量会在本书的第三部分讨论。 26 | 变量API 27 | 28 | 本章的分析目标是variable.c。我们先来看一下可用的API。 29 | 30 | VALUE rb_iv_get(VALUE obj, char *name) 31 | VALUE rb_ivar_get(VALUE obj, ID name) 32 | VALUE rb_iv_set(VALUE obj, char *name, VALUE val) 33 | VALUE rb_ivar_set(VALUE obj, ID name, VALUE val) 34 | 35 | 我们已经讨论过这些函数,但是必须再次提起它们,因为它们都在variable.c中。 当然,它们是用来访问实例变量的。 36 | 37 | VALUE rb_cv_get(VALUE klass, char *name) 38 | VALUE rb_cvar_get(VALUE klass, ID name) 39 | VALUE rb_cv_set(VALUE klass, char *name, VALUE val) 40 | VALUE rb_cvar_set(VALUE klass, ID name, VALUE val) 41 | 42 | 这些函数是访问类变量的API。类变量直接属于类,因此函数需要用一个类作为参数。 根据名字是以rb_Xv还是rb_Xvar开头,函数分为两组。差别在于变量“名”。 稍短名字的函数通常更易用,因为它们以char*为参数。稍长名字的参数更多在内部使用, 它们以ID为参数。 43 | 44 | VALUE rb_const_get(VALUE klass, ID name) 45 | VALUE rb_const_get_at(VALUE klass, ID name) 46 | VALUE rb_const_set(VALUE klass, ID name, VALUE val) 47 | 48 | 这些函数用于访问常量。常量属于类,因此它们要以类为为参数。 rb_const_get()会沿超类链查询,而rb_const_get_at()不会(它只查询klass)。 49 | 50 | struct global_entry *rb_global_entry(ID name) 51 | VALUE rb_gv_get(char *name) 52 | VALUE rb_gvar_get(struct global_entry *ent) 53 | VALUE rb_gv_set(char *name, VALUE val) 54 | VALUE rb_gvar_set(struct global_entry *ent, VALUE val) 55 | 56 | 最后的这些函数用以访问全局变量。因为使用struct global_entry了,它们显得与众不同。 我们会在描述实现的时候对此进行解释。 57 | 58 | ### 本章的要点 59 | 60 | 本章最重要的话题是“变量存在哪里以及如何存储?”,也就是,数据结构。 61 | 62 | 第二重要的在于如何搜索值。Ruby变量和常量的范围相当复杂, 因为变量和常量有时可以继承,有时可以在局部范围外看到……为了有个更好的理解, 你应该先从行为上猜测一下其如何实现,然后与真正的实现进行对比。 63 | 64 | ## 类变量 65 | ---- 66 | 67 | 类变量是属于类的变量。在Java或C++中,它们叫做静态变量。它们既可以从类中访问, 也可以从实例中访问。但是“从实例中”或“从类中”,只是求值器可用的信息。 我们此刻并不知道。因此,从C的层次上来看,它像是没有访问的范围。 我们只关注这些变量存储的方式。 68 | 读取 69 | 70 | 获取类变量的函数是rb_cvar_get()和rb_cv_get()。名字稍长的函数以ID为参数, 短一些的以char*为参数。因为以ID为参数看上去和内核部分更接近,我们来看一下。 71 | 72 | ▼ rb_cvar_get() 73 | 74 | 1508 VALUE 75 | 1509 rb_cvar_get(klass, id) 76 | 1510 VALUE klass; 77 | 1511 ID id; 78 | 1512 { 79 | 1513 VALUE value; 80 | 1514 VALUE tmp; 81 | 1515 82 | 1516 tmp = klass; 83 | 1517 while (tmp) { 84 | 1518 if (RCLASS(tmp)->iv_tbl) { 85 | 1519 if (st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { 86 | 1520 if (RTEST(ruby_verbose)) { 87 | 1521 cvar_override_check(id, tmp); 88 | 1522 } 89 | 1523 return value; 90 | 1524 } 91 | 1525 } 92 | 1526 tmp = RCLASS(tmp)->super; 93 | 1527 } 94 | 1528 95 | 1529 rb_name_error(id,"uninitialized class variable %s in %s", 96 | 1530 rb_id2name(id), rb_class2name(klass)); 97 | 1531 return Qnil; /* not reached */ 98 | 1532 } 99 | 100 | (variable.c) 101 | 102 | 这个函数读取klass的一个类变量。 103 | 104 | 正如我之前所说,像rb_raise()这样的错误处理函数可以简单的忽略。 这次出现的rb_name_error()是一个生成异常的函数,因此,基于同样的原因, 也可以忽略它。在ruby中,你可以假设所有以_error结尾的函数都会产生异常。 105 | 106 | 去除所有这些之后,我们看到的是,沿着klass的超类链,在iv_tbl中搜索。 在这里,你会说“什么?iv_tbl不是实例变量表吗?”事实上, 类变量就是存在实例变量表中。 107 | 108 | 我们可以这么做,因为创建ID时,考虑的是变量的全名,包括前缀: rb_intern()对“@var”和“@@var”返回的是不同的ID。 在Ruby的层次上,变量类型有前缀唯一确定,因此在Ruby中无法访问一个叫做@var的类变量。 109 | 110 | ## 常量 111 | ---- 112 | 113 | 有些唐突,但是我希望你能记住struct RClass的成员。如果我们排除basic, struct RClass包括: 114 | 115 | - VALUE super 116 | - struct st_table *iv_tbl 117 | - struct st_table *m_tbl 118 | 119 | 然后,考虑一下: 120 | 121 | - 常量属于类 122 | - 在struct RClass中,我们没有看到任何常量专用的表 123 | - 类变量和实例变量都在iv_tbl中 124 | 125 | 或许常量也…… 126 | 127 | ### 赋值 128 | 129 | rb_const_set()是一个为常量设置值的函数: 它将类klass的常量id设为值val。 130 | 131 | ▼ rb_const_set() 132 | 133 | 1377 void 134 | 1378 rb_const_set(klass, id, val) 135 | 1379 VALUE klass; 136 | 1380 ID id; 137 | 1381 VALUE val; 138 | 1382 { 139 | 1383 mod_av_set(klass, id, val, Qtrue); 140 | 1384 } 141 | 142 | (variable.c) 143 | 144 | mod_av_set()完成了所有艰苦的工作: 145 | 146 | ▼ mod_av_set() 147 | 148 | 1352 static void 149 | 1353 mod_av_set(klass, id, val, isconst) 150 | 1354 VALUE klass; 151 | 1355 ID id; 152 | 1356 VALUE val; 153 | 1357 int isconst; 154 | 1358 { 155 | 1359 char *dest = isconst ? "constant" : "class variable"; 156 | 1360 157 | 1361 if (!OBJ_TAINTED(klass) && rb_safe_level() >= 4) 158 | 1362 rb_raise(rb_eSecurityError, "Insecure: can't set %s", dest); 159 | 1363 if (OBJ_FROZEN(klass)) rb_error_frozen("class/module"); 160 | 1364 if (!RCLASS(klass)->iv_tbl) { 161 | 1365 RCLASS(klass)->iv_tbl = st_init_numtable(); 162 | 1366 } 163 | 1367 else if (isconst) { 164 | 1368 if (st_lookup(RCLASS(klass)->iv_tbl, id, 0) || 165 | 1369 (klass == rb_cObject && st_lookup(rb_class_tbl, id, 0))) { 166 | 1370 rb_warn("already initialized %s %s", dest, rb_id2name(id)); 167 | 1371 } 168 | 1372 } 169 | 1373 170 | 1374 st_insert(RCLASS(klass)->iv_tbl, id, val); 171 | 1375 } 172 | 173 | (variable.c) 174 | 175 | 这里你可以再次忽略警告检查(rb_raise(),rb_error_frozen()和rb_warn())。 这是剩下的部分: 176 | 177 | ▼ mod_av_set() (只有重要的部分) 178 | 179 | if (!RCLASS(klass)->iv_tbl) { 180 | RCLASS(klass)->iv_tbl = st_init_numtable(); 181 | } 182 | st_insert(RCLASS(klass)->iv_tbl, id, val); 183 | 184 | 我们现在可以确定,常量是在实例表中。这也就意味着,在struct RClass的iv_tbl中, 下面这些东西混在一起: 185 | 186 | 1. 类自己的实例变量 187 | 2. 类变量 188 | 3. 常量 189 | 190 | ### 读取 191 | 192 | 我们现在知道了常量如何存储。这回,我们把目光转向常量如何工作。 193 | rb_const_get() 194 | 195 | 我们现在来看一下rb_const_get(),一个读取常量的函数。 这个函数从klass中返回id所引用的常量。 196 | 197 | ▼ rb_const_get() 198 | 199 | 1156 VALUE 200 | 1157 rb_const_get(klass, id) 201 | 1158 VALUE klass; 202 | 1159 ID id; 203 | 1160 { 204 | 1161 VALUE value, tmp; 205 | 1162 int mod_retry = 0; 206 | 1163 207 | 1164 tmp = klass; 208 | 1165 retry: 209 | 1166 while (tmp) { 210 | 1167 if (RCLASS(tmp)->iv_tbl && 211 | st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { 212 | 1168 return value; 213 | 1169 } 214 | 1170 if (tmp == rb_cObject && top_const_get(id, &value)) 215 | return value; 216 | 1171 tmp = RCLASS(tmp)->super; 217 | 1172 } 218 | 1173 if (!mod_retry && BUILTIN_TYPE(klass) == T_MODULE) { 219 | 1174 mod_retry = 1; 220 | 1175 tmp = rb_cObject; 221 | 1176 goto retry; 222 | 1177 } 223 | 1178 224 | 1179 /* Uninitialized constant */ 225 | 1180 if (klass && klass != rb_cObject) { 226 | 1181 rb_name_error(id, "uninitialized constant %s at %s", 227 | 1182 rb_id2name(id), 228 | 1183 RSTRING(rb_class_path(klass))->ptr); 229 | 1184 } 230 | 1185 else { /* global_uninitialized */ 231 | 1186 rb_name_error(id, "uninitialized constant %s",rb_id2name(id)); 232 | 1187 } 233 | 1188 return Qnil; /* not reached */ 234 | 1189 } 235 | 236 | (variable.c) 237 | 238 | 一路上有许多代码。首先,我们至少该去除后半部分的rb_name_error()。在中间部分, mod_entry附近好像是对模块的特殊处理,我们也暂时去掉。函数化简为这样: 239 | 240 | ▼ rb_const_get (简化版) 241 | 242 | VALUE 243 | rb_const_get(klass, id) 244 | VALUE klass; 245 | ID id; 246 | { 247 | VALUE value, tmp; 248 | 249 | tmp = klass; 250 | while (tmp) { 251 | if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) { 252 | return value; 253 | } 254 | if (tmp == rb_cObject && top_const_get(id, &value)) return value; 255 | tmp = RCLASS(tmp)->super; 256 | } 257 | } 258 | 259 | 现在应该很容易看懂了。这个函数沿着klass的超类链在iv_tbl中搜索常量。这就意味着: 260 | {% highlight ruby %} 261 | class A 262 | Const = "ok" 263 | end 264 | class B < A 265 | p(Const) # 可以被访问 266 | end 267 | {% endhighlight %} 268 | 唯一剩下的问题就是top_const_get()。这个函数仅限于rb_cObject调用。 top一定是“顶层”。先和你确认一下,顶层的类是Object。 “在定义C的类语句中,类成为C”,这等同于“顶层的类就是Object”。 269 | {% highlight ruby %} 270 | # 顶层的类是`Object` 271 | class A 272 | # 类是A 273 | class B 274 | # 类是B 275 | end 276 | end 277 | {% endhighlight %} 278 | 因此,可以预期,top_const_get()是专门针对顶层进行的处理。 279 | top_const_get() 280 | 281 | 我们来看看这个top_const_get函数。它查询id,把值写入klassp,然后返回。 282 | 283 | ▼ top_const_get() 284 | 285 | 1102 static int 286 | 1103 top_const_get(id, klassp) 287 | 1104 ID id; 288 | 1105 VALUE *klassp; 289 | 1106 { 290 | 1107 /* pre-defined class */ 291 | 1108 if (st_lookup(rb_class_tbl, id, klassp)) return Qtrue; 292 | 1109 293 | 1110 /* autoload */ 294 | 1111 if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { 295 | 1112 rb_autoload_load(id); 296 | 1113 *klassp = rb_const_get(rb_cObject, id); 297 | 1114 return Qtrue; 298 | 1115 } 299 | 1116 return Qfalse; 300 | 1117 } 301 | 302 | (variable.c) 303 | 304 | rb_class_tbl在第四章《类与模块》中已经提到过了。它是一个表,用来存储已经定义的顶层类。 比如内建类String或Array都登记在其中。因此,搜索顶层变量时,不要忘了这个表。 305 | 306 | 下一块同自动加载相关。这允许我们在第一次访问顶层常量时自动加载一个库。 比如这样使用: 307 | 308 | > autoload(:VeryBigClass, "verybigclass") # VeryBigClass定义在其中 309 | 310 | 之后,第一次访问VeryBigClass时,就会加载verybigclass(以require方式)。 只要VeryBigClass定义在库中,就可以顺利的执行。当库特别大,需要很长时间加载时, 这种方式就显得很高效了。 311 | 312 | 这种自动加载由rb_autoload_xxxx()完成。我们就不在本章进一步讨论自动加载了, 因为很快它的工作方式就会发生巨大的改变(译注:自动加载确实在1.8中发生了改变: 自动加载的常量不再需要定义在顶层了)。 313 | 314 | ### 其它的类? 315 | 316 | 对了,查询其它类常量的代码到底在哪?毕竟,常量的查询应该先从外面的类开始, 然后才是超类。 317 | 318 | 实际上,对此,我们还没有足够的知识。外部类的改变依赖于在程序中的位置。 总之,它与程序上下文相关。因此,我们先来理解求值器的内部状态。特别是, eval.c中的ev_const_get()函数,它完成在其它类中的搜索。 常量故事的结局会在本书的第三部分中出现。 319 | 320 | ## 全局变量 321 | ---- 322 | 323 | ### 总论 324 | 325 | 全局变量可以在任何地方访问。反过来说,没有必要限制对它们的访问。因为它们与上下文无关, 有个地方用来存放表就可以,没有必要做任何检查。因此,实现非常简单。 326 | 327 | 尽管如此,仍有一大堆代码。原因是全局变量与普通变量有很大差异。下面的功能只用于全局变量。 328 | 329 | - 你可以对全局变量的访问设置钩子 330 | - 你可以用alias为它们起个别名 331 | 332 | 我们来简单解释一下。 333 | 334 | #### 变量的别名 335 | 336 | > alias $newname $oldname 337 | 338 | 随后,你就可以用$newname代替$oldname。 339 | 340 | 变量的alias主要是为了“符号变量”而准备的。“符号变量”是一种源自Perl的变量, 比如$=或$0。$=用以确定字符串比较是是否区分大小写。$0表示Ruby主程序的名字。 还有一些其它的符号变量,但它们的名字只有一个字符长,对于不了解Perl的人来说, 它们难于记忆。因此才创建了别名,让它们好记一点。 341 | 342 | 现在不推荐使用符号变量,它们也一个个的移到适当模块的singleton方法中。 现在的氛围是2.0会废弃$=和其它符号变量。 343 | 344 | #### 钩子 345 | 346 | 你可以对全局变量的读写设置“钩子”。 347 | 348 | 钩子可以在Ruby的层次上设置,但是我一直在想:为什么不在C层次上看看系统用的特殊变量, 比如$KCODE?$KCODE是一个包含了编码信息的变量,解释器用它来处理字符串。 它只能设置为特殊的值,比如"EUC"或"UTF8"。但是这很讨厌,因此,也可以设为"e"或"u"。 349 | 350 | p($KCODE) # "NONE" (缺省) 351 | $KCODE = "e" 352 | p($KCODE) # "EUC" 353 | $KCODE = "u" 354 | p($KCODE) # "UTF8" 355 | 356 | 我想如果能够对全局变量的赋值设置钩子,就能更好的理解这里的所做的。顺便说一下, $KCODE的K来自“kanji”(日语中中文字符的名字)。 357 | 358 | 你也许会说,即便有了alias或是钩子,全局变量也不要用得那么多,所以, 它的功能没多大关系。不用的函数就让它适当的结束,我还需要篇幅分析解析器和求值器。 因此,下面的讨论中我抛弃一些不重要的东西。 359 | 360 | ### 数据结构 361 | 362 | 看变量结构的时候,我说过,它们的存储方式很重要。希望你可以好好掌握全局变量的结构。 363 | ▼ 全局变量的数据结构 364 | 365 | 21 static st_table *rb_global_tbl; 366 | 367 | 334 struct global_entry { 368 | 335 struct global_variable *var; 369 | 336 ID id; 370 | 337 }; 371 | 372 | 324 struct global_variable { 373 | 325 int counter; /* 引用计数 */ 374 | 326 void *data; /* 变量值 */ 375 | 327 VALUE (*getter)(); /* 取值函数 */ 376 | 328 void (*setter)(); /* 设置函数 */ 377 | 329 void (*marker)(); /* 标记函数 */ 378 | 330 int block_trace; 379 | 331 struct trace_var *trace; 380 | 332 }; 381 | 382 | (variable.c) 383 | 384 | `rb_global_tbl`是主要的表。所有的全局变量都存在这个表里。这个表的键值当然就是变量名(ID)。 值由`struct global_entry和struct global_variable`表示。(图1) 385 |  386 | 图1: 执行时的全局变量表 387 | 388 | 表示变量的结构分为两个部分,以便创建别名。如果创建了别名, 两个global_entry会指向同一个struct global_variable。 389 | 390 | 这里,引用计数器(struct global_variable的counter)是必需的。 我在前一章《垃圾回收》中解释过引用计数的概念。简单回顾一下,生成新的引用时, 计数器加1。引用不再使用时,计数器减1。计数器为0时,结构不再使用,调用free()。 391 | 392 | 如果在Ruby的层次上设置了钩子,就有一个struct trace_var列表存在struct global_variable 的trace成员中,这个就不说了,省略struct trace_var。 393 | 394 | ### 读取 395 | 396 | 只要看一下如何读取全局变量,就可以对全局变量有个大概的认识。 读取的函数是rb_gv_get()和rb_gvar_get()。 397 | ▼ rb_gv_get() rb_gvar_get() 398 | 399 | 716 VALUE 400 | 717 rb_gv_get(name) 401 | 718 const char *name; 402 | 719 { 403 | 720 struct global_entry *entry; 404 | 721 405 | 722 entry = rb_global_entry(global_id(name)); 406 | 723 return rb_gvar_get(entry); 407 | 724 } 408 | 409 | 649 VALUE 410 | 650 rb_gvar_get(entry) 411 | 651 struct global_entry *entry; 412 | 652 { 413 | 653 struct global_variable *var = entry->var; 414 | 654 return (*var->getter)(entry->id, var->data, var); 415 | 655 } 416 | 417 | (variable.c) 418 | 419 | 实际的内容都在`rb_global_entry()`中,但这并不妨碍我们理解。 global_id函数将`char*`转成ID,并检查它是否是全局变量的ID。 (*var->getter)(...)当然就是使用函数指针var->getter进行的函数调用。 如果p是一个函数指针,(*p)(arg)就在调用函数。 420 | 421 | 剩下的主要部分还是rb_global_entry()。 422 | 423 | ▼ rb_global_entry() 424 | 425 | 351 struct global_entry* 426 | 352 rb_global_entry(id) 427 | 353 ID id; 428 | 354 { 429 | 355 struct global_entry *entry; 430 | 356 431 | 357 if (!st_lookup(rb_global_tbl, id, &entry)) { 432 | 358 struct global_variable *var; 433 | 359 entry = ALLOC(struct global_entry); 434 | 360 st_add_direct(rb_global_tbl, id, entry); 435 | 361 var = ALLOC(struct global_variable); 436 | 362 entry->id = id; 437 | 363 entry->var = var; 438 | 364 var->counter = 1; 439 | 365 var->data = 0; 440 | 366 var->getter = undef_getter; 441 | 367 var->setter = undef_setter; 442 | 368 var->marker = undef_marker; 443 | 369 444 | 370 var->block_trace = 0; 445 | 371 var->trace = 0; 446 | 372 } 447 | 373 return entry; 448 | 374 } 449 | 450 | (variable.c) 451 | 452 | 主要处理由开始的`st_lookup()`完成。随后只是创建(和存储)了一个新项。 访问的项不存在时,便自动创建一项,rb_global_entry()不会返回NULL。 453 | 454 | 这主要是考虑速度。解析器找到一个全局变量时,它得到是相应的`struct global_entry`。 读取变量值时,解析器就是取该项的值(用rb_gv_get()),不必做任何检查。 455 | 456 | 让我们稍微前进一步,接下来的代码,var->getter等几个设为undef_xxxx。 对于当前未定义的全局变量,其setter/getter/marker用undef表示。 457 | 458 | 即便读取未定义的全局变量,`undef_getter()`也只是显示一个警告,返回nil。 undef_setter()则稍微有趣一些,来看一下。 459 | 460 | ▼ undef_setter() 461 | 462 | 385 static void 463 | 386 undef_setter(val, id, data, var) 464 | 387 VALUE val; 465 | 388 ID id; 466 | 389 void *data; 467 | 390 struct global_variable *var; 468 | 391 { 469 | 392 var->getter = val_getter; 470 | 393 var->setter = val_setter; 471 | 394 var->marker = val_marker; 472 | 395 473 | 396 var->data = (void*)val; 474 | 397 } 475 | 476 | (variable.c) 477 | 478 | val_getter()得到entry->data的值,返回它。val_getter()只是把值放到entry->data中。 这样设置处理器使我们不必对为定义变量进行特殊处理(图2)。很有技巧,不是吗? 479 | 480 |  481 | 482 | 图2: 全局变量的设置和访问 483 | -------------------------------------------------------------------------------- /en/fin.textile: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | --- 4 | 5 | h1. Final Chapter: Ruby's future 6 | 7 | h2. Issues to be addressed 8 | 9 | @ruby@ isn't 'completely finished' software. It's still being developed, 10 | there are still a lot of issues. Firstly, we want to try removing 11 | inherent problems in the current interpreter. 12 | 13 | The order of the topics is mostly in the same order as the chapters of 14 | this book. 15 | 16 | 17 | h3. Performance of GC 18 | 19 | The performance of the current GC might be 20 | "not notably bad, but not notably good". 21 | "not notably bad" means "it won't cause troubles in our daily life", 22 | and "not notably good" means "its downside will be exposed under heavy load". 23 | For example, if it is an application which creates plenty of objects and keeps 24 | holding them, its speed would slow down radically. 25 | Every time doing GC, it needs to mark all of the objects, 26 | and furthermore it would becomes to need to invoke GC more often 27 | because it can't collect them. 28 | To counter this problem, Generational GC, which was mentioned in Chapter 5, 29 | must be effective. (At least, it is said so in theory.) 30 | 31 | Also regarding its response speed, 32 | there are still rooms we can improve. 33 | With the current GC, while it is running, the entire interpretor stops. 34 | Thus, when the program is an editor or a GUI application, 35 | sometimes it freezes and stops to react. 36 | Even if it's just 0.1 second, 37 | stopping when typing characters would give a very bad impression. 38 | Currently, there are few such applications created or, 39 | even if exists, its size might be enough small not to expose this problem. 40 | However, if such application will actually be created in the future, 41 | there might be the necessity to consider Incremental GC. 42 | 43 | 44 | h3. Implementation of parser 45 | 46 | As we saw in Part 2, the implementation of @ruby@ parser has already utilized 47 | @yacc@'s ability to almost its limit, thus I can't think it can endure further 48 | expansions. It's all right if there's nothing planned to expand, 49 | but a big name "keyword argument" is planned next 50 | and it's sad if we could not express another demanded grammar because of the 51 | limitation of @yacc@. 52 | 53 | 54 | h3. Reuse of parser 55 | 56 | Ruby's parser is very complex. In particular, dealing with around @lex_state@ 57 | seriously is very hard. Due to this, embedding a Ruby program or creating a 58 | program to deal with a Ruby program itself is quite difficult. 59 | 60 | For example, I'm developing a tool named @racc@, 61 | which is prefixed with R because it is a Ruby-version @yacc@. 62 | With @racc@, the syntax of grammar files are almost the same as @yacc@ 63 | but we can write actions in Ruby. 64 | To do so, it could not determine the end of an action without parsing Ruby code 65 | properly, but "properly" is very difficult. Since there's no other choice, 66 | currently I've compromised at the level that it can parse "almost all". 67 | 68 | As another example which requires analyzing Ruby program, 69 | I can enumerate some tools like @indent@ and @lint@, 70 | but creating such tool also requires a lot efforts. 71 | It would be desperate if it is something complex like a refactoring tool. 72 | 73 | Then, what can we do? If we can't recreate the same thing, 74 | what if @ruby@'s original parser can be used as a component? 75 | In other words, making the parser itself a library. 76 | This is a feature we want by all means. 77 | 78 | However, what becomes problem here is, as long as @yacc@ is used, 79 | we cannot make parser reentrant. 80 | It means, say, we cannot call @yyparse()@ recursively, 81 | and we cannot call it from multiple threads. 82 | Therefore, it should be implemented in the way of not returning control to Ruby 83 | while parsing. 84 | 85 | 86 | 87 | h3. Hiding Code 88 | 89 | With current @ruby@, it does not work without the source code of the program to 90 | run. Thus, people who don't want others to read their source code might have 91 | trouble. 92 | 93 | 94 | h3. Interpretor Object 95 | 96 | Currently each process cannot have multiple @ruby@ interpretors, 97 | this was discussed in Chapter 13. 98 | If having multiple interpretors is practically possible, it seems better, 99 | but is it possible to implement such thing? 100 | 101 | 102 | h3. The structure of evaluator 103 | 104 | Current @eval.c@ is, above all, too complex. 105 | Embedding Ruby's stack frames to machine stack could occasionally become the 106 | source of trouble, using @setjmp() longjmp()@ aggressively makes it less easy to 107 | understand and slows down its speed. 108 | Particularly with RISC machine, which has many registers, using @setjmp()@ 109 | aggressively can easily cause slowing down because @setjmp()@ set aside all 110 | things in registers. 111 | 112 | 113 | h3. The performance of evaluator 114 | 115 | @ruby@ is already enough fast for ordinary use. 116 | But aside from it, regarding a language processor, 117 | definitely the faster is the better. 118 | To achieve better performance, in other words to optimize, 119 | what can we do? 120 | In such case, the first thing we have to do is profiling. 121 | So I profiled. 122 | 123 |124 | % cumulative self self total 125 | time seconds seconds calls ms/call ms/call name 126 | 20.25 1.64 1.64 2638359 0.00 0.00 rb_eval 127 | 12.47 2.65 1.01 1113947 0.00 0.00 ruby_re_match 128 | 8.89 3.37 0.72 5519249 0.00 0.00 rb_call0 129 | 6.54 3.90 0.53 2156387 0.00 0.00 st_lookup 130 | 6.30 4.41 0.51 1599096 0.00 0.00 rb_yield_0 131 | 5.43 4.85 0.44 5519249 0.00 0.00 rb_call 132 | 5.19 5.27 0.42 388066 0.00 0.00 st_foreach 133 | 3.46 5.55 0.28 8605866 0.00 0.00 rb_gc_mark 134 | 2.22 5.73 0.18 3819588 0.00 0.00 call_cfunc 135 |136 | 137 | This is a profile when running some application but 138 | this is approximately the profile of a general Ruby program. 139 | @rb_eval()@ appeared in the overwhelming percentage being at the top, 140 | after that, in addition to functions of GC, evaluator core, 141 | functions that are specific to the program are mixed. 142 | For example, in the case of this application, 143 | it takes a lot of time for regular expression match (@ruby_re_match@). 144 | 145 | However, even if we understood this, the question is how to improve it. 146 | To think simply, it can be archived by making @rb_eval()@ faster. 147 | That said, but as for @ruby@ core, there are almost not any room which can be 148 | easily optimized. For instance, apparently "tail recursive -> @goto@ conversion" 149 | used in the place of @NODE_IF@ and others has already applied almost all 150 | possible places it can be applied. 151 | In other words, without changing the way of thinking fundamentally, 152 | there's no room to improve. 153 | 154 | 155 | h3. The implementation of thread 156 | 157 | This was also discussed in Chapter 19. There are really a lot of issues about 158 | the implementation of the current ruby's thread. Particularly, it cannot mix 159 | with native threads so badly. The two great advantages of @ruby@'s thread, 160 | (1) high portability (2) the same behavior everywhere, 161 | are definitely incomparable, but probably that implementation is something we 162 | cannot continue to use eternally, isn't it? 163 | 164 | 165 | 166 | 167 | h2. `ruby` 2 168 | 169 | Subsequently, on the other hand, I'll introduce the trend of the original `ruby`, 170 | how it is trying to counter these issues. 171 | 172 | 173 | h3. Rite 174 | 175 | At the present time, ruby's edge is 1.6.7 as the stable version and 1.7.3 as the 176 | development version, but perhaps the next stable version 1.8 will come out in 177 | the near future. Then at that point, the next development version 1.9.0 will 178 | start at the same time. And after that, this is a little irregular but 1.9.1 179 | will be the next stable version. 180 | 181 | |_. stable |_. development |_. when to start | 182 | | 1.6.x | 1.7.x | 1.6.0 was released on 2000-09-19 | 183 | | 1.8.x | 1.9.x | probably it will come out within 6 months | 184 | | 1.9.1~ | 2.0.0 | maybe about 2 years later | 185 | 186 | 187 | And the next-to-next generational development version is `ruby` 2, whose code 188 | name is Rite. Apparently this name indicates a respect for the inadequacy that 189 | Japanese cannot distinguish the sounds of L and R. 190 | 191 | What will be changed in 2.0 is, in short, almost all the entire core. 192 | Thread, evaluator, parser, all of them will be changed. 193 | However, nothing has been written as a code yet, so things written here is 194 | entirely just a "plan". If you expect so much, it's possible it will turn out 195 | disappointments. Therefore, for now, let's just expect slightly. 196 | 197 | 198 | h3. The language to write 199 | 200 | Firstly, the language to use. Definitely it will be C. Mr. Matsumoto said to 201 | `ruby-talk`, which is the English mailing list for Ruby, 202 | 203 |204 | I hate C++. 205 |206 | 207 | So, C++ is most unlikely. Even if all the parts will be recreated, 208 | it is reasonable that the object system will remain almost the same, 209 | so not to increase extra efforts around this is necessary. 210 | However, chances are good that it will be ANSI C next time. 211 | 212 | 213 | h3. GC 214 | 215 | Regarding the implementation of GC, 216 | the good start point would be 217 | `Boehm GC`\footnote{Boehm GC `http://www.hpl.hp.com/personal/Hans_Boehm/gc`}. 218 | Bohem GC is a conservative and incremental and generational GC, 219 | furthermore, it can mark all stack spaces of all threads even while native 220 | threads are running. It's really an impressive GC. 221 | Even if it is introduced once, it's hard to tell whether it will be used 222 | perpetually, but anyway it will proceed for the direction to which we can expect 223 | somewhat improvement on speed. 224 | 225 | 226 | h3. Parser 227 | 228 | Regarding the specification, it's very likely that the nested method calls 229 | without parentheses will be forbidden. As we've seen, `command_call` has a great 230 | influence on all over the grammar. If this is simplified, both the parser and 231 | the scanner will also be simplified a lot. 232 | However, the ability to omit parentheses itself will never be disabled. 233 | 234 | And regarding its implementation, whether we continue to use `yacc` is still 235 | under discussion. If we won't use, it would mean hand-writing, but is it 236 | possible to implement such complex thing by hand? Such anxiety might left. 237 | Whichever way we choose, the path must be thorny. 238 | 239 | 240 | h3. Evaluator 241 | 242 | The evaluator will be completely recreated. 243 | Its aims are mainly to improve speed and to simplify the implementation. 244 | There are two main viewpoints: 245 | 246 | 247 | * remove recursive calls like `rb_eval()` 248 | * switch to a bytecode interpretor 249 | 250 | First, removing recursive calls of `rb_eval()`. The way to remove is, 251 | maybe the most intuitive explanation is that it's like the "tail recursive -> 252 | `goto` conversion". Inside a single `rb_eval()`, circling around by using 253 | `goto`. That decreases the number of function calls and removes the necessity of 254 | `setjmp()` that is used for `return` or `break`. 255 | However, when a function defined in C is called, calling a function is 256 | inevitable, and at that point `setjmp()` will still be required. 257 | 258 | 259 | Bytecode is, in short, something like a program written in machine language. 260 | It became famous because of the virtual machine of Smalltalk90, 261 | it is called bytecode because each instruction is one-byte. 262 | For those who are usually working at more abstract level, byte would seem 263 | so natural basis in size to deal with, 264 | but in many cases each instruction consists of bits in machine languages. 265 | For example, in Alpha, among a 32-bit instruction code, the beginning 6-bit 266 | represents the instruction type. 267 | 268 | 269 | The advantage of bytecode interpretors is mainly for speed. There are two 270 | reasons: Firstly, unlike syntax trees, there's no need to traverse pointers. 271 | Secondly, it's easy to do peephole optimization. 272 | 273 | 274 | And in the case when bytecode is saved and read in later, 275 | because there's no need to parse, we can naturally expect better performance. 276 | However, parsing is a procedure which is done only once at the beginning of a 277 | program and even currently it does not take so much time. Therefore, its 278 | influence will not be so much. 279 | 280 | 281 | If you'd like to know about how the bytecode evaluator could be, 282 | `regex.c` is worth to look at. 283 | For another example, Python is a bytecode interpretor. 284 | 285 | 286 | 287 | 288 | h3. Thread 289 | 290 | Regarding thread, the thing is native thread support. 291 | The environment around thread has been significantly improved, 292 | comparing with the situation in 1994, the year of Ruby's birth. 293 | So it might be judged that 294 | we can get along with native thread now. 295 | 296 | 297 | Using native thread means being preemptive also at C level, 298 | thus the interpretor itself must be multi-thread safe, 299 | but it seems this point is going to be solved by using a global lock 300 | for the time being. 301 | 302 | 303 | Additionally, that somewhat arcane "continuation", it seems likely to be removed. 304 | `ruby`'s continuation highly depends on the implementation of thread, 305 | so naturally it will disappear if thread is switched to native thread. 306 | The existence of that feature is because "it can be implemented" 307 | and it is rarely actually used. Therefore there might be no problem. 308 | 309 | 310 | 311 | h3. M17N 312 | 313 | In addition, I'd like to mention a few things about class libraries. 314 | This is about multi-lingualization (M17N for short). 315 | What it means exactly in the context of programming is 316 | being able to deal with multiple character encodings. 317 | 318 | 319 | `ruby` with Multi-lingualization support has already implemented and you can 320 | obtain it from the `ruby_m17m` branch of the CVS repository. 321 | It is not absorbed yet because it is judged that its specification is immature. 322 | If good interfaces is designed, 323 | it will be absorbed at some point in the middle of 1.9. 324 | 325 | 326 | 327 | 328 | h3. IO 329 | 330 | 331 | The `IO` class in current Ruby is a simple wrapper of `stdio`, 332 | but in this approach, 333 | 334 | * there are too many but slight differences between various platforms. 335 | * we'd like to have finer control on buffers. 336 | 337 | these two points cause complaints. 338 | Therefore, it seems Rite will have its own `stdio`. 339 | 340 | 341 | 342 | 343 | 344 | 345 | h2. Ruby Hacking Guide 346 | 347 | 348 | So far, we've always acted as observers who look at `ruby` from outside. 349 | But, of course, `ruby` is not a product which displayed in in a showcase. 350 | It means we can influence it if we take an action for it. 351 | In the last section of this book, 352 | I'll introduce the suggestions and activities for `ruby` from community, 353 | as a farewell gift for Ruby Hackers both at present and in the future. 354 | 355 | 356 | h3. Generational GC 357 | 358 | First, as also mentioned in Chapter 5, 359 | the generational GC made by Mr. Kiyama Masato. 360 | As described before, with the current patch, 361 | 362 | * it is less fast than expected. 363 | * it needs to be updated to fit the edge `ruby` 364 | 365 | these points are problems, but here I'd like to highly value it because, 366 | more than anything else, it was the first large non-official patch. 367 | 368 | 369 | 370 | 371 | h3. Oniguruma 372 | 373 | The regular expression engine used by current Ruby is a remodeled version of GNU 374 | regex. That GNU regex was in the first place written for Emacs. And then it was 375 | remodeled so that it can support multi-byte characters. And then Mr. Matsumoto 376 | remodeled so that it is compatible with Perl. 377 | As we can easily imagine from this history, 378 | its construction is really intricate and spooky. 379 | Furthermore, due to the LPGL license of this GNU regex, 380 | the license of `ruby` is very complicated, 381 | so replacing this engine has been an issue from a long time ago. 382 | 383 | What suddenly emerged here is the regular expression engine "Oniguruma" by 384 | Mr. K. Kosako. I heard this is written really well, it is likely being 385 | absorbed as soon as possible. 386 | 387 | You can obtain Oniguruma from the `ruby`'s CVS repository in the following way. 388 | 389 |390 | % cvs -d :pserver:anonymous@cvs.ruby-lang.org:/src co oniguruma 391 |392 | 393 | 394 | 395 | h3. ripper 396 | 397 | Next, ripper is my product. It is an extension library made by remodeling 398 | `parse.y`. It is not a change applied to the `ruby`'s main body, but I 399 | introduced it here as one possible direction to make the parser a component. 400 | 401 | It is implemented with kind of streaming interface and 402 | it can pick up things such as token scan or parser's reduction as events. 403 | It is put in the attached CD-ROM 404 | \footnote{ripper:`archives/ripper-0.0.5.tar.gz` of the attached CD-ROM}, 405 | so I'd like you to give it a try. 406 | Note that the supported grammar is a little different from the current one 407 | because this version is based on `ruby` 1.7 almost half-year ago. 408 | 409 | I created this just because "I happened to come up with this idea", 410 | if this is accounted, I think it is constructed well. 411 | It took only three days or so to implement, really just a piece of cake. 412 | 413 | 414 | h3. A parser alternative 415 | 416 | This product has not yet appeared in a clear form, 417 | there's a person who write a Ruby parser in C++ which can be used totally 418 | independent of `ruby`. (`[ruby-talk:50497]`). 419 | 420 | 421 | h3. JRuby 422 | 423 | More aggressively, there's an attempt to rewrite entire the interpretor. 424 | For example, a Ruby written in Java, 425 | Ruby\footnote{JRuby `http://jruby.sourceforge.net`}, 426 | has appeared. 427 | It seems it is being implemented by a large group of people, 428 | Mr. Jan Arne Petersen and many others. 429 | 430 | 431 | I tried it a little and as my reviews, 432 | 433 | * the parser is written really well. It does precisely handle even finer 434 | behaviors such as spaces or here document. 435 | * `instance_eval` seems not in effect (probably it couldn't be helped). 436 | * it has just a few built-in libraries yet (couldn't be helped as well). 437 | * we can't use extension libraries with it (naturally). 438 | * because Ruby's UNIX centric is all cut out, 439 | there's little possibility that we can run already-existing scripts without 440 | any change. 441 | * slow 442 | 443 | perhaps I could say at least these things. 444 | Regarding the last one "slow", its degree is, 445 | the execution time it takes is 20 times longer than the one of the original 446 | `ruby`. Going this far is too slow. 447 | It is not expected running fast because that Ruby VM runs on Java VM. 448 | Waiting for the machine to become 20 times faster seems only way. 449 | 450 | 451 | However, the overall impression I got was, it's way better than I imagined. 452 | 453 | 454 | 455 | h3. NETRuby 456 | 457 | If it can run with Java, it should also with C#. 458 | Therefore, a Ruby written in C# appeared, 459 | "NETRuby\footnote{NETRuby `http://sourceforge.jp/projects/netruby/`}". 460 | The author is Mr. arton. 461 | 462 | Because I don't have any .NET environment at hand, 463 | I checked only the source code, 464 | but according to the author, 465 | 466 | * more than anything, it's slow 467 | * it has a few class libraries 468 | * the compatibility of exception handling is not good 469 | 470 | such things are the problems. 471 | But `instance_eval` is in effect (astounding!). 472 | 473 | 474 | h3. How to join `ruby` development 475 | 476 | `ruby`'s developer is really Mr. Matsumoto as an individual, 477 | regarding the final decision about the direction `ruby` will take, 478 | he has the definitive authority. 479 | But at the same time, `ruby` is an open source software, 480 | anyone can join the development. 481 | Joining means, you can suggest your opinions or send patches. 482 | The below is to concretely tell you how to join. 483 | 484 | In `ruby`'s case, the mailing list is at the center of the development, 485 | so it's good to join the mailing list. 486 | The mailing lists currently at the center of the community are three: 487 | `ruby-list`, `ruby-dev`, `ruby-talk`. 488 | `ruby-list` is a mailing list for "anything relating to Ruby" in Japanese. 489 | `ruby-dev` is for the development version `ruby`, this is also in Japanese. 490 | `ruby-talk` is an English mailing list. 491 | The way to join is shown on the page "mailing lists" at Ruby's official site 492 | \footnote{Ruby's official site: `http://www.ruby-lang.org/ja/`}. 493 | For these mailing lists, read-only people are also welcome, 494 | so I recommend just joining first and watching discussions 495 | to grasp how it is. 496 | 497 | Though Ruby's activity started in Japan, 498 | recently sometimes it is said "the main authority now belongs to `ruby-talk`". 499 | But the center of the development is still `ruby-dev`. 500 | Because people who has the commit right to `ruby` (e.g. core members) are mostly 501 | Japanese, the difficulty and reluctance of using English 502 | naturally lead them to `ruby-dev`. 503 | If there will be more core members who prefer to use English, 504 | the situation could be changed, 505 | but meanwhile the core of `ruby`'s development might remain `ruby-dev`. 506 | 507 | However, it's bad if people who cannot speak Japanese cannot join the 508 | development, so currently the summary of `ruby-dev` is translated once a week 509 | and posted to `ruby-talk`. 510 | I also help that summarising, but only three people do it in turn now, 511 | so the situation is really harsh. 512 | The members to help summarize is always in demand. 513 | If you think you're the person who can help, 514 | I'd like you to state it at `ruby-list`. 515 | 516 | And as the last note, 517 | only its source code is not enough for a software. 518 | It's necessary to prepare various documents and maintain web sites. 519 | And people who take care of these kind of things are always in short. 520 | There's also a mailing list for the document-related activities, 521 | but as the first step you just have to propose "I'd like to do something" to `ruby-list`. 522 | I'll answer it as much as possible, 523 | and other people would respond to it, too. 524 | 525 | 526 | h3. Finale 527 | 528 | The long journey of this book is going to end now. 529 | As there was the limitation of the number of pages, 530 | explaining all of the parts comprehensively was impossible, 531 | however I told everything I could tell about the `ruby`'s core. 532 | I won't add extra things any more here. 533 | If you still have things you didn't understand, 534 | I'd like you to investigate it by reading the source code by yourself as much as 535 | you want. 536 | -------------------------------------------------------------------------------- /zh/20-fin.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | h1. Final Chapter: Ruby's future 6 | 7 | h2. Issues to be addressed 8 | 9 | @ruby@ isn't 'completely finished' software. It's still being developed, 10 | there are still a lot of issues. Firstly, we want to try removing 11 | inherent problems in the current interpreter. 12 | 13 | The order of the topics is mostly in the same order as the chapters of 14 | this book. 15 | 16 | 17 | h3. Performance of GC 18 | 19 | The performance of the current GC might be 20 | "not notably bad, but not notably good". 21 | "not notably bad" means "it won't cause troubles in our daily life", 22 | and "not notably good" means "its downside will be exposed under heavy load". 23 | For example, if it is an application which creates plenty of objects and keeps 24 | holding them, its speed would slow down radically. 25 | Every time doing GC, it needs to mark all of the objects, 26 | and furthermore it would becomes to need to invoke GC more often 27 | because it can't collect them. 28 | To counter this problem, Generational GC, which was mentioned in Chapter 5, 29 | must be effective. (At least, it is said so in theory.) 30 | 31 | Also regarding its response speed, 32 | there are still rooms we can improve. 33 | With the current GC, while it is running, the entire interpretor stops. 34 | Thus, when the program is an editor or a GUI application, 35 | sometimes it freezes and stops to react. 36 | Even if it's just 0.1 second, 37 | stopping when typing characters would give a very bad impression. 38 | Currently, there are few such applications created or, 39 | even if exists, its size might be enough small not to expose this problem. 40 | However, if such application will actually be created in the future, 41 | there might be the necessity to consider Incremental GC. 42 | 43 | 44 | h3. Implementation of parser 45 | 46 | As we saw in Part 2, the implementation of @ruby@ parser has already utilized 47 | @yacc@'s ability to almost its limit, thus I can't think it can endure further 48 | expansions. It's all right if there's nothing planned to expand, 49 | but a big name "keyword argument" is planned next 50 | and it's sad if we could not express another demanded grammar because of the 51 | limitation of @yacc@. 52 | 53 | 54 | h3. Reuse of parser 55 | 56 | Ruby's parser is very complex. In particular, dealing with around @lex_state@ 57 | seriously is very hard. Due to this, embedding a Ruby program or creating a 58 | program to deal with a Ruby program itself is quite difficult. 59 | 60 | For example, I'm developing a tool named @racc@, 61 | which is prefixed with R because it is a Ruby-version @yacc@. 62 | With @racc@, the syntax of grammar files are almost the same as @yacc@ 63 | but we can write actions in Ruby. 64 | To do so, it could not determine the end of an action without parsing Ruby code 65 | properly, but "properly" is very difficult. Since there's no other choice, 66 | currently I've compromised at the level that it can parse "almost all". 67 | 68 | As another example which requires analyzing Ruby program, 69 | I can enumerate some tools like @indent@ and @lint@, 70 | but creating such tool also requires a lot efforts. 71 | It would be desperate if it is something complex like a refactoring tool. 72 | 73 | Then, what can we do? If we can't recreate the same thing, 74 | what if @ruby@'s original parser can be used as a component? 75 | In other words, making the parser itself a library. 76 | This is a feature we want by all means. 77 | 78 | However, what becomes problem here is, as long as @yacc@ is used, 79 | we cannot make parser reentrant. 80 | It means, say, we cannot call @yyparse()@ recursively, 81 | and we cannot call it from multiple threads. 82 | Therefore, it should be implemented in the way of not returning control to Ruby 83 | while parsing. 84 | 85 | 86 | 87 | h3. Hiding Code 88 | 89 | With current @ruby@, it does not work without the source code of the program to 90 | run. Thus, people who don't want others to read their source code might have 91 | trouble. 92 | 93 | 94 | h3. Interpretor Object 95 | 96 | Currently each process cannot have multiple @ruby@ interpretors, 97 | this was discussed in Chapter 13. 98 | If having multiple interpretors is practically possible, it seems better, 99 | but is it possible to implement such thing? 100 | 101 | 102 | h3. The structure of evaluator 103 | 104 | Current @eval.c@ is, above all, too complex. 105 | Embedding Ruby's stack frames to machine stack could occasionally become the 106 | source of trouble, using @setjmp() longjmp()@ aggressively makes it less easy to 107 | understand and slows down its speed. 108 | Particularly with RISC machine, which has many registers, using @setjmp()@ 109 | aggressively can easily cause slowing down because @setjmp()@ set aside all 110 | things in registers. 111 | 112 | 113 | h3. The performance of evaluator 114 | 115 | @ruby@ is already enough fast for ordinary use. 116 | But aside from it, regarding a language processor, 117 | definitely the faster is the better. 118 | To achieve better performance, in other words to optimize, 119 | what can we do? 120 | In such case, the first thing we have to do is profiling. 121 | So I profiled. 122 | 123 |124 | % cumulative self self total 125 | time seconds seconds calls ms/call ms/call name 126 | 20.25 1.64 1.64 2638359 0.00 0.00 rb_eval 127 | 12.47 2.65 1.01 1113947 0.00 0.00 ruby_re_match 128 | 8.89 3.37 0.72 5519249 0.00 0.00 rb_call0 129 | 6.54 3.90 0.53 2156387 0.00 0.00 st_lookup 130 | 6.30 4.41 0.51 1599096 0.00 0.00 rb_yield_0 131 | 5.43 4.85 0.44 5519249 0.00 0.00 rb_call 132 | 5.19 5.27 0.42 388066 0.00 0.00 st_foreach 133 | 3.46 5.55 0.28 8605866 0.00 0.00 rb_gc_mark 134 | 2.22 5.73 0.18 3819588 0.00 0.00 call_cfunc 135 |136 | 137 | This is a profile when running some application but 138 | this is approximately the profile of a general Ruby program. 139 | @rb_eval()@ appeared in the overwhelming percentage being at the top, 140 | after that, in addition to functions of GC, evaluator core, 141 | functions that are specific to the program are mixed. 142 | For example, in the case of this application, 143 | it takes a lot of time for regular expression match (@ruby_re_match@). 144 | 145 | However, even if we understood this, the question is how to improve it. 146 | To think simply, it can be archived by making @rb_eval()@ faster. 147 | That said, but as for @ruby@ core, there are almost not any room which can be 148 | easily optimized. For instance, apparently "tail recursive -> @goto@ conversion" 149 | used in the place of @NODE_IF@ and others has already applied almost all 150 | possible places it can be applied. 151 | In other words, without changing the way of thinking fundamentally, 152 | there's no room to improve. 153 | 154 | 155 | h3. The implementation of thread 156 | 157 | This was also discussed in Chapter 19. There are really a lot of issues about 158 | the implementation of the current ruby's thread. Particularly, it cannot mix 159 | with native threads so badly. The two great advantages of @ruby@'s thread, 160 | (1) high portability (2) the same behavior everywhere, 161 | are definitely incomparable, but probably that implementation is something we 162 | cannot continue to use eternally, isn't it? 163 | 164 | 165 | 166 | 167 | h2. `ruby` 2 168 | 169 | Subsequently, on the other hand, I'll introduce the trend of the original `ruby`, 170 | how it is trying to counter these issues. 171 | 172 | 173 | h3. Rite 174 | 175 | At the present time, ruby's edge is 1.6.7 as the stable version and 1.7.3 as the 176 | development version, but perhaps the next stable version 1.8 will come out in 177 | the near future. Then at that point, the next development version 1.9.0 will 178 | start at the same time. And after that, this is a little irregular but 1.9.1 179 | will be the next stable version. 180 | 181 | |_. stable |_. development |_. when to start | 182 | | 1.6.x | 1.7.x | 1.6.0 was released on 2000-09-19 | 183 | | 1.8.x | 1.9.x | probably it will come out within 6 months | 184 | | 1.9.1~ | 2.0.0 | maybe about 2 years later | 185 | 186 | 187 | And the next-to-next generational development version is `ruby` 2, whose code 188 | name is Rite. Apparently this name indicates a respect for the inadequacy that 189 | Japanese cannot distinguish the sounds of L and R. 190 | 191 | What will be changed in 2.0 is, in short, almost all the entire core. 192 | Thread, evaluator, parser, all of them will be changed. 193 | However, nothing has been written as a code yet, so things written here is 194 | entirely just a "plan". If you expect so much, it's possible it will turn out 195 | disappointments. Therefore, for now, let's just expect slightly. 196 | 197 | 198 | h3. The language to write 199 | 200 | Firstly, the language to use. Definitely it will be C. Mr. Matsumoto said to 201 | `ruby-talk`, which is the English mailing list for Ruby, 202 | 203 |204 | I hate C++. 205 |206 | 207 | So, C++ is most unlikely. Even if all the parts will be recreated, 208 | it is reasonable that the object system will remain almost the same, 209 | so not to increase extra efforts around this is necessary. 210 | However, chances are good that it will be ANSI C next time. 211 | 212 | 213 | h3. GC 214 | 215 | Regarding the implementation of GC, 216 | the good start point would be 217 | `Boehm GC`\footnote{Boehm GC `http://www.hpl.hp.com/personal/Hans_Boehm/gc`}. 218 | Bohem GC is a conservative and incremental and generational GC, 219 | furthermore, it can mark all stack spaces of all threads even while native 220 | threads are running. It's really an impressive GC. 221 | Even if it is introduced once, it's hard to tell whether it will be used 222 | perpetually, but anyway it will proceed for the direction to which we can expect 223 | somewhat improvement on speed. 224 | 225 | 226 | h3. Parser 227 | 228 | Regarding the specification, it's very likely that the nested method calls 229 | without parentheses will be forbidden. As we've seen, `command_call` has a great 230 | influence on all over the grammar. If this is simplified, both the parser and 231 | the scanner will also be simplified a lot. 232 | However, the ability to omit parentheses itself will never be disabled. 233 | 234 | And regarding its implementation, whether we continue to use `yacc` is still 235 | under discussion. If we won't use, it would mean hand-writing, but is it 236 | possible to implement such complex thing by hand? Such anxiety might left. 237 | Whichever way we choose, the path must be thorny. 238 | 239 | 240 | h3. Evaluator 241 | 242 | The evaluator will be completely recreated. 243 | Its aims are mainly to improve speed and to simplify the implementation. 244 | There are two main viewpoints: 245 | 246 | 247 | * remove recursive calls like `rb_eval()` 248 | * switch to a bytecode interpretor 249 | 250 | First, removing recursive calls of `rb_eval()`. The way to remove is, 251 | maybe the most intuitive explanation is that it's like the "tail recursive -> 252 | `goto` conversion". Inside a single `rb_eval()`, circling around by using 253 | `goto`. That decreases the number of function calls and removes the necessity of 254 | `setjmp()` that is used for `return` or `break`. 255 | However, when a function defined in C is called, calling a function is 256 | inevitable, and at that point `setjmp()` will still be required. 257 | 258 | 259 | Bytecode is, in short, something like a program written in machine language. 260 | It became famous because of the virtual machine of Smalltalk90, 261 | it is called bytecode because each instruction is one-byte. 262 | For those who are usually working at more abstract level, byte would seem 263 | so natural basis in size to deal with, 264 | but in many cases each instruction consists of bits in machine languages. 265 | For example, in Alpha, among a 32-bit instruction code, the beginning 6-bit 266 | represents the instruction type. 267 | 268 | 269 | The advantage of bytecode interpretors is mainly for speed. There are two 270 | reasons: Firstly, unlike syntax trees, there's no need to traverse pointers. 271 | Secondly, it's easy to do peephole optimization. 272 | 273 | 274 | And in the case when bytecode is saved and read in later, 275 | because there's no need to parse, we can naturally expect better performance. 276 | However, parsing is a procedure which is done only once at the beginning of a 277 | program and even currently it does not take so much time. Therefore, its 278 | influence will not be so much. 279 | 280 | 281 | If you'd like to know about how the bytecode evaluator could be, 282 | `regex.c` is worth to look at. 283 | For another example, Python is a bytecode interpretor. 284 | 285 | 286 | 287 | 288 | h3. Thread 289 | 290 | Regarding thread, the thing is native thread support. 291 | The environment around thread has been significantly improved, 292 | comparing with the situation in 1994, the year of Ruby's birth. 293 | So it might be judged that 294 | we can get along with native thread now. 295 | 296 | 297 | Using native thread means being preemptive also at C level, 298 | thus the interpretor itself must be multi-thread safe, 299 | but it seems this point is going to be solved by using a global lock 300 | for the time being. 301 | 302 | 303 | Additionally, that somewhat arcane "continuation", it seems likely to be removed. 304 | `ruby`'s continuation highly depends on the implementation of thread, 305 | so naturally it will disappear if thread is switched to native thread. 306 | The existence of that feature is because "it can be implemented" 307 | and it is rarely actually used. Therefore there might be no problem. 308 | 309 | 310 | 311 | h3. M17N 312 | 313 | In addition, I'd like to mention a few things about class libraries. 314 | This is about multi-lingualization (M17N for short). 315 | What it means exactly in the context of programming is 316 | being able to deal with multiple character encodings. 317 | 318 | 319 | `ruby` with Multi-lingualization support has already implemented and you can 320 | obtain it from the `ruby_m17m` branch of the CVS repository. 321 | It is not absorbed yet because it is judged that its specification is immature. 322 | If good interfaces is designed, 323 | it will be absorbed at some point in the middle of 1.9. 324 | 325 | 326 | 327 | 328 | h3. IO 329 | 330 | 331 | The `IO` class in current Ruby is a simple wrapper of `stdio`, 332 | but in this approach, 333 | 334 | * there are too many but slight differences between various platforms. 335 | * we'd like to have finer control on buffers. 336 | 337 | these two points cause complaints. 338 | Therefore, it seems Rite will have its own `stdio`. 339 | 340 | 341 | 342 | 343 | 344 | 345 | h2. Ruby Hacking Guide 346 | 347 | 348 | So far, we've always acted as observers who look at `ruby` from outside. 349 | But, of course, `ruby` is not a product which displayed in in a showcase. 350 | It means we can influence it if we take an action for it. 351 | In the last section of this book, 352 | I'll introduce the suggestions and activities for `ruby` from community, 353 | as a farewell gift for Ruby Hackers both at present and in the future. 354 | 355 | 356 | h3. Generational GC 357 | 358 | First, as also mentioned in Chapter 5, 359 | the generational GC made by Mr. Kiyama Masato. 360 | As described before, with the current patch, 361 | 362 | * it is less fast than expected. 363 | * it needs to be updated to fit the edge `ruby` 364 | 365 | these points are problems, but here I'd like to highly value it because, 366 | more than anything else, it was the first large non-official patch. 367 | 368 | 369 | 370 | 371 | h3. Oniguruma 372 | 373 | The regular expression engine used by current Ruby is a remodeled version of GNU 374 | regex. That GNU regex was in the first place written for Emacs. And then it was 375 | remodeled so that it can support multi-byte characters. And then Mr. Matsumoto 376 | remodeled so that it is compatible with Perl. 377 | As we can easily imagine from this history, 378 | its construction is really intricate and spooky. 379 | Furthermore, due to the LPGL license of this GNU regex, 380 | the license of `ruby` is very complicated, 381 | so replacing this engine has been an issue from a long time ago. 382 | 383 | What suddenly emerged here is the regular expression engine "Oniguruma" by 384 | Mr. K. Kosako. I heard this is written really well, it is likely being 385 | absorbed as soon as possible. 386 | 387 | You can obtain Oniguruma from the `ruby`'s CVS repository in the following way. 388 | 389 |390 | % cvs -d :pserver:anonymous@cvs.ruby-lang.org:/src co oniguruma 391 |392 | 393 | 394 | 395 | h3. ripper 396 | 397 | Next, ripper is my product. It is an extension library made by remodeling 398 | `parse.y`. It is not a change applied to the `ruby`'s main body, but I 399 | introduced it here as one possible direction to make the parser a component. 400 | 401 | It is implemented with kind of streaming interface and 402 | it can pick up things such as token scan or parser's reduction as events. 403 | It is put in the attached CD-ROM 404 | \footnote{ripper:`archives/ripper-0.0.5.tar.gz` of the attached CD-ROM}, 405 | so I'd like you to give it a try. 406 | Note that the supported grammar is a little different from the current one 407 | because this version is based on `ruby` 1.7 almost half-year ago. 408 | 409 | I created this just because "I happened to come up with this idea", 410 | if this is accounted, I think it is constructed well. 411 | It took only three days or so to implement, really just a piece of cake. 412 | 413 | 414 | h3. A parser alternative 415 | 416 | This product has not yet appeared in a clear form, 417 | there's a person who write a Ruby parser in C++ which can be used totally 418 | independent of `ruby`. (`[ruby-talk:50497]`). 419 | 420 | 421 | h3. JRuby 422 | 423 | More aggressively, there's an attempt to rewrite entire the interpretor. 424 | For example, a Ruby written in Java, 425 | Ruby\footnote{JRuby `http://jruby.sourceforge.net`}, 426 | has appeared. 427 | It seems it is being implemented by a large group of people, 428 | Mr. Jan Arne Petersen and many others. 429 | 430 | 431 | I tried it a little and as my reviews, 432 | 433 | * the parser is written really well. It does precisely handle even finer 434 | behaviors such as spaces or here document. 435 | * `instance_eval` seems not in effect (probably it couldn't be helped). 436 | * it has just a few built-in libraries yet (couldn't be helped as well). 437 | * we can't use extension libraries with it (naturally). 438 | * because Ruby's UNIX centric is all cut out, 439 | there's little possibility that we can run already-existing scripts without 440 | any change. 441 | * slow 442 | 443 | perhaps I could say at least these things. 444 | Regarding the last one "slow", its degree is, 445 | the execution time it takes is 20 times longer than the one of the original 446 | `ruby`. Going this far is too slow. 447 | It is not expected running fast because that Ruby VM runs on Java VM. 448 | Waiting for the machine to become 20 times faster seems only way. 449 | 450 | 451 | However, the overall impression I got was, it's way better than I imagined. 452 | 453 | 454 | 455 | h3. NETRuby 456 | 457 | If it can run with Java, it should also with C#. 458 | Therefore, a Ruby written in C# appeared, 459 | "NETRuby\footnote{NETRuby `http://sourceforge.jp/projects/netruby/`}". 460 | The author is Mr. arton. 461 | 462 | Because I don't have any .NET environment at hand, 463 | I checked only the source code, 464 | but according to the author, 465 | 466 | * more than anything, it's slow 467 | * it has a few class libraries 468 | * the compatibility of exception handling is not good 469 | 470 | such things are the problems. 471 | But `instance_eval` is in effect (astounding!). 472 | 473 | 474 | h3. How to join `ruby` development 475 | 476 | `ruby`'s developer is really Mr. Matsumoto as an individual, 477 | regarding the final decision about the direction `ruby` will take, 478 | he has the definitive authority. 479 | But at the same time, `ruby` is an open source software, 480 | anyone can join the development. 481 | Joining means, you can suggest your opinions or send patches. 482 | The below is to concretely tell you how to join. 483 | 484 | In `ruby`'s case, the mailing list is at the center of the development, 485 | so it's good to join the mailing list. 486 | The mailing lists currently at the center of the community are three: 487 | `ruby-list`, `ruby-dev`, `ruby-talk`. 488 | `ruby-list` is a mailing list for "anything relating to Ruby" in Japanese. 489 | `ruby-dev` is for the development version `ruby`, this is also in Japanese. 490 | `ruby-talk` is an English mailing list. 491 | The way to join is shown on the page "mailing lists" at Ruby's official site 492 | \footnote{Ruby's official site: `http://www.ruby-lang.org/ja/`}. 493 | For these mailing lists, read-only people are also welcome, 494 | so I recommend just joining first and watching discussions 495 | to grasp how it is. 496 | 497 | Though Ruby's activity started in Japan, 498 | recently sometimes it is said "the main authority now belongs to `ruby-talk`". 499 | But the center of the development is still `ruby-dev`. 500 | Because people who has the commit right to `ruby` (e.g. core members) are mostly 501 | Japanese, the difficulty and reluctance of using English 502 | naturally lead them to `ruby-dev`. 503 | If there will be more core members who prefer to use English, 504 | the situation could be changed, 505 | but meanwhile the core of `ruby`'s development might remain `ruby-dev`. 506 | 507 | However, it's bad if people who cannot speak Japanese cannot join the 508 | development, so currently the summary of `ruby-dev` is translated once a week 509 | and posted to `ruby-talk`. 510 | I also help that summarising, but only three people do it in turn now, 511 | so the situation is really harsh. 512 | The members to help summarize is always in demand. 513 | If you think you're the person who can help, 514 | I'd like you to state it at `ruby-list`. 515 | 516 | And as the last note, 517 | only its source code is not enough for a software. 518 | It's necessary to prepare various documents and maintain web sites. 519 | And people who take care of these kind of things are always in short. 520 | There's also a mailing list for the document-related activities, 521 | but as the first step you just have to propose "I'd like to do something" to `ruby-list`. 522 | I'll answer it as much as possible, 523 | and other people would respond to it, too. 524 | 525 | 526 | h3. Finale 527 | 528 | The long journey of this book is going to end now. 529 | As there was the limitation of the number of pages, 530 | explaining all of the parts comprehensively was impossible, 531 | however I told everything I could tell about the `ruby`'s core. 532 | I won't add extra things any more here. 533 | If you still have things you didn't understand, 534 | I'd like you to investigate it by reading the source code by yourself as much as 535 | you want. 536 | --------------------------------------------------------------------------------