1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::cmp;
use std::mem;
use crate::convert::{FromMrb, RustBackedValue, TryFromMrb};
use crate::extn::core::matchdata::MatchData;
use crate::extn::core::regexp::Regexp;
use crate::sys;
use crate::value::Value;
use crate::Mrb;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Error {
Fatal,
NoImplicitConversionToString,
}
#[derive(Debug, Clone)]
pub struct Args {
pub string: Option<String>,
}
impl Args {
const ARGSPEC: &'static [u8] = b"o\0";
pub unsafe fn extract(interp: &Mrb) -> Result<Self, Error> {
let mut string = <mem::MaybeUninit<sys::mrb_value>>::uninit();
sys::mrb_get_args(
interp.borrow().mrb,
Self::ARGSPEC.as_ptr() as *const i8,
string.as_mut_ptr(),
);
let string = string.assume_init();
if let Ok(string) = <Option<String>>::try_from_mrb(interp, Value::new(interp, string)) {
Ok(Self { string })
} else {
Err(Error::NoImplicitConversionToString)
}
}
}
pub fn method(interp: &Mrb, args: Args, value: &Value) -> Result<Value, Error> {
let mrb = interp.borrow().mrb;
let data = unsafe { Regexp::try_from_ruby(interp, value) }.map_err(|_| Error::Fatal)?;
let string = if let Some(string) = args.string {
string
} else {
unsafe {
sys::mrb_gv_set(
mrb,
interp.borrow_mut().sym_intern("$~"),
sys::mrb_sys_nil_value(),
);
return Ok(Value::from_mrb(interp, false));
}
};
let borrow = data.borrow();
let regex = (*borrow.regex).as_ref().ok_or(Error::Fatal)?;
let matchdata = if let Some(captures) = regex.captures(string.as_str()) {
let num_regexp_globals_to_set = {
let num_previously_set_globals = interp.borrow().num_set_regexp_capture_globals;
cmp::max(num_previously_set_globals, captures.len())
};
for group in 0..num_regexp_globals_to_set {
let sym = if group == 0 {
interp.borrow_mut().sym_intern("$&")
} else {
interp.borrow_mut().sym_intern(&format!("${}", group))
};
let value = Value::from_mrb(&interp, captures.at(group));
unsafe {
sys::mrb_gv_set(mrb, sym, value.inner());
}
}
interp.borrow_mut().num_set_regexp_capture_globals = captures.len();
if let Some(match_pos) = captures.pos(0) {
let pre_match = &string[..match_pos.0];
let post_match = &string[match_pos.1..];
unsafe {
let pre_match_sym = interp.borrow_mut().sym_intern("$`");
sys::mrb_gv_set(
mrb,
pre_match_sym,
Value::from_mrb(interp, pre_match).inner(),
);
let post_match_sym = interp.borrow_mut().sym_intern("$'");
sys::mrb_gv_set(
mrb,
post_match_sym,
Value::from_mrb(interp, post_match).inner(),
);
}
}
let matchdata = MatchData::new(string.as_str(), borrow.clone(), 0, string.len());
unsafe { matchdata.try_into_ruby(&interp, None) }.map_err(|_| Error::Fatal)?
} else {
unsafe {
let pre_match_sym = interp.borrow_mut().sym_intern("$`");
sys::mrb_gv_set(
mrb,
pre_match_sym,
Value::from_mrb(interp, None::<Value>).inner(),
);
let post_match_sym = interp.borrow_mut().sym_intern("$'");
sys::mrb_gv_set(
mrb,
post_match_sym,
Value::from_mrb(interp, None::<Value>).inner(),
);
}
Value::from_mrb(interp, None::<Value>)
};
unsafe {
sys::mrb_gv_set(mrb, interp.borrow_mut().sym_intern("$~"), matchdata.inner());
}
Ok(Value::from_mrb(&interp, !unsafe {
sys::mrb_sys_value_is_nil(matchdata.inner())
}))
}