Skip to content

Commit a9868b6

Browse files
committed
Use JSON to detect real typing
1 parent 16be278 commit a9868b6

File tree

6 files changed

+183
-163
lines changed

6 files changed

+183
-163
lines changed

.cargo/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ rustflags = [
33
"-C",
44
"link-args=-o out.js",
55
"-C",
6-
"link-args=-s EXPORTED_FUNCTIONS=['_eval_ruby_script_int','_eval_ruby_script_float','_eval_ruby_script_string','_eval_ruby_script_bool','_main']",
6+
"link-args=-s EXPORTED_FUNCTIONS=['_eval_ruby_script_returning_json','_eval_ruby_script_returning_json1','_main']",
77
"-C",
88
"link-args=-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString']",
99
"-C",

Cargo.lock

Lines changed: 69 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ edition = "2024"
55

66
[dependencies]
77
mrubyedge = { version = "1.1.8", default-features = false }
8-
mruby-compiler2-sys = { path = "/Users/udzura/ghq/github.com/mrubyedge/mruby-compiler2-sys", default-features = false }
9-
# mruby-compiler2-sys = { version = "0.3.0", default-features = false }
8+
mruby-compiler2-sys = { version = "0.4.0", default-features = false }
109
mrubyedge-math = "0.1.0"
10+
mrubyedge-serde-json = "0.1.0"
1111

1212
[profile.dev]
1313
# FIXME: unccomment

combined.js

Lines changed: 33 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

main.js

Lines changed: 32 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,38 @@
11
/**
22
* eval ruby script and return integer value
3-
* @param {string} input ruby code.
3+
* Arguments are accessible as $arg1, $arg2, etc. in Ruby code.
4+
* @param {string} text ruby code.
5+
* @param {...(string|number|Array<Array<string|number>>)} args arguments accessible as $arg1, $arg2, etc. (converted to string).
46
* @return The integer result of the Ruby script.
57
* @customfunction
68
*/
7-
function EVAL_RUBY_SCRIPT_INT(text) {
8-
return Module.ccall(
9-
'eval_ruby_script_int',
10-
'number',
11-
['string'],
12-
[text]
13-
);
14-
}
15-
16-
/**
17-
* eval ruby script and return float value
18-
* @param {string} input ruby code.
19-
* @return The float result of the Ruby script.
20-
* @customfunction
21-
*/
22-
function EVAL_RUBY_SCRIPT_FLOAT(text) {
23-
return Module.ccall(
24-
'eval_ruby_script_float',
25-
'number',
26-
['string'],
27-
[text]
28-
);
29-
}
30-
31-
/**
32-
* eval ruby script and return boolean value
33-
* @param {string} input ruby code.
34-
* @return The boolean result of the Ruby script.
35-
* @customfunction
36-
*/
37-
function EVAL_RUBY_SCRIPT_BOOL(text) {
38-
var result = Module.ccall(
39-
'eval_ruby_script_bool',
40-
'number',
41-
['string'],
42-
[text]
43-
);
44-
return result !== 0;
45-
}
46-
47-
/**
48-
* eval ruby script and return string value
49-
* @param {string} input ruby code.
50-
* @return The string result of the Ruby script.
51-
* @customfunction
52-
*/
53-
function EVAL_RUBY_SCRIPT_STRING(text) {
54-
var ptr = Module.ccall(
55-
'eval_ruby_script_string',
56-
'number',
57-
['string'],
58-
[text]
59-
);
60-
if (ptr === 0) {
61-
return "";
9+
function EVAL_RUBY_SCRIPT(text) {
10+
var args = Array.prototype.slice.call(arguments, 1);
11+
if (args.length === 0) {
12+
const result = Module.ccall(
13+
'eval_ruby_script_returning_json',
14+
'string',
15+
['string'],
16+
[text]
17+
);
18+
return JSON.parse(result);
19+
} else if (args.length === 1) {
20+
const result = Module.ccall(
21+
'eval_ruby_script_returning_json1',
22+
'string',
23+
['string', 'string'],
24+
[text, JSON.stringify(args[0])]
25+
);
26+
return JSON.parse(result);
27+
} else {
28+
// For now, only support up to 1 argument
29+
// TODO: Add support for more arguments
30+
const result = Module.ccall(
31+
'eval_ruby_script_returning_json1',
32+
'string',
33+
['string', 'string'],
34+
[text, JSON.stringify(args[0])]
35+
);
36+
return JSON.parse(result);
6237
}
63-
return Module.UTF8ToString(ptr);
64-
}
38+
}

src/main.rs

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ use std::rc::Rc;
55

66
use mruby_compiler2_sys::MRubyCompiler2Context;
77
use mrubyedge::yamrb::value::RObject;
8+
use mrubyedge::yamrb::vm::VM;
89

910
fn main() {}
1011

1112
/// Execute Ruby script and return the result as Rc<RObject>
1213
/// Returns None if compilation or execution fails
1314
fn eval_ruby_script(text: &str) -> Option<Rc<RObject>> {
15+
eval_ruby_script_with_setup(text, |_| {})
16+
}
17+
18+
/// Execute Ruby script with VM setup callback
19+
/// The callback can be used to set global variables before execution
20+
fn eval_ruby_script_with_setup<F>(text: &str, setup: F) -> Option<Rc<RObject>>
21+
where
22+
F: FnOnce(&mut VM),
23+
{
1424
let mut context = unsafe { MRubyCompiler2Context::new() };
1525

1626
// Compile the Ruby script
@@ -35,14 +45,18 @@ fn eval_ruby_script(text: &str) -> Option<Rc<RObject>> {
3545
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
3646
mrubyedge_math::init_math(&mut vm);
3747

48+
// Call setup callback to configure VM (e.g., set global variables)
49+
setup(&mut vm);
50+
3851
// Execute the script and handle exceptions
39-
match vm.run() {
40-
Ok(r) => Some(r),
41-
Err(e) => {
42-
eprintln!("Runtime error: {:?}", e);
43-
None
44-
}
52+
let result = vm.run();
53+
if let Err(e) = result {
54+
eprintln!("Runtime error: {:?}", e);
55+
return None;
4556
}
57+
mrubyedge_serde_json::mrb_json_class_dump(&mut vm, &[result.unwrap()])
58+
.unwrap_or_else(|_| RObject::string("null".to_string()).to_refcount_assigned())
59+
.into()
4660
}
4761

4862
/// Convert C string pointer to Rust &str
@@ -51,56 +65,46 @@ unsafe fn cstr_to_str<'a>(text_ptr: *const c_char) -> &'a str {
5165
c_str.to_str().unwrap_or("")
5266
}
5367

54-
// Function called from JavaScript
55-
// Receives Ruby script, executes it, and returns integer result
56-
#[unsafe(no_mangle)]
57-
pub extern "C" fn eval_ruby_script_int(text_ptr: *const c_char) -> i32 {
58-
unsafe {
59-
let text = cstr_to_str(text_ptr);
60-
match eval_ruby_script(text) {
61-
Some(result) => (&*result).try_into().unwrap_or(-1),
62-
None => -1,
63-
}
64-
}
65-
}
66-
67-
// Receives Ruby script, executes it, and returns float result
68-
#[unsafe(no_mangle)]
69-
pub extern "C" fn eval_ruby_script_float(text_ptr: *const c_char) -> f64 {
70-
unsafe {
71-
let text = cstr_to_str(text_ptr);
72-
match eval_ruby_script(text) {
73-
Some(result) => (&*result).try_into().unwrap_or(f64::NAN),
74-
None => f64::NAN,
75-
}
76-
}
77-
}
68+
// Receives Ruby script, executes it, and returns string result
69+
// Returns a pointer to a null-terminated C string (caller should NOT free it)
70+
// The returned string is valid until the next call to this function
71+
static mut LAST_STRING_RESULT: Option<CString> = None;
7872

79-
// Receives Ruby script, executes it, and returns boolean result (0 or 1)
8073
#[unsafe(no_mangle)]
81-
pub extern "C" fn eval_ruby_script_bool(text_ptr: *const c_char) -> i32 {
74+
pub extern "C" fn eval_ruby_script_returning_json(text_ptr: *const c_char) -> *const c_char {
8275
unsafe {
8376
let text = cstr_to_str(text_ptr);
8477
match eval_ruby_script(text) {
8578
Some(result) => {
86-
let b: bool = (&*result).try_into().unwrap_or(false);
87-
if b { 1 } else { 0 }
79+
let s: String = (&*result).try_into().unwrap_or_default();
80+
match CString::new(s) {
81+
Ok(cstring) => {
82+
let ptr = cstring.as_ptr();
83+
LAST_STRING_RESULT = Some(cstring);
84+
ptr
85+
}
86+
Err(_) => std::ptr::null(),
87+
}
8888
}
89-
None => 0,
89+
None => std::ptr::null(),
9090
}
9191
}
9292
}
9393

94-
// Receives Ruby script, executes it, and returns string result
95-
// Returns a pointer to a null-terminated C string (caller should NOT free it)
96-
// The returned string is valid until the next call to this function
97-
static mut LAST_STRING_RESULT: Option<CString> = None;
98-
9994
#[unsafe(no_mangle)]
100-
pub extern "C" fn eval_ruby_script_string(text_ptr: *const c_char) -> *const c_char {
95+
pub extern "C" fn eval_ruby_script_returning_json1(
96+
text_ptr: *const c_char,
97+
arg1_ptr: *const c_char,
98+
) -> *const c_char {
10199
unsafe {
102100
let text = cstr_to_str(text_ptr);
103-
match eval_ruby_script(text) {
101+
let arg1 = cstr_to_str(arg1_ptr);
102+
match eval_ruby_script_with_setup(text, |vm| {
103+
let arg1_str = RObject::string(arg1.to_string()).to_refcount_assigned();
104+
let arg1_json = mrubyedge_serde_json::mrb_json_class_load(vm, &[arg1_str])
105+
.unwrap_or_else(|_| RObject::nil().to_refcount_assigned());
106+
vm.globals.insert("$arg1".to_string(), arg1_json);
107+
}) {
104108
Some(result) => {
105109
let s: String = (&*result).try_into().unwrap_or_default();
106110
match CString::new(s) {

0 commit comments

Comments
 (0)