galaaz 0.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/LICENSE +0 -0
- data/README.md +1360 -636
- data/Rakefile +61 -41
- data/bin/galaaz-bootstrap +137 -0
- data/bin/galaaz-jruby +14 -0
- data/bin/galaaz_jruby_env.inc.sh +6 -0
- data/bin/gbookdown +64 -0
- data/bin/gknit +84 -13
- data/bin/gknit-draft.rb +0 -0
- data/bin/gstudio +5 -3
- data/bin/gstudio_irb.rb +0 -0
- data/bin/gstudio_pry.rb +0 -0
- data/bin/install-tinytex +6 -0
- data/bin/run_all_rspec +43 -0
- data/bin/run_example +14 -0
- data/bin/run_old_rspec +19 -0
- data/bin/run_rspec +23 -0
- data/bin/run_rspec_subset +38 -0
- data/bin/run_slow_rspec +19 -0
- data/blogs/R-on-Rails-Planning-Document.md +940 -0
- data/blogs/README.md +100 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -66
- data/blogs/galaaz_ggplot/galaaz_ggplot.log +754 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +115 -155
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +607 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/midwest.Rmd +3 -3
- data/blogs/galaaz_ggplot/midwest_external_png +0 -0
- data/blogs/gknit/gknit.Rmd +47 -52
- data/blogs/gknit/gknit.md +1430 -0
- data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
- data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
- data/blogs/gknit/lst.rds +0 -0
- data/blogs/gknit/model.rb +1 -1
- data/blogs/gknit/stats.bib +0 -0
- data/blogs/manual/include_model_local_repro.Rmd +14 -0
- data/blogs/manual/include_model_local_repro.md +75 -0
- data/blogs/manual/lst.rds +0 -0
- data/blogs/manual/manual.Rmd +852 -239
- data/blogs/manual/manual.log +1786 -0
- data/blogs/manual/manual.md +1360 -636
- data/blogs/manual/manual.tex +1883 -1161
- data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-html/diverging_bar.png +0 -0
- data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
- data/blogs/manual/model.rb +1 -1
- data/blogs/nse_dplyr/nse_dplyr.Rmd +84 -111
- data/blogs/nse_dplyr/nse_dplyr.log +928 -0
- data/blogs/nse_dplyr/nse_dplyr.md +198 -229
- data/blogs/oh_my/not_so.rb +0 -0
- data/blogs/oh_my/oh_my.Rmd +1234 -25
- data/blogs/oh_my/oh_my.log +804 -0
- data/blogs/oh_my/oh_my.md +1663 -86
- data/blogs/oh_my/oh_my.tex +821 -0
- data/blogs/oh_my/old.Rmd +15 -14
- data/blogs/ruby_plot/ruby_plot.Rmd +58 -82
- data/blogs/ruby_plot/ruby_plot.log +885 -0
- data/blogs/ruby_plot/ruby_plot.md +71 -102
- data/blogs/ruby_plot/ruby_plot.tex +940 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/blogs/test/test.Rmd +14 -0
- data/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
- data/examples/50Plots_MasterList/ScatterPlot.rb +0 -0
- data/examples/50Plots_MasterList/scatter_plot.rb +0 -0
- data/examples/Bibliography/master.bib +0 -0
- data/examples/Bibliography/stats.bib +0 -0
- data/examples/R/calc.R +0 -0
- data/examples/R/java_interop.R +0 -0
- data/examples/bioconductor_deseq2_airway/Documentation/DESeq2-airway-walkthrough.md +56 -0
- data/examples/bioconductor_deseq2_airway/bench_galaaz_three_same_process.rb +53 -0
- data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +33 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_minimal.R +30 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_pipeline_for_bench.R +36 -0
- data/examples/islr/all.rb +13 -0
- data/examples/islr/ch2.spec.rb +37 -7
- data/examples/islr/ch3.spec.rb +11 -2
- data/examples/islr/ch3_boston.rb +27 -0
- data/examples/islr/ch3_multiple_regression.rb +0 -0
- data/examples/islr/ch6.spec.rb +24 -1
- data/examples/islr/x_y_rnorm.jpg +0 -0
- data/examples/latex_templates/Test-acm_article/acm_proc_article-sp.cls +0 -0
- data/examples/latex_templates/Test-acm_article/sigproc.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-Test-acs_article.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-my_output.bib +0 -0
- data/examples/latex_templates/Test-aea_article/BibFile.bib +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.Rmd +0 -0
- data/examples/latex_templates/Test-aea_article/references.bib +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.Rmd +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdfsync +0 -0
- data/examples/latex_templates/Test-ieee_article/IEEEtran.bst +0 -0
- data/examples/latex_templates/Test-ieee_article/mybibfile.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJournal.sty +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJreferences.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/Test-rjournal_article.Rmd +0 -0
- data/examples/misc/baseball.csv +0 -0
- data/examples/misc/ggplot.rb +3 -2
- data/examples/misc/moneyball.rb +0 -0
- data/examples/misc/subsetting.rb +0 -0
- data/examples/multithread_shards_to_r/shards_to_r.rb +67 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/attend-grade-relationships.csv +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.Rmd +0 -0
- data/examples/rmarkdown/svm-xaringan-example/svm-xaringan-example.Rmd +0 -0
- data/examples/sthda_ggplot/README.md +0 -0
- data/examples/sthda_ggplot/RUN.md +41 -0
- data/examples/sthda_ggplot/all.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +2 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/stat.rb +0 -0
- data/examples/sthda_ggplot/one_variable_discrete/bar.rb +0 -0
- data/examples/sthda_ggplot/qplots/box_violin_dot.rb +0 -0
- data/examples/sthda_ggplot/qplots/scatter_plots.rb +0 -0
- data/examples/sthda_ggplot/scatter_gg.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +4 -3
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +0 -0
- data/ext/new_bridge/Makefile +46 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase0.cpp +12 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase1.cpp +1639 -0
- data/lib/R_interface/galaaz_device.R +20 -0
- data/lib/R_interface/include_engine.R +109 -0
- data/lib/R_interface/new_bridge_adapter.rb +824 -0
- data/lib/R_interface/r.rb +177 -25
- data/lib/R_interface/r_arrow.rb +113 -0
- data/lib/R_interface/r_libs.R +3 -3
- data/lib/R_interface/r_methods.rb +13 -126
- data/lib/R_interface/r_module_s.rb +0 -0
- data/lib/R_interface/rbinary_operators.rb +20 -2
- data/lib/R_interface/rclosure.rb +5 -1
- data/lib/R_interface/rdata_frame.rb +34 -70
- data/lib/R_interface/rdevice.rb +125 -0
- data/lib/R_interface/rdevices.R +0 -0
- data/lib/R_interface/renvironment.rb +10 -4
- data/lib/R_interface/rexpression.rb +5 -1
- data/lib/R_interface/rindexed_object.rb +41 -13
- data/lib/R_interface/rlanguage.rb +20 -62
- data/lib/R_interface/rlist.rb +115 -25
- data/lib/R_interface/rlogical_operators.rb +0 -0
- data/lib/R_interface/rmatrix.rb +2 -11
- data/lib/R_interface/rmd_indexed_object.rb +5 -1
- data/lib/R_interface/robject.rb +348 -290
- data/lib/R_interface/rpkg.rb +0 -0
- data/lib/R_interface/rsupport.rb +609 -328
- data/lib/R_interface/rsupport_scope.rb +2 -1
- data/lib/R_interface/rsymbol.rb +50 -0
- data/lib/R_interface/ruby_callback.rb +2 -3
- data/lib/R_interface/ruby_extensions.rb +225 -175
- data/lib/R_interface/runary_operators.rb +0 -0
- data/lib/R_interface/rvector.rb +147 -31
- data/lib/galaaz.rb +0 -0
- data/lib/galaaz_jruby.rb +22 -0
- data/lib/gknit/diagnostics.rb +50 -0
- data/lib/gknit/draft.rb +23 -17
- data/lib/gknit/include_engine.rb +15 -7
- data/lib/gknit/knitr_engine.rb +223 -74
- data/lib/gknit/rb_engine.rb +3 -3
- data/lib/gknit/ruby_engine.rb +0 -0
- data/lib/gknit.rb +1 -0
- data/lib/new_bridge/bootstrap/windows_bootstrap.rb +285 -0
- data/lib/new_bridge/envelope.rb +51 -0
- data/lib/new_bridge/eval_result.rb +26 -0
- data/lib/new_bridge/framing.rb +39 -0
- data/lib/new_bridge/instance_pool_client.rb +38 -0
- data/lib/new_bridge/r_instance_manager.rb +404 -0
- data/lib/new_bridge/session_client.rb +530 -0
- data/lib/new_bridge/tcp_framed.rb +44 -0
- data/lib/new_bridge.rb +9 -0
- data/lib/util/exec_ruby.rb +95 -20
- data/lib/util/inline_file.rb +35 -30
- data/new_bridge_specs/benchmark_phase5_5_unboxing_spec.rb +96 -0
- data/new_bridge_specs/eval_r_async_spec.rb +113 -0
- data/new_bridge_specs/integration_phase5_1_concurrent_spec.rb +50 -0
- data/new_bridge_specs/integration_phase5_1_eval_spec.rb +16 -0
- data/new_bridge_specs/integration_phase5_1_r_api_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_1_smoke_spec.rb +31 -0
- data/new_bridge_specs/integration_phase5_2_dataframe_unboxing_spec.rb +19 -0
- data/new_bridge_specs/integration_phase5_2_handle_eval_unboxing_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_3_callback_args_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callback_error_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_callback_timeout_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callbacks_smoke_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_edge_cases_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_3_nested_spec.rb +30 -0
- data/new_bridge_specs/integration_phase5_4_concurrent_sessions_spec.rb +53 -0
- data/new_bridge_specs/integration_phase5_4_nested_session_callbacks_spec.rb +49 -0
- data/new_bridge_specs/integration_phase5_4_session_routing_spec.rb +38 -0
- data/new_bridge_specs/integration_phase5_5_stress_concurrency_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_5_unbox_walk_spec.rb +46 -0
- data/new_bridge_specs/phase0_protocol_spec.rb +96 -0
- data/new_bridge_specs/phase1_req_ret_spec.rb +66 -0
- data/new_bridge_specs/phase2_multi_instance_spec.rb +67 -0
- data/new_bridge_specs/phase3_callbacks_spec.rb +71 -0
- data/new_bridge_specs/phase4_2_hardening_spec.rb +252 -0
- data/new_bridge_specs/phase4_3_r_instance_manager_spec.rb +85 -0
- data/new_bridge_specs/phase4_nested_callbacks_spec.rb +123 -0
- data/r_requires/ggplot.rb +0 -0
- data/r_requires/knitr.rb +0 -0
- data/specs/all.rb +15 -11
- data/specs/arrow_from_ruby_batches_spec.rb +50 -0
- data/specs/arrow_semantics_spec.rb +64 -0
- data/specs/bridge_concurrent_spec.rb +46 -0
- data/specs/bridge_nested_spec.rb +25 -0
- data/specs/dataframe_semantics_spec.rb +122 -0
- data/specs/dataframe_single_index_logical_filter_spec.rb +21 -0
- data/specs/dispatch_probe_cache_spec.rb +38 -0
- data/specs/dispatch_probe_error_class_fallback_spec.rb +20 -0
- data/specs/dispatch_probe_fallback_spec.rb +18 -0
- data/specs/environment_semantics_spec.rb +89 -0
- data/specs/field_access_spec.rb +31 -0
- data/specs/figures/bg.jpeg +0 -0
- data/specs/figures/bg.png +0 -0
- data/specs/figures/bg.svg +168 -57
- data/specs/figures/dose_len.png +0 -0
- data/specs/figures/no_args.jpeg +0 -0
- data/specs/figures/no_args.png +0 -0
- data/specs/figures/no_args.svg +168 -57
- data/specs/figures/width_height.jpeg +0 -0
- data/specs/figures/width_height.png +0 -0
- data/specs/figures/width_height_units1.jpeg +0 -0
- data/specs/figures/width_height_units1.png +0 -0
- data/specs/figures/width_height_units2.jpeg +0 -0
- data/specs/figures/width_height_units2.png +0 -0
- data/specs/formula_semantics_spec.rb +81 -0
- data/specs/galaaz_util_exec_ruby_spec.rb +85 -0
- data/specs/galaaz_util_inline_file_spec.rb +54 -0
- data/specs/gknit_cli_option_permutation_spec.rb +24 -0
- data/specs/gknit_include_engine_spec.rb +72 -0
- data/specs/gknit_install_timeout_report_spec.rb +69 -0
- data/specs/gknit_internal_error_report_spec.rb +57 -0
- data/specs/gknit_vector_map_output_spec.rb +59 -0
- data/specs/globalenv_guardrail_spec.rb +52 -0
- data/specs/language_expression_semantics_spec.rb +145 -0
- data/specs/list_semantics_spec.rb +111 -0
- data/specs/new_bridge_bulk_dataframe_transfer_spec.rb +44 -0
- data/specs/new_bridge_bulk_vector_transfer_spec.rb +73 -0
- data/specs/new_bridge_callback_timeout_spec.rb +69 -0
- data/specs/new_bridge_eval_r_fallback_spec.rb +55 -0
- data/specs/nil_null_spec.rb +42 -0
- data/specs/object_build_phase2_spec.rb +53 -0
- data/specs/phase1_callback_bridge_spec.rb +84 -0
- data/specs/phase2_gknit_generic_rendering_guardrail_spec.rb +46 -0
- data/specs/phase2_gknit_no_raw_code_leakage_spec.rb +43 -0
- data/specs/phase3_gknit_generic_graphics_capture_spec.rb +71 -0
- data/specs/plot_device_semantics_spec.rb +28 -0
- data/specs/plot_snapshot_semantics_spec.rb +58 -0
- data/specs/protocol_result_spec.rb +236 -0
- data/specs/r_batch_fail_fast_spec.rb +47 -0
- data/specs/r_bridge_bootstrap_spec.rb +11 -0
- data/specs/r_devices.spec.rb +1 -1
- data/specs/r_eval.spec.rb +16 -18
- data/specs/r_function.spec.rb +1 -1
- data/specs/r_instance_manager_spec.rb +285 -0
- data/specs/r_list_apply.spec.rb +15 -15
- data/specs/r_matrix.spec.rb +0 -0
- data/specs/r_nse.spec.rb +5 -5
- data/specs/r_object_send_dispatch_spec.rb +13 -0
- data/specs/r_vector_comparator_spec.rb +8 -0
- data/specs/r_vector_creation.spec.rb +0 -0
- data/specs/r_vector_functions.spec.rb +0 -0
- data/specs/r_vector_object.spec.rb +0 -0
- data/specs/r_vector_operators.spec.rb +0 -0
- data/specs/r_vector_structured_scalar_reads_spec.rb +35 -0
- data/specs/r_vector_subsetting.spec.rb +0 -0
- data/specs/range_helper_spec.rb +21 -0
- data/specs/rsupport_scope_spec.rb +28 -0
- data/specs/rsupport_var_name_thread_safety_spec.rb +24 -0
- data/specs/scalar_character_spec.rb +44 -0
- data/specs/scoped_symbol_dsl_refinement_spec.rb +40 -0
- data/specs/session_env_bridge_spec.rb +25 -0
- data/specs/simplecov_bootstrap_spec.rb +10 -0
- data/specs/spec_helper.rb +10 -0
- data/specs/tmp.rb +0 -0
- data/specs/unboxing_recursion_regression_spec.rb +30 -0
- data/specs/unboxing_spec.rb +49 -0
- data/specs/verify_callbacks.rb +42 -0
- data/sty/galaaz.sty +0 -0
- data/version.rb +1 -1
- metadata +194 -64
- data/blogs/galaaz_ggplot/galaaz_ggplot.html +0 -520
- data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
- data/blogs/galaaz_ggplot/midwest.html +0 -188
- data/blogs/gknit/gknit.html +0 -2266
- data/blogs/gknit/gknit.pdf +0 -0
- data/blogs/manual/manual.html +0 -4638
- data/blogs/manual/manual.pdf +0 -0
- data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
- data/blogs/nse_dplyr/nse_dplyr.html +0 -878
- data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
- data/blogs/oh_my/oh_my.html +0 -568
- data/blogs/ruby_plot/ruby_plot.html +0 -544
- data/blogs/ruby_plot/ruby_plot.pdf +0 -0
- data/examples/latex_templates/Test-acs_article/Test-acs_article.pdf +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/pics/Figure2.pdf +0 -0
- data/examples/latex_templates/Test-asa_article/Test-asa_article.pdf +0 -0
- data/examples/latex_templates/Test-ieee_article/Test-ieee_article.pdf +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJwrapper.pdf +0 -0
- data/examples/latex_templates/Test-springer_article/Test-springer_article.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.pdf +0 -0
- data/specs/r_dataframe.spec.rb +0 -379
- data/specs/r_environment.spec.rb +0 -140
- data/specs/r_formula.spec.rb +0 -232
- data/specs/r_language.spec.rb +0 -112
- data/specs/r_list.spec.rb +0 -293
- data/specs/r_plots.spec.rb +0 -72
- data/specs/ruby_expression.spec.rb +0 -316
data/lib/R_interface/rsupport.rb
CHANGED
|
@@ -1,396 +1,677 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
##########################################################################################
|
|
4
|
-
# @author Rodrigo Botafogo
|
|
5
|
-
#
|
|
6
|
-
# Copyright © 2018 Rodrigo Botafogo. All Rights Reserved. Permission to use, copy, modify,
|
|
7
|
-
# and distribute this software and its documentation, without fee and without a signed
|
|
8
|
-
# licensing agreement, is hereby granted, provided that the above copyright notice, this
|
|
9
|
-
# paragraph and the following two paragraphs appear in all copies, modifications, and
|
|
10
|
-
# distributions.
|
|
1
|
+
# rsupport.rb
|
|
11
2
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
3
|
+
# R::Support provides the core translation layer between Ruby and R: symbol conversion,
|
|
4
|
+
# argument parsing (parse_arg), evaluation (eval), and dispatching (exec_function,
|
|
5
|
+
# process_missing). process_missing is the entry point when an R::Object or the R
|
|
6
|
+
# module receives an unknown method: it handles setters (x=), eval, and generic
|
|
7
|
+
# calls (function, field access, or method with receiver as first arg).
|
|
16
8
|
#
|
|
17
|
-
|
|
18
|
-
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
|
19
|
-
# SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS".
|
|
20
|
-
# RODRIGO BOTAFOGO HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
|
|
21
|
-
# OR MODIFICATIONS.
|
|
22
|
-
##########################################################################################
|
|
23
|
-
|
|
9
|
+
require 'singleton'
|
|
24
10
|
|
|
25
11
|
module R
|
|
26
|
-
|
|
27
|
-
#--------------------------------------------------------------------------------------
|
|
28
|
-
# The empty_symbol is necessary to represent indexing with a missing argument such
|
|
29
|
-
# as [x, ]. What follows the ',' is an empty_symbol. Whenever we use in Ruby the
|
|
30
|
-
# :all symbol, it will be converted to an empty_symbol in R.
|
|
31
|
-
#--------------------------------------------------------------------------------------
|
|
32
|
-
=begin
|
|
33
|
-
@@empty_symbol = Polyglot.eval("R", <<-R)
|
|
34
|
-
__missing_arg = quote(f(,0));
|
|
35
|
-
__missing_arg[[2]]
|
|
36
|
-
R
|
|
37
|
-
|
|
38
12
|
def self.empty_symbol
|
|
39
|
-
|
|
13
|
+
""
|
|
40
14
|
end
|
|
41
|
-
=end
|
|
42
15
|
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
# Singleton representing R's NA; used when R returns NA and we keep it as a sentinel in Ruby.
|
|
17
|
+
class NotAvailable
|
|
18
|
+
def to_s; "NA"; end
|
|
19
|
+
def r_interop; "NA"; end
|
|
45
20
|
end
|
|
46
|
-
|
|
47
|
-
# When evaluating to NA, Interop treats it as FALSE. This breaks all expectations
|
|
48
|
-
# about NA. We need to protect NA from Interop unboxing. Class NotAvailable
|
|
49
|
-
# puts a list around NA, so that no unboxing occurs.
|
|
50
|
-
@na = Polyglot.eval("R", <<-R)
|
|
51
|
-
list(NA)
|
|
52
|
-
R
|
|
53
|
-
|
|
54
|
-
class NotAvailable < R::Object
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
NA = NotAvailable.new(@na)
|
|
21
|
+
NA = NotAvailable.new
|
|
58
22
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#--------------------------------------------------------------------------------------
|
|
23
|
+
# Raised when unboxing recurses beyond MAX_UNBOX_DEPTH (prevents stack/memory exhaustion).
|
|
24
|
+
class UnboxDepthError < RuntimeError; end
|
|
62
25
|
|
|
63
26
|
module Support
|
|
27
|
+
@@var_id = 0
|
|
28
|
+
@@var_id_mutex = Mutex.new
|
|
29
|
+
@dispatch_probe_cache = { func: {} }
|
|
30
|
+
# Phase 3: cache bridge dispatch_probe(handle, name) → { is_field, is_func } (FIFO eviction).
|
|
31
|
+
DISPATCH_PROBE_CACHE_MAX = 4096
|
|
32
|
+
@dispatch_probe_handle_cache = {}
|
|
33
|
+
@dispatch_probe_handle_fifo = []
|
|
34
|
+
@dispatch_probe_cache_mx = Mutex.new
|
|
35
|
+
@dispatch_probe_cache_hits = 0
|
|
36
|
+
@dispatch_probe_cache_misses = 0
|
|
37
|
+
TRANSPORT_NL = "\uE000".freeze
|
|
38
|
+
|
|
39
|
+
# Maximum recursion depth when unboxing lists. Beyond this we raise UnboxDepthError.
|
|
40
|
+
MAX_UNBOX_DEPTH = 100
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
attr_reader :dispatch_probe_cache_hits, :dispatch_probe_cache_misses
|
|
44
|
+
end
|
|
64
45
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
46
|
+
def self.reset_dispatch_probe_cache_stats!
|
|
47
|
+
@dispatch_probe_cache_mx&.synchronize do
|
|
48
|
+
@dispatch_probe_cache_hits = 0
|
|
49
|
+
@dispatch_probe_cache_misses = 0
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.dispatch_probe_handle_cache_size
|
|
54
|
+
@dispatch_probe_cache_mx.synchronize { @dispatch_probe_handle_cache.size }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.clear_dispatch_probe_handle_cache!
|
|
58
|
+
@dispatch_probe_cache_mx.synchronize do
|
|
59
|
+
@dispatch_probe_handle_cache.clear
|
|
60
|
+
@dispatch_probe_handle_fifo.clear
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.dispatch_probe_cache_key(handle, name)
|
|
65
|
+
"#{handle}\u0000#{name}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.store_dispatch_probe_handle_cache(key, is_field, is_func)
|
|
69
|
+
@dispatch_probe_cache_mx.synchronize do
|
|
70
|
+
if @dispatch_probe_handle_cache.key?(key)
|
|
71
|
+
@dispatch_probe_handle_cache[key] = { is_field: is_field, is_func: is_func }
|
|
72
|
+
return
|
|
73
|
+
end
|
|
74
|
+
while @dispatch_probe_handle_cache.size >= DISPATCH_PROBE_CACHE_MAX && !@dispatch_probe_handle_fifo.empty?
|
|
75
|
+
oldest = @dispatch_probe_handle_fifo.shift
|
|
76
|
+
@dispatch_probe_handle_cache.delete(oldest) if oldest
|
|
77
|
+
end
|
|
78
|
+
@dispatch_probe_handle_cache[key] = { is_field: is_field, is_func: is_func }
|
|
79
|
+
@dispatch_probe_handle_fifo << key
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Generate a unique R-side variable name (e.g. g2_v1, g2_v2) for assignment results.
|
|
84
|
+
def self.generate_var_name
|
|
85
|
+
@@var_id_mutex.synchronize do
|
|
86
|
+
@@var_id += 1
|
|
87
|
+
"g2_v#{@@var_id}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Convert a Ruby method name to the R name: __ => ., ___ => ::, rclass => class, eql => ==.
|
|
92
|
+
def self.convert_symbol2r(symbol)
|
|
93
|
+
name = symbol.to_s
|
|
94
|
+
name.gsub!(/___/, "::")
|
|
95
|
+
name.gsub!(/__/, ".")
|
|
96
|
+
case name
|
|
97
|
+
when "rclass" then "class"
|
|
98
|
+
when "eql" then "=="
|
|
99
|
+
else name
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Evaluate an R expression (string, Language, or handle). Returns a scalar value or R::Object.build(...).
|
|
104
|
+
# Uses .expression or .r_interop when present so we never treat R's printed output as code.
|
|
86
105
|
def self.eval(string)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
puts "DEBUG: Support.eval(#{string.inspect})" if ENV['GALAAZ_DEBUG']
|
|
107
|
+
|
|
108
|
+
var_name = self.generate_var_name
|
|
109
|
+
r_code = if string.respond_to?(:expression) && string.expression
|
|
110
|
+
string.expression.to_s
|
|
111
|
+
elsif string.respond_to?(:r_interop) && string.r_interop.is_a?(::String) && string.r_interop.start_with?("g2_v")
|
|
112
|
+
string.r_interop
|
|
113
|
+
else
|
|
114
|
+
string.to_s
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Determine if we need to wrap in braces or use eval()
|
|
118
|
+
# If it's a handle, we MUST use eval() in R to get its value if it's a symbol
|
|
119
|
+
final_r_code = if r_code.start_with?("g2_v")
|
|
120
|
+
"eval(#{r_code})"
|
|
121
|
+
elsif r_code.include?("\n") && !r_code.strip.start_with?("{") && !r_code.include?("<-") && !r_code.include?("=")
|
|
122
|
+
"{#{r_code}\n}"
|
|
123
|
+
else
|
|
124
|
+
r_code
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
assignment = "#{var_name} <- { #{final_r_code}\n }"
|
|
128
|
+
envelope = R.bridge.eval_r_with_result(assignment)
|
|
129
|
+
if ENV['GALAAZ_DEBUG']
|
|
130
|
+
puts "DEBUG: eval envelope=#{envelope.inspect}"
|
|
131
|
+
end
|
|
132
|
+
raise "Result protocol: no envelope (buffer missing or invalid)" unless envelope
|
|
133
|
+
|
|
134
|
+
case envelope[:type]
|
|
135
|
+
when :scalar_double, :scalar_integer, :scalar_logical
|
|
136
|
+
return envelope[:value]
|
|
137
|
+
when :scalar_character
|
|
138
|
+
return R::Object.build(var_name, nil, r_class: "character")
|
|
139
|
+
when :scalar_symbol
|
|
140
|
+
sym_name = envelope[:value].to_s
|
|
141
|
+
return sym_name.gsub("::", "___").gsub(".", "__").to_sym
|
|
142
|
+
when :handle
|
|
143
|
+
# Protocol spec: eval returns scalar symbol as Ruby Symbol. R sends symbol/name as handle (type 4); unbox here only.
|
|
144
|
+
r_class = envelope[:r_class].to_s.strip
|
|
145
|
+
if r_class == "name" || r_class == "symbol"
|
|
146
|
+
raw = R.bridge.eval_r("as.character(#{envelope[:handle]})").to_s
|
|
147
|
+
m = raw.match(/\[1\]\s*"([^"]*)"/)
|
|
148
|
+
name = m ? m[1] : raw.strip
|
|
149
|
+
return name.gsub("::", "___").gsub(".", "__").to_sym
|
|
150
|
+
end
|
|
151
|
+
return R::Object.build(envelope[:handle], nil, r_class: envelope[:r_class], wrapper_tag: envelope[:wrapper_tag])
|
|
152
|
+
else
|
|
153
|
+
raise "Result protocol: unknown envelope type #{envelope[:type].inspect}"
|
|
91
154
|
end
|
|
92
155
|
end
|
|
93
156
|
|
|
94
|
-
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
157
|
+
# String suitable for expression display (to_s). For R::Language use stored expression; for other R::Object
|
|
158
|
+
# use R's deparse() so display reflects the current value (not the creating R code stored in .expression).
|
|
159
|
+
def self.expression_display_arg(arg)
|
|
160
|
+
puts "DEBUG expression_display_arg: arg=#{arg.inspect} class=#{arg.class}" if ENV["GALAAZ_DEBUG"]
|
|
161
|
+
return arg.expression if arg.is_a?(R::Language) && arg.respond_to?(:expression) && arg.expression
|
|
162
|
+
return self.get_deparse_string(arg) if arg.is_a?(R::Object)
|
|
163
|
+
self.parse_arg(arg).to_s
|
|
101
164
|
end
|
|
102
|
-
|
|
103
|
-
#----------------------------------------------------------------------------------------
|
|
104
|
-
# @param arg [Object] A Ruby object to be converted to R to be used by an R function, or
|
|
105
|
-
# whatever needs it
|
|
106
|
-
# @return Object that can be used in R
|
|
107
|
-
#----------------------------------------------------------------------------------------
|
|
108
165
|
|
|
109
|
-
|
|
166
|
+
# R-side deparse of an R::Object to a single string (e.g. "c(1L, 2L, 3L, 4L)").
|
|
167
|
+
# Relies on R evaluating deparse() correctly on the object.
|
|
168
|
+
def self.get_deparse_string(arg)
|
|
169
|
+
puts "DEBUG get_deparse_string: called for #{arg.r_interop.inspect}" if ENV["GALAAZ_DEBUG"]
|
|
170
|
+
dep = self.exec_function("deparse", arg)
|
|
171
|
+
puts "DEBUG get_deparse_string: deparse result class=#{dep.class} value=#{dep.inspect}" if ENV["GALAAZ_DEBUG"]
|
|
172
|
+
str = self.exec_function("paste0", dep, { collapse: "" })
|
|
173
|
+
return str.to_s if str.is_a?(::String)
|
|
174
|
+
raw = R.bridge.eval_r("paste0(#{str.r_interop}, collapse='')").to_s
|
|
175
|
+
puts "DEBUG get_deparse_string: eval_r paste0 raw=#{raw.inspect}" if ENV["GALAAZ_DEBUG"]
|
|
176
|
+
m = raw.match(/\[1\]\s*"([^"]*)"/)
|
|
177
|
+
result = m ? m[1] : raw.strip
|
|
178
|
+
puts "DEBUG get_deparse_string: returning #{result.inspect}" if ENV["GALAAZ_DEBUG"]
|
|
179
|
+
result
|
|
180
|
+
end
|
|
110
181
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
182
|
+
# Turn a Ruby value into an R code fragment (string): handles, scalars, hashes -> list(), arrays -> c(), Procs -> R callback stub, etc.
|
|
183
|
+
def self.parse_arg(arg)
|
|
184
|
+
return arg.r_interop if arg.respond_to?(:r_interop) && arg.r_interop
|
|
185
|
+
return arg.expression if arg.respond_to?(:expression) && arg.expression
|
|
186
|
+
|
|
187
|
+
case arg
|
|
188
|
+
when Hash
|
|
189
|
+
arg.map do |k, v|
|
|
190
|
+
key = k.to_s.gsub(/__/, ".")
|
|
191
|
+
# R list names with spaces or special chars must be backtick-quoted
|
|
192
|
+
key_r = (key =~ /\A[a-zA-Z._][a-zA-Z0-9._]*\z/) ? key : "`#{key.gsub('`', '\\`')}`"
|
|
193
|
+
"#{key_r} = #{self.parse_arg(v)}"
|
|
194
|
+
end.join(", ")
|
|
195
|
+
when Array
|
|
196
|
+
"c(#{arg.map { |v| self.parse_arg(v) }.join(", ")})"
|
|
197
|
+
when Symbol
|
|
198
|
+
# :all means "all" in that dimension; R uses missing argument. Bridge defines missing_arg().
|
|
199
|
+
return "missing_arg()" if arg == :all
|
|
200
|
+
arg.to_s.gsub(/__/,".")
|
|
201
|
+
when R::SymbolRef
|
|
202
|
+
arg.to_r_symbol
|
|
203
|
+
when String
|
|
204
|
+
# If it's already a handle, don't quote it
|
|
205
|
+
if arg.start_with?("g2_v")
|
|
206
|
+
arg
|
|
207
|
+
else
|
|
208
|
+
"'#{arg.gsub("'", "\\\\'")}'"
|
|
209
|
+
end
|
|
116
210
|
when Numeric
|
|
117
|
-
|
|
118
|
-
when
|
|
119
|
-
|
|
120
|
-
|
|
211
|
+
arg.to_s + (arg.is_a?(Integer) ? "L" : "")
|
|
212
|
+
when TrueClass
|
|
213
|
+
"TRUE"
|
|
214
|
+
when FalseClass
|
|
215
|
+
"FALSE"
|
|
121
216
|
when Range
|
|
122
|
-
final_value = (arg.exclude_end?)? (arg.last - 1) : arg.last
|
|
123
|
-
|
|
124
|
-
when :all
|
|
125
|
-
R.empty_symbol
|
|
126
|
-
when Symbol
|
|
127
|
-
arg = R::Support.eval('as.name').call(arg.to_s.gsub(/__/,"."))
|
|
217
|
+
final_value = (arg.exclude_end?) ? (arg.last - 1) : arg.last
|
|
218
|
+
"seq(#{arg.first}, #{final_value})"
|
|
128
219
|
when Proc, Method
|
|
129
|
-
R
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
arg
|
|
220
|
+
if R.bridge.respond_to?(:register_callback_proc_stub)
|
|
221
|
+
return R.bridge.register_callback_proc_stub(arg)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
id = R::Support.register_callback(arg)
|
|
225
|
+
"function(...) {
|
|
226
|
+
args <- list(...)
|
|
227
|
+
handles <- character(0)
|
|
228
|
+
if (length(args) > 0) {
|
|
229
|
+
for (i in 1:length(args)) {
|
|
230
|
+
h <- paste0('g2_v_cb_', i, '_', as.integer(runif(1, 1e8, 9e8)))
|
|
231
|
+
assign(h, args[[i]], envir = .GlobalEnv)
|
|
232
|
+
cls <- paste(class(args[[i]]), collapse=' ')
|
|
233
|
+
handles <- c(handles, paste0(h, ':', cls))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
cat('--G_CALLBACK--#{id}--', paste(handles, collapse='|'), '--\\n', sep='')
|
|
237
|
+
flush.console()
|
|
238
|
+
while(TRUE) {
|
|
239
|
+
res_str <- readLines('/dev/shm/galaaz_callback_fifo', n=1)
|
|
240
|
+
if (length(res_str) < 1L) next
|
|
241
|
+
line <- res_str[1]
|
|
242
|
+
if (startsWith(line, '--G_CMD--')) {
|
|
243
|
+
# Extract sequence number if present (regmatches returns list; use [[1]] for sub)
|
|
244
|
+
seq_match <- regmatches(line, regexpr('--G_CMD--seq=([0-9]+)--', line))
|
|
245
|
+
seq_num <- if (length(seq_match) > 0L && length(seq_match[[1]]) > 0L) sub('--G_CMD--seq=([0-9]+)--', '\\\\1', seq_match[[1]]) else '0'
|
|
246
|
+
# Extract actual command
|
|
247
|
+
cmd_part <- sub('--G_CMD--(seq=[0-9]+--)?', '', line)
|
|
248
|
+
cmd <- trimws(cmd_part)
|
|
249
|
+
# Restore newlines (Ruby sends U+E000 as placeholder so FIFO is one line)
|
|
250
|
+
cmd <- gsub(\"#{TRANSPORT_NL}\", \"\\n\", cmd, fixed=TRUE)
|
|
251
|
+
recv_log <- Sys.getenv('GALAAZ_R_RECEIVED_LOG', '')
|
|
252
|
+
if (nchar(recv_log) > 0L) tryCatch({
|
|
253
|
+
write('---CMD---\\n', file=recv_log, append=TRUE)
|
|
254
|
+
write(cmd, file=recv_log, append=TRUE)
|
|
255
|
+
write('\\n', file=recv_log, append=TRUE)
|
|
256
|
+
}, error=function(e) NULL)
|
|
257
|
+
# Log that we're about to execute a command
|
|
258
|
+
cat('[R_CALLBACK]', 'seq=', seq_num, 'Executing:', substr(cmd, 1, 50), '...\\n', sep='')
|
|
259
|
+
# Wrap entire command handling in tryCatch to ensure --G_CMD_END-- is always sent
|
|
260
|
+
# Use capture.output with explicit print() to ensure output is captured
|
|
261
|
+
tryCatch({
|
|
262
|
+
if (nchar(cmd) > 0L) {
|
|
263
|
+
# Evaluate in .GlobalEnv so g2_v* handles created by Ruby are visible.
|
|
264
|
+
# Without this, eval() uses parent.frame() (chunk/engine env) and
|
|
265
|
+
# object 'g2_vNNN' not found occurs when indexing vectors/matrices.
|
|
266
|
+
result <- capture.output(print(eval(parse(text=cmd), envir = .GlobalEnv)))
|
|
267
|
+
cat(result, sep='\\n')
|
|
268
|
+
}
|
|
269
|
+
}, error=function(e) {
|
|
270
|
+
cat('--G_ERR--', conditionMessage(e), '\\n', sep='')
|
|
271
|
+
tb <- paste(capture.output(traceback()), collapse = '\\\\\\\\n')
|
|
272
|
+
if (nchar(tb) > 0L) cat('--G_TRACE--', tb, '\\n', sep='')
|
|
273
|
+
}, finally={
|
|
274
|
+
# Include sequence number in response for synchronization
|
|
275
|
+
cat('--G_CMD_END--seq=', seq_num, '--\\n', sep='')
|
|
276
|
+
flush.console()
|
|
277
|
+
})
|
|
278
|
+
} else if (startsWith(line, '--G_RET--')) {
|
|
279
|
+
res_handle <- trimws(sub('--G_RET--', '', line))
|
|
280
|
+
res_handle <- gsub(\"#{TRANSPORT_NL}\", \"\\n\", res_handle, fixed=TRUE)
|
|
281
|
+
if (nchar(res_handle) > 0L) return(eval(parse(text=res_handle), envir = .GlobalEnv))
|
|
282
|
+
return(invisible(NULL))
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}"
|
|
286
|
+
when nil
|
|
287
|
+
"NULL"
|
|
288
|
+
else
|
|
289
|
+
handle = self.register_ruby_object(arg)
|
|
290
|
+
"'#{handle}'"
|
|
134
291
|
end
|
|
292
|
+
end
|
|
135
293
|
|
|
294
|
+
# R `[` with a true missing dimension (Ruby :all). tidyselect on tbl_df rejects
|
|
295
|
+
# `missing_arg()` results as subscripts ("empty string"); alist(, ) is real missing.
|
|
296
|
+
def self.build_subscript_do_call_alist(all_args)
|
|
297
|
+
inner = all_args.map do |arg|
|
|
298
|
+
(arg.is_a?(::Symbol) && arg == :all) ? nil : parse_arg(arg)
|
|
299
|
+
end
|
|
300
|
+
inner = inner.map { |frag| frag.nil? ? '' : frag }.join(', ')
|
|
301
|
+
"do.call(`[`, alist(#{inner}))"
|
|
136
302
|
end
|
|
137
303
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
k = key.to_s.gsub(/__/,".")
|
|
146
|
-
|
|
147
|
-
case
|
|
148
|
-
when (value.is_a? NotAvailable)
|
|
149
|
-
na_named = R::Support.eval("`names<-`").call(value.r_interop, k)
|
|
150
|
-
lst = R::Support.eval("c").call(lst, na_named)
|
|
151
|
-
when (k == 'all')
|
|
152
|
-
lst = R::Support.eval("`[[<-`").
|
|
153
|
-
call(lst, R::Support.eval('`+`').
|
|
154
|
-
call(R::Support.eval('length').call(lst), 1),
|
|
155
|
-
R::Support.parse_arg(value))
|
|
304
|
+
# `[<-` with i/j = :all (R::DataFrame#[]=). Same tidyselect / anyNA issues with missing_arg().
|
|
305
|
+
def self.build_subscript_assign_do_call_alist(receiver, kw_hash)
|
|
306
|
+
pairs = kw_hash.map do |k, v|
|
|
307
|
+
key = k.to_s.gsub(/__/, ".")
|
|
308
|
+
key_r = (key =~ /\A[a-zA-Z._][a-zA-Z0-9._]*\z/) ? key : "`#{key.gsub('`', '\\`')}`"
|
|
309
|
+
if v.is_a?(::Symbol) && v == :all
|
|
310
|
+
"#{key_r} = "
|
|
156
311
|
else
|
|
157
|
-
|
|
158
|
-
call(lst, k, R::Support.parse_arg(value))
|
|
312
|
+
"#{key_r} = #{parse_arg(v)}"
|
|
159
313
|
end
|
|
160
314
|
end
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
315
|
+
inner = [parse_arg(receiver), *pairs].join(", ")
|
|
316
|
+
"do.call(`[<-`, alist(#{inner}))"
|
|
164
317
|
end
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def self.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
318
|
+
|
|
319
|
+
MD_INDEX_BACKTICK = '`[`'.freeze
|
|
320
|
+
MD_ASSIGN_BACKTICK = '`[<-`'.freeze
|
|
321
|
+
|
|
322
|
+
# Run an R call: f_name(args...). Builds assignment, gets envelope from bridge, returns R::Object (boxed).
|
|
323
|
+
# Unbox with .to_ruby, .unboxed_get(0), or >> 0. kwargs (e.g. i:, value:) are merged into args for R named-argument calls like `[[<-`(df, i=..., value=...).
|
|
324
|
+
def self.exec_function(function, *args, unbox: false, **kwargs)
|
|
325
|
+
f_name = function.respond_to?(:r_interop) ? function.r_interop : function
|
|
326
|
+
|
|
327
|
+
if args.empty? && kwargs.empty? && (f_name.include?("::") || f_name.start_with?("g2_v"))
|
|
328
|
+
return self.eval(f_name)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
var_name = self.generate_var_name
|
|
332
|
+
all_args = kwargs.empty? ? args : args + [kwargs]
|
|
333
|
+
|
|
334
|
+
use_subscript_alist =
|
|
335
|
+
kwargs.empty? &&
|
|
336
|
+
f_name == MD_INDEX_BACKTICK &&
|
|
337
|
+
all_args.any? { |a| a.is_a?(::Symbol) && a == :all } &&
|
|
338
|
+
!all_args.any? { |a| a.is_a?(Hash) }
|
|
339
|
+
|
|
340
|
+
use_assign_alist =
|
|
341
|
+
!kwargs.empty? &&
|
|
342
|
+
f_name == MD_ASSIGN_BACKTICK &&
|
|
343
|
+
all_args.size == 2 &&
|
|
344
|
+
all_args[1].is_a?(Hash) &&
|
|
345
|
+
all_args[1].values.any? { |v| v.is_a?(::Symbol) && v == :all }
|
|
346
|
+
|
|
347
|
+
if use_subscript_alist
|
|
348
|
+
r_expr = build_subscript_do_call_alist(all_args)
|
|
349
|
+
elsif use_assign_alist
|
|
350
|
+
r_expr = build_subscript_assign_do_call_alist(all_args[0], all_args[1])
|
|
351
|
+
else
|
|
352
|
+
r_args = all_args.map { |arg| self.parse_arg(arg) }
|
|
353
|
+
# eval(expr, envir): R expects expr as expression; parse_arg on Language returns bare string -> wrap in parse(text=...) so envir is used
|
|
354
|
+
if f_name == "eval" && args.size == 2 && !r_args[0].to_s.start_with?("g2_v", "quote(")
|
|
355
|
+
r_args[0] = "parse(text=#{r_args[0].to_s.inspect})"
|
|
185
356
|
end
|
|
357
|
+
|
|
358
|
+
r_expr = "#{f_name}(#{r_args.join(", ")})"
|
|
186
359
|
end
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# Converts a Ruby symbol onto an R symbol. Converts '__' to '.' and 'rclass' to
|
|
196
|
-
# class
|
|
197
|
-
# @param symbol [Symbol] A Ruby symbol to convert to R
|
|
198
|
-
#----------------------------------------------------------------------------------------
|
|
199
|
-
|
|
200
|
-
def self.convert_symbol2r(symbol)
|
|
201
|
-
name = symbol.to_s
|
|
202
|
-
# convert '__' to '.'
|
|
203
|
-
name.gsub!(/__/,".")
|
|
204
|
-
# Method 'rclass' is a substitute for R method 'class'. Needed, as 'class' is also
|
|
205
|
-
# a Ruby method on an object
|
|
206
|
-
name.gsub!("rclass", "class")
|
|
207
|
-
name
|
|
360
|
+
assignment = "#{var_name} <- #{r_expr}"
|
|
361
|
+
envelope = R.bridge.eval_r_with_result(assignment)
|
|
362
|
+
unless envelope
|
|
363
|
+
reason = R.bridge.respond_to?(:last_envelope_nil_reason) && R.bridge.last_envelope_nil_reason
|
|
364
|
+
raise "Result protocol: no envelope (buffer missing or invalid)#{reason ? " [#{reason}]" : ''}"
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
ruby_result_from_envelope(envelope, var_name, r_expr)
|
|
208
368
|
end
|
|
209
|
-
|
|
210
|
-
#----------------------------------------------------------------------------------------
|
|
211
|
-
# @param function [R function (Interop)] R function to execute
|
|
212
|
-
# @args [Array] Array of arguments for the function
|
|
213
|
-
#----------------------------------------------------------------------------------------
|
|
214
|
-
|
|
215
|
-
def self.exec_function(function, *args)
|
|
216
369
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
370
|
+
# Build the same Ruby value as +exec_function+ from a legacy envelope (+:handle+, scalars, +rb_obj_*+).
|
|
371
|
+
def self.ruby_result_from_envelope(envelope, var_name, r_expr)
|
|
372
|
+
case envelope[:type]
|
|
373
|
+
when :scalar_double, :scalar_integer, :scalar_logical, :scalar_character
|
|
374
|
+
val = envelope[:value]
|
|
375
|
+
if envelope[:type] == :scalar_character && val.is_a?(String) && val =~ /^rb_obj_\d+$/
|
|
376
|
+
return get_ruby_object(val)
|
|
224
377
|
end
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
378
|
+
r_class = { scalar_double: "numeric", scalar_integer: "integer", scalar_logical: "logical", scalar_character: "character" }[envelope[:type]]
|
|
379
|
+
return R::Object.build(var_name, r_expr, r_class: r_class)
|
|
380
|
+
when :scalar_symbol
|
|
381
|
+
sym_name = envelope[:value].to_s
|
|
382
|
+
return sym_name.gsub("::", "___").gsub(".", "__").to_sym
|
|
383
|
+
when :handle
|
|
384
|
+
return R::Object.build(envelope[:handle], r_expr, r_class: envelope[:r_class], wrapper_tag: envelope[:wrapper_tag])
|
|
385
|
+
else
|
|
386
|
+
raise "Result protocol: unknown envelope type #{envelope[:type].inspect}"
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Async variant of +exec_function+; +block+ receives +NewBridge::EvalResult+ (+#value+ is like +exec_function+ return).
|
|
391
|
+
def self.exec_function_async(function, *args, unbox: false, timeout: nil, **kwargs, &block)
|
|
392
|
+
raise ArgumentError, 'exec_function_async requires a block' unless block
|
|
393
|
+
|
|
394
|
+
f_name = function.respond_to?(:r_interop) ? function.r_interop : function
|
|
395
|
+
|
|
396
|
+
if args.empty? && kwargs.empty? && (f_name.include?("::") || f_name.start_with?("g2_v"))
|
|
397
|
+
raise ArgumentError, 'exec_function_async does not support bare handle/namespace reference; use R.eval_r_async'
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
var_name = self.generate_var_name
|
|
401
|
+
all_args = kwargs.empty? ? args : args + [kwargs]
|
|
402
|
+
|
|
403
|
+
use_subscript_alist =
|
|
404
|
+
kwargs.empty? &&
|
|
405
|
+
f_name == MD_INDEX_BACKTICK &&
|
|
406
|
+
all_args.any? { |a| a.is_a?(::Symbol) && a == :all } &&
|
|
407
|
+
!all_args.any? { |a| a.is_a?(Hash) }
|
|
408
|
+
|
|
409
|
+
use_assign_alist =
|
|
410
|
+
!kwargs.empty? &&
|
|
411
|
+
f_name == MD_ASSIGN_BACKTICK &&
|
|
412
|
+
all_args.size == 2 &&
|
|
413
|
+
all_args[1].is_a?(Hash) &&
|
|
414
|
+
all_args[1].values.any? { |v| v.is_a?(::Symbol) && v == :all }
|
|
415
|
+
|
|
416
|
+
if use_subscript_alist
|
|
417
|
+
r_expr = build_subscript_do_call_alist(all_args)
|
|
418
|
+
elsif use_assign_alist
|
|
419
|
+
r_expr = build_subscript_assign_do_call_alist(all_args[0], all_args[1])
|
|
420
|
+
else
|
|
421
|
+
r_args = all_args.map { |arg| self.parse_arg(arg) }
|
|
422
|
+
if f_name == "eval" && args.size == 2 && !r_args[0].to_s.start_with?("g2_v", "quote(")
|
|
423
|
+
r_args[0] = "parse(text=#{r_args[0].to_s.inspect})"
|
|
235
424
|
end
|
|
236
425
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
426
|
+
r_expr = "#{f_name}(#{r_args.join(", ")})"
|
|
427
|
+
end
|
|
428
|
+
assignment = "#{var_name} <- #{r_expr}"
|
|
429
|
+
|
|
430
|
+
R.bridge.eval_r_with_result_async(assignment, timeout: timeout) do |result|
|
|
431
|
+
if result.ok?
|
|
432
|
+
begin
|
|
433
|
+
env = result.value[:envelope]
|
|
434
|
+
vn = result.value[:var_name]
|
|
435
|
+
rx = result.value[:r_expr]
|
|
436
|
+
obj = ruby_result_from_envelope(env, vn, rx)
|
|
437
|
+
block.call(NewBridge::EvalResult.success(obj))
|
|
438
|
+
rescue StandardError => e
|
|
439
|
+
block.call(NewBridge::EvalResult.failure(e))
|
|
248
440
|
end
|
|
441
|
+
else
|
|
442
|
+
block.call(result)
|
|
249
443
|
end
|
|
250
|
-
|
|
251
|
-
rescue StandardError => e
|
|
252
|
-
R::Support.stop_capture.call(@@con) if (@@exec_counter == 0)
|
|
253
|
-
raise e
|
|
254
444
|
end
|
|
255
|
-
|
|
256
|
-
res
|
|
257
|
-
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
#----------------------------------------------------------------------------------------
|
|
261
|
-
# @param function_name [String] Name of the R function to execute
|
|
262
|
-
# @args [Array] Array of arguments for the function
|
|
263
|
-
#----------------------------------------------------------------------------------------
|
|
264
|
-
|
|
265
|
-
def self.exec_function_name(function_name, *args)
|
|
266
|
-
# @TODO: should check all that can go wrong when calling eval(function_name) to
|
|
267
|
-
# raise the proper exception
|
|
268
|
-
f = R::Support.eval(function_name)
|
|
269
|
-
R::Support.exec_function(f, *args)
|
|
270
445
|
end
|
|
271
446
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
#----------------------------------------------------------------------------------------
|
|
275
|
-
|
|
276
|
-
def self.exec_function_i(function, *args)
|
|
277
|
-
pl = R::Support.parse2list(*args)
|
|
278
|
-
R::Support.eval("invoke").call(function, pl)
|
|
447
|
+
class << self
|
|
448
|
+
alias_method :exec_function_name, :exec_function
|
|
279
449
|
end
|
|
280
450
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
451
|
+
# Entry point for method_missing: handle setters (x=), eval, or dispatch to R (function / field / method with receiver).
|
|
452
|
+
def self.process_missing(symbol, internal, *args)
|
|
453
|
+
name = self.convert_symbol2r(symbol)
|
|
454
|
+
if ENV['GALAAZ_DEBUG_EVAL'] && name == "expr" && args.size >= 1
|
|
455
|
+
puts "[GALAAZ_DEBUG_EVAL] R.expr(...) called:"
|
|
456
|
+
puts " args[0].class = #{args[0].class}, args[0].inspect = #{args[0].inspect}"
|
|
457
|
+
end
|
|
458
|
+
return process_missing_setter(name, internal, args) if name =~ /(.*)=$/
|
|
459
|
+
return process_missing_eval(name, internal, args) if name == "eval"
|
|
460
|
+
# R.expr(expression_text): build R expression from string/SymbolExprString via parse(text=...), return R::Object.
|
|
461
|
+
if name == "expr" && args.size == 1
|
|
462
|
+
arg = args[0]
|
|
463
|
+
if arg.is_a?(SymbolExprString) || (arg.is_a?(String) && !arg.start_with?("g2_v"))
|
|
464
|
+
str = arg.to_s
|
|
465
|
+
var_name = self.generate_var_name
|
|
466
|
+
R.bridge.eval_r("#{var_name} <- parse(text=#{str.inspect})[[1]]")
|
|
467
|
+
return R::Object.build(var_name)
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
process_missing_dispatch(name, internal, args)
|
|
471
|
+
end
|
|
286
472
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
473
|
+
# Handle obj.var = rhs or R.var = rhs: use `var<-` or `$<-` on R::Object, else .GlobalEnv assignment.
|
|
474
|
+
def self.process_missing_setter(name, internal, args)
|
|
475
|
+
var = name[/^(.*)=$/, 1]
|
|
476
|
+
rhs = self.parse_arg(args[0])
|
|
477
|
+
if internal.is_a?(R::Object)
|
|
478
|
+
handle = internal.r_interop
|
|
479
|
+
r_var = (var == "rclass") ? "class" : var
|
|
480
|
+
R.bridge.eval_r(<<~RCODE)
|
|
481
|
+
#{handle} <- tryCatch(
|
|
482
|
+
`#{r_var}<-`(#{handle}, #{rhs}),
|
|
483
|
+
error = function(e) `$<-`(#{handle}, '#{var}', #{rhs})
|
|
484
|
+
)
|
|
485
|
+
RCODE
|
|
486
|
+
else
|
|
487
|
+
R.bridge.eval_r(".GlobalEnv$#{var} <- #{rhs}")
|
|
488
|
+
end
|
|
290
489
|
end
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
490
|
+
|
|
491
|
+
# Handle obj.eval(env) or R.eval(code): expression in context, or single-arg R.eval.
|
|
492
|
+
def self.process_missing_eval(name, internal, args)
|
|
493
|
+
if internal.is_a?(R::Object) && name == "eval" && args.empty?
|
|
494
|
+
return self.exec_function("eval", internal)
|
|
495
|
+
end
|
|
496
|
+
if internal.is_a?(R::Object) && args.size >= 1
|
|
497
|
+
expr = self.parse_arg(internal)
|
|
498
|
+
env = self.parse_arg(args[0])
|
|
499
|
+
unless expr.start_with?("g2_v") || expr.start_with?("quote(")
|
|
500
|
+
puts "DEBUG: Quoting expression: #{expr}" if ENV['GALAAZ_DEBUG']
|
|
501
|
+
expr = "quote(#{expr})"
|
|
502
|
+
end
|
|
503
|
+
var_name = self.generate_var_name
|
|
504
|
+
r_code = "#{var_name} <- eval(#{expr}, #{env})"
|
|
505
|
+
if ENV['GALAAZ_DEBUG_EVAL']
|
|
506
|
+
puts "[GALAAZ_DEBUG_EVAL] expr.eval(env) path:"
|
|
507
|
+
puts " internal.class = #{internal.class}"
|
|
508
|
+
puts " internal.r_interop = #{internal.r_interop.inspect}" if internal.respond_to?(:r_interop)
|
|
509
|
+
puts " internal.expression = #{internal.expression.inspect}" if internal.respond_to?(:expression)
|
|
510
|
+
puts " parse_arg(internal) => expr = #{expr.inspect}"
|
|
511
|
+
puts " parse_arg(args[0]) => env = #{env.inspect}"
|
|
512
|
+
puts " R code sent: #{r_code}"
|
|
513
|
+
end
|
|
514
|
+
R.bridge.eval_r(r_code)
|
|
515
|
+
return R::Object.build(var_name)
|
|
516
|
+
end
|
|
517
|
+
if args.size == 1
|
|
518
|
+
expr_arg = args[0].is_a?(SymbolExprString) ? args[0].to_s : args[0]
|
|
519
|
+
return self.eval(expr_arg)
|
|
520
|
+
end
|
|
521
|
+
self.exec_function(name, *args)
|
|
303
522
|
end
|
|
304
523
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
# @param object [Ruby Object] the ruby object to which the method is applied, false if
|
|
311
|
-
# it is not applied to an object
|
|
312
|
-
#----------------------------------------------------------------------------------------
|
|
524
|
+
# Dispatch: R module (eval handle/namespace or exec_function) or R::Object (length / function / field / fallback).
|
|
525
|
+
def self.process_missing_dispatch(name, internal, args)
|
|
526
|
+
if internal == R && args.empty? && (name.include?("::") || name.start_with?("g2_v"))
|
|
527
|
+
return R::Object.build(name)
|
|
528
|
+
end
|
|
313
529
|
|
|
314
|
-
|
|
530
|
+
# Phase 3: R.foo(...) — +internal+ is false/true from R.method_missing, not an R::Object. No per-handle
|
|
531
|
+
# field vs function ambiguity; go straight to function execution.
|
|
532
|
+
unless internal.is_a?(R::Object)
|
|
533
|
+
return self.exec_function(name, *args)
|
|
534
|
+
end
|
|
315
535
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
R
|
|
536
|
+
handle = internal.r_interop
|
|
537
|
+
return self.exec_function("length", internal, *args) if name == "length"
|
|
538
|
+
|
|
539
|
+
# Prefer component/field access (obj.beta => obj[["beta"]]) over calling a global function (beta()).
|
|
540
|
+
# Use [[ instead of $ so we avoid "$ operator is invalid for atomic vectors" when receiver is atomic;
|
|
541
|
+
# [[ on list/data.frame/env returns the element; result protocol maps NA/NULL as appropriate.
|
|
542
|
+
begin
|
|
543
|
+
R.bridge.log_connections_in_r if ENV["GALAAZ_DEBUG_R"].to_s == "1" || ENV["GALAAZ_DEBUG_R"].to_s == "true"
|
|
544
|
+
R.bridge.log_object_in_r(handle) if ENV["GALAAZ_DEBUG_R"].to_s == "1" || ENV["GALAAZ_DEBUG_R"].to_s == "true" || ENV["GALAAZ_DEBUG_OBJECT"].to_s == "1" || ENV["GALAAZ_DEBUG_OBJECT"].to_s == "true"
|
|
545
|
+
rescue StandardError => e
|
|
546
|
+
# Debug dump must not block the is_field check (e.g. if R throws "invalid connection" when inspecting the object).
|
|
547
|
+
File.open(R.bridge.log_path("galaaz_obj_debug.log"), "a") { |f| f.puts "[#{Time.now.strftime('%H:%M:%S.%L')}] log_object_in_r(#{handle}) failed: #{e.message}" }
|
|
548
|
+
end
|
|
549
|
+
# In callback, eval_r can fail with "invalid connection" - wrap in begin/rescue
|
|
550
|
+
is_field = false
|
|
551
|
+
is_func = false
|
|
552
|
+
if R.bridge.respond_to?(:dispatch_probe)
|
|
553
|
+
# Environments are mutable (rm, assign); do not cache probe results — stale is_field breaks semantics.
|
|
554
|
+
cache_probe = !internal.is_a?(::R::Environment)
|
|
555
|
+
probe_key = dispatch_probe_cache_key(handle, name)
|
|
556
|
+
cached_probe = nil
|
|
557
|
+
if cache_probe
|
|
558
|
+
@dispatch_probe_cache_mx.synchronize do
|
|
559
|
+
cached_probe = @dispatch_probe_handle_cache[probe_key]
|
|
560
|
+
@dispatch_probe_cache_hits += 1 if cached_probe
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
if cached_probe
|
|
564
|
+
is_field = !!cached_probe[:is_field]
|
|
565
|
+
is_func = !!cached_probe[:is_func]
|
|
566
|
+
else
|
|
567
|
+
begin
|
|
568
|
+
probe = R.bridge.dispatch_probe(handle, name)
|
|
569
|
+
is_field = !!probe[:is_field]
|
|
570
|
+
is_func = !!probe[:is_func]
|
|
571
|
+
if cache_probe
|
|
572
|
+
@dispatch_probe_cache_mx.synchronize do
|
|
573
|
+
@dispatch_probe_cache_misses += 1
|
|
574
|
+
end
|
|
575
|
+
store_dispatch_probe_handle_cache(probe_key, is_field, is_func)
|
|
576
|
+
end
|
|
577
|
+
@dispatch_probe_cache[:func][name] = is_func
|
|
578
|
+
rescue StandardError => e
|
|
579
|
+
raise unless e.message.include?("invalid connection") || e.message.include?("invalid dispatch_probe params")
|
|
580
|
+
is_field = false
|
|
581
|
+
is_func = false
|
|
582
|
+
end
|
|
583
|
+
end
|
|
325
584
|
else
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
585
|
+
is_field = begin
|
|
586
|
+
R.bridge.eval_r("isTRUE('#{name}' %in% names(#{handle})) || (is.environment(#{handle}) && isTRUE(exists('#{name}', envir = #{handle}, inherits = FALSE)))") == "[1] TRUE"
|
|
587
|
+
rescue RuntimeError => e
|
|
588
|
+
e.message.include?("invalid connection") ? false : raise
|
|
589
|
+
end
|
|
590
|
+
if is_field
|
|
591
|
+
res = self.exec_function_name("`[[`", internal, name)
|
|
592
|
+
return res.call(*args) if !args.empty? && res.respond_to?(:call)
|
|
593
|
+
return res
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
is_func = begin
|
|
597
|
+
cached = @dispatch_probe_cache[:func][name]
|
|
598
|
+
if cached.nil?
|
|
599
|
+
cached = (R.bridge.eval_r("is.function(try(get('#{name}'), silent=TRUE))") == "[1] TRUE")
|
|
600
|
+
@dispatch_probe_cache[:func][name] = cached
|
|
601
|
+
end
|
|
602
|
+
cached
|
|
603
|
+
rescue RuntimeError => e
|
|
604
|
+
e.message.include?("invalid connection") ? false : raise
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
if is_field
|
|
608
|
+
res = self.exec_function_name("`[[`", internal, name)
|
|
609
|
+
return res.call(*args) if !args.empty? && res.respond_to?(:call)
|
|
610
|
+
return res
|
|
329
611
|
end
|
|
330
612
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
#----------------------------------------------------------------------------------------
|
|
334
|
-
#
|
|
335
|
-
#----------------------------------------------------------------------------------------
|
|
613
|
+
return self.exec_function(name, internal, *args) if is_func
|
|
336
614
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
puts lst
|
|
341
|
-
=begin
|
|
342
|
-
(1..lst.length >> 0).each do |i|
|
|
343
|
-
puts lst[[i]]
|
|
615
|
+
# Environment: missing name should raise NoMethodError (like Ruby), not call name(env) in R.
|
|
616
|
+
if internal.is_a?(::R::Environment)
|
|
617
|
+
::Kernel.raise(::NoMethodError, "undefined method `#{name}' for #{internal.inspect}")
|
|
344
618
|
end
|
|
345
|
-
|
|
619
|
+
|
|
620
|
+
self.exec_function(name, internal, *args)
|
|
346
621
|
end
|
|
347
622
|
|
|
348
|
-
#----------------------------------------------------------------------------------------
|
|
349
|
-
# Prints a foreign R interop pointer. Used for debug.
|
|
350
|
-
#----------------------------------------------------------------------------------------
|
|
351
623
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
624
|
+
@callbacks = {}
|
|
625
|
+
@ruby_objects = {}
|
|
626
|
+
@ruby_obj_id = 0
|
|
627
|
+
@ruby_object_id = 0
|
|
628
|
+
|
|
629
|
+
# Store a Ruby object for passing to R; returns handle string "rb_obj_<id>" for use in R.
|
|
630
|
+
def self.register_ruby_object(obj)
|
|
631
|
+
@ruby_object_id += 1
|
|
632
|
+
id = @ruby_object_id
|
|
633
|
+
@ruby_objects[id] = obj
|
|
634
|
+
"rb_obj_#{id}"
|
|
356
635
|
end
|
|
357
636
|
|
|
358
|
-
|
|
359
|
-
|
|
637
|
+
# Register a Proc/Method to be invoked from R (e.g. in outer()); returns callback id for the R stub.
|
|
638
|
+
def self.register_callback(proc)
|
|
639
|
+
@ruby_obj_id += 1
|
|
640
|
+
id = @ruby_obj_id
|
|
641
|
+
@callbacks[id] = proc
|
|
642
|
+
id
|
|
360
643
|
end
|
|
361
|
-
|
|
362
|
-
#----------------------------------------------------------------------------------------
|
|
363
|
-
#
|
|
364
|
-
#----------------------------------------------------------------------------------------
|
|
365
|
-
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
end
|
|
369
644
|
|
|
370
|
-
|
|
371
|
-
|
|
645
|
+
# Retrieve the Ruby proc registered for a callback id (from --G_CALLBACK--id--...).
|
|
646
|
+
def self.get_callback(id)
|
|
647
|
+
@callbacks[id.to_i]
|
|
648
|
+
end
|
|
372
649
|
|
|
650
|
+
# Retrieve the Ruby object for a handle "rb_obj_<id>" returned from R.
|
|
651
|
+
def self.get_ruby_object(id_str)
|
|
652
|
+
id = id_str.sub("rb_obj_", "").to_i
|
|
653
|
+
@ruby_objects[id]
|
|
654
|
+
end
|
|
373
655
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
# '=~' operator, then a formula should be constructed, if not, then an quo should be
|
|
378
|
-
# constructed
|
|
379
|
-
# @param expression [R::Expression]
|
|
380
|
-
# @return quo or formula
|
|
381
|
-
#----------------------------------------------------------------------------------------
|
|
656
|
+
# Collects assignment strings for R.batch (Phase 4).
|
|
657
|
+
class BatchCollector
|
|
658
|
+
attr_reader :ops
|
|
382
659
|
|
|
383
|
-
|
|
660
|
+
def initialize
|
|
661
|
+
@ops = []
|
|
662
|
+
end
|
|
384
663
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
# add a '~' before the expression so we can parse it as an
|
|
389
|
-
# R expression from a string. Didn't find a good way of creating an
|
|
390
|
-
# expression from string.
|
|
391
|
-
# R.as_quosure(R.as__formula("~ #{expression.infix}"))
|
|
392
|
-
R.rhs(R.as__formula("~ #{expression.infix}"))
|
|
664
|
+
# Same form as a single bridge eval_with_result assignment, e.g. "#{vn} <- 1L".
|
|
665
|
+
def eval_with_result(assignment_code)
|
|
666
|
+
@ops << assignment_code.to_s
|
|
393
667
|
end
|
|
394
|
-
|
|
395
668
|
end
|
|
396
|
-
|
|
669
|
+
|
|
670
|
+
def self.batch_eval_with_result(assignment_codes)
|
|
671
|
+
R.bridge.batch_eval_r_with_result(Array(assignment_codes))
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
require_relative 'r_module_s'
|
|
677
|
+
require_relative 'rsupport_scope'
|