haskell_match 0.1.0 → 0.2.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 +25 -1
- data/README.md +7 -4
- data/ext/haskell_match/Cargo.lock +49 -3
- data/ext/haskell_match/Cargo.toml +3 -3
- data/ext/haskell_match/src/lib.rs +0 -2
- data/ext/haskell_match/src/ruby/error.rs +123 -0
- data/ext/haskell_match/src/ruby/mod.rs +165 -551
- data/ext/haskell_match/src/ruby/native.rs +227 -0
- data/ext/haskell_match/src/ruby/state.rs +91 -0
- data/lib/haskell_match/version.rb +1 -1
- metadata +18 -1
|
@@ -1,45 +1,50 @@
|
|
|
1
|
-
//! Ruby-facing API: the `HaskellMatch::Native` module
|
|
2
|
-
//! `
|
|
1
|
+
//! Ruby-facing API: the `HaskellMatch::Native` module (`native`, declared
|
|
2
|
+
//! with `#[rubymodule]`), the exceptions it raises (`error`, declared with
|
|
3
|
+
//! `#[rubyerror]`) and the `HaskellMatch::Native::Matcher` class defined
|
|
4
|
+
//! here.
|
|
3
5
|
//!
|
|
4
|
-
//! Matcher
|
|
5
|
-
//!
|
|
6
|
+
//! `Matcher` keeps a hand-written typed-data definition rather than
|
|
7
|
+
//! `#[rubyclass]`: it sets `RUBY_TYPED_FROZEN_SHAREABLE`, which a frozen
|
|
8
|
+
//! matcher (and the `Function` objects built on it) needs to pass
|
|
9
|
+
//! `Ractor.make_shareable`, and the derive data type has no way to add that
|
|
10
|
+
//! flag. Its hot-path methods (`call`, `prepare`, `select`, `select_with`,
|
|
11
|
+
//! `run`) are raw `extern "C"` functions (argc/argv style) so that a call
|
|
6
12
|
//! allocates nothing on the Rust side and clause bodies are invoked from a
|
|
7
13
|
//! frame that owns no values needing `Drop` (a body may leave by exception,
|
|
8
|
-
//! `throw`, `break` or `return`).
|
|
14
|
+
//! `throw`, `break` or `return`). The remaining methods go through
|
|
15
|
+
//! `methods!` and return `Result`s, raised once their values are dropped.
|
|
9
16
|
|
|
17
|
+
pub mod error;
|
|
18
|
+
pub mod native;
|
|
10
19
|
pub mod runtime;
|
|
20
|
+
pub mod state;
|
|
11
21
|
|
|
12
22
|
use std::ffi::CString;
|
|
13
23
|
use std::os::raw::c_int;
|
|
14
24
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
15
|
-
use std::sync::{
|
|
25
|
+
use std::sync::{LazyLock, OnceLock};
|
|
16
26
|
|
|
27
|
+
use rutie::derive::IntoException;
|
|
17
28
|
use rutie::rubysys::typed_data::{RUBY_TYPED_FREE_IMMEDIATELY, RUBY_TYPED_FROZEN_SHAREABLE};
|
|
18
29
|
use rutie::rubysys::{array, class as rclass, rstruct, value::ValueType};
|
|
19
30
|
use rutie::typed_data::DataTypeWrapper;
|
|
20
31
|
use rutie::types::{CallbackPtr, DataType, DataTypeFunction, Id, Value};
|
|
21
32
|
use rutie::{
|
|
22
|
-
AnyException, AnyObject, Array, Boolean, Class, Exception, Integer, Module,
|
|
23
|
-
RString, VM,
|
|
33
|
+
AnyException, AnyObject, Array, Boolean, Class, Exception, Integer, Module, Object, RString, VM,
|
|
24
34
|
};
|
|
25
35
|
|
|
26
36
|
use crate::core::error::{CoreError, ErrorKind};
|
|
27
37
|
use crate::core::exhaust;
|
|
28
|
-
use crate::core::parser::
|
|
29
|
-
use crate::core::pretty::
|
|
38
|
+
use crate::core::parser::parse_pattern;
|
|
39
|
+
use crate::core::pretty::render_witness;
|
|
30
40
|
use crate::core::resolve::resolve_clause;
|
|
31
41
|
use crate::core::tree;
|
|
32
42
|
use crate::core::typecheck;
|
|
33
|
-
use crate::core::types::
|
|
43
|
+
use crate::core::types::TypeEnv;
|
|
44
|
+
use error::{argument_error, HmError};
|
|
34
45
|
use runtime::{Bound, Matcher, RtError, STACK_BINDS};
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
/// Type scopes: index 0 is the global registry; every other scope is a
|
|
38
|
-
/// snapshot of the global one at its creation plus its own declarations
|
|
39
|
-
/// (a compiled Haskell module gets one, like a Haskell module namespace).
|
|
40
|
-
static ref SCOPES: Mutex<Vec<TypeEnv>> = Mutex::new(vec![TypeEnv::new()]);
|
|
41
|
-
static ref MATCHER_TYPE: MatcherType = MatcherType::new();
|
|
42
|
-
}
|
|
47
|
+
static MATCHER_TYPE: LazyLock<MatcherType> = LazyLock::new(MatcherType::new);
|
|
43
48
|
|
|
44
49
|
/// Interned ivar names read on every call.
|
|
45
50
|
struct Ids {
|
|
@@ -55,7 +60,7 @@ static TAIL_CALL: OnceLock<usize> = OnceLock::new();
|
|
|
55
60
|
|
|
56
61
|
fn tail_call_class() -> usize {
|
|
57
62
|
*TAIL_CALL.get_or_init(|| {
|
|
58
|
-
let k =
|
|
63
|
+
let k = Module::from_existing("HaskellMatch").get_nested_class("TailCall");
|
|
59
64
|
unsafe { rutie::rubysys::gc::rb_gc_register_mark_object(k.value()) };
|
|
60
65
|
k.value().value
|
|
61
66
|
})
|
|
@@ -79,26 +84,6 @@ extern "C" {
|
|
|
79
84
|
fn rb_fiber_resume(fiber: Value, argc: c_int, argv: *const Value) -> Value;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
thread_local! {
|
|
83
|
-
/// Nesting depth of native calls on this thread.
|
|
84
|
-
static DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/// Every this many nested native calls, the next clause body runs in a fresh
|
|
88
|
-
/// Fiber. Each Fiber brings its own VM stack and machine stack, so chained
|
|
89
|
-
/// segments make the recursion depth a matter of memory rather than of
|
|
90
|
-
/// Ruby's fixed stack size. 0 disables segmentation.
|
|
91
|
-
static STACK_SEGMENT: std::sync::atomic::AtomicUsize =
|
|
92
|
-
std::sync::atomic::AtomicUsize::new(DEFAULT_STACK_SEGMENT);
|
|
93
|
-
const DEFAULT_STACK_SEGMENT: usize = 100;
|
|
94
|
-
|
|
95
|
-
/// Maximum nesting depth of native calls before `StackOverflowError` is
|
|
96
|
-
/// raised, so a runaway recursion fails instead of consuming all memory
|
|
97
|
-
/// (GHC has the same kind of limit). 0 means unlimited.
|
|
98
|
-
static MAX_DEPTH: std::sync::atomic::AtomicUsize =
|
|
99
|
-
std::sync::atomic::AtomicUsize::new(DEFAULT_MAX_DEPTH);
|
|
100
|
-
const DEFAULT_MAX_DEPTH: usize = 250_000;
|
|
101
|
-
|
|
102
87
|
rutie_callback! {
|
|
103
88
|
/// Body of a stack-segment Fiber: `data` is `[body, values]`.
|
|
104
89
|
fn segment_fiber_body(
|
|
@@ -116,24 +101,20 @@ rutie_callback! {
|
|
|
116
101
|
}
|
|
117
102
|
}
|
|
118
103
|
|
|
119
|
-
/// Invoke a clause body, either directly or (every `
|
|
104
|
+
/// Invoke a clause body, either directly or (every `stack_segment` levels)
|
|
120
105
|
/// in a fresh Fiber. Exceptions and non-local jumps are returned as the
|
|
121
106
|
/// `rb_protect` state for the caller to resume once it has cleaned up.
|
|
107
|
+
///
|
|
108
|
+
/// # Safety
|
|
109
|
+
/// Must run on a Ruby thread holding the GVL with no Rust values needing
|
|
110
|
+
/// `Drop` live in the caller (a `StackOverflowError` is raised from here).
|
|
122
111
|
unsafe fn invoke_body(body: Value, bound: &Bound, buf: &[Value]) -> Result<Value, i32> {
|
|
123
|
-
let
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
&format!(
|
|
130
|
-
"recursion deeper than HaskellMatch.max_depth ({}); use tail calls, defer, or raise the limit",
|
|
131
|
-
max
|
|
132
|
-
),
|
|
133
|
-
));
|
|
134
|
-
}
|
|
135
|
-
DEPTH.with(|d| d.set(depth + 1));
|
|
136
|
-
let result = if segment > 0 && (depth + 1).is_multiple_of(segment) {
|
|
112
|
+
let depth = match state::depth_push() {
|
|
113
|
+
Ok(depth) => depth,
|
|
114
|
+
Err(e) => raise(e),
|
|
115
|
+
};
|
|
116
|
+
let segment = state::stack_segment();
|
|
117
|
+
let result = if segment > 0 && depth.is_multiple_of(segment) {
|
|
137
118
|
let vals = runtime::bound_to_array(bound, buf);
|
|
138
119
|
let pair = [body, vals];
|
|
139
120
|
let data = array::rb_ary_new_from_values(2, pair.as_ptr());
|
|
@@ -145,13 +126,14 @@ unsafe fn invoke_body(body: Value, bound: &Bound, buf: &[Value]) -> Result<Value
|
|
|
145
126
|
} else {
|
|
146
127
|
runtime::call_protected(|| runtime::invoke(body, bound, buf))
|
|
147
128
|
};
|
|
148
|
-
|
|
129
|
+
state::set_depth(depth - 1);
|
|
149
130
|
result
|
|
150
131
|
}
|
|
151
132
|
|
|
152
133
|
/// Typed-data definition for `Matcher`. Unlike Rutie's `wrappable_struct!`
|
|
153
|
-
/// it sets `RUBY_TYPED_FROZEN_SHAREABLE`, so a frozen
|
|
154
|
-
/// `Function` objects built on it) can be passed to
|
|
134
|
+
/// and `#[rubyclass]` it sets `RUBY_TYPED_FROZEN_SHAREABLE`, so a frozen
|
|
135
|
+
/// matcher (and the `Function` objects built on it) can be passed to
|
|
136
|
+
/// `Ractor.make_shareable`.
|
|
155
137
|
pub struct MatcherType {
|
|
156
138
|
data_type: DataType,
|
|
157
139
|
}
|
|
@@ -185,60 +167,21 @@ impl DataTypeWrapper<Matcher> for MatcherType {
|
|
|
185
167
|
}
|
|
186
168
|
}
|
|
187
169
|
|
|
188
|
-
class!(RMatcher);
|
|
189
|
-
module!(Native);
|
|
170
|
+
class!(RMatcher, "HaskellMatch::Native::Matcher");
|
|
190
171
|
|
|
191
172
|
// ---------------------------------------------------------------------------
|
|
192
|
-
// error helpers
|
|
173
|
+
// error helpers for the raw methods
|
|
193
174
|
// ---------------------------------------------------------------------------
|
|
194
175
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
fn exception(class_name: &str, message: &str) -> AnyException {
|
|
200
|
-
AnyException::from_class(&hm_class(class_name), message)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/// A scope id argument (raises ArgumentError when malformed).
|
|
204
|
-
fn scope_arg(scope: Result<Integer, rutie::AnyException>) -> usize {
|
|
205
|
-
let n = scope
|
|
206
|
-
.unwrap_or_else(|_| raise_arg("scope must be an Integer"))
|
|
207
|
-
.to_i64();
|
|
208
|
-
if n < 0 {
|
|
209
|
-
raise_arg("scope must not be negative");
|
|
210
|
-
}
|
|
211
|
-
n as usize
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/// The type environment of scope `id`.
|
|
215
|
-
fn scope_env(scopes: &mut [TypeEnv], id: usize) -> Result<&mut TypeEnv, String> {
|
|
216
|
-
scopes
|
|
217
|
-
.get_mut(id)
|
|
218
|
-
.ok_or_else(|| format!("unknown type scope {}", id))
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
fn error_class(kind: ErrorKind) -> &'static str {
|
|
222
|
-
match kind {
|
|
223
|
-
ErrorKind::Syntax => "PatternSyntaxError",
|
|
224
|
-
ErrorKind::UnknownConstructor => "UnknownConstructorError",
|
|
225
|
-
ErrorKind::Arity => "ArityError",
|
|
226
|
-
ErrorKind::Type => "PatternTypeError",
|
|
227
|
-
ErrorKind::DuplicateVariable => "DuplicateVariableError",
|
|
228
|
-
ErrorKind::Field => "FieldError",
|
|
229
|
-
ErrorKind::DataDeclaration => "DataDeclarationError",
|
|
230
|
-
ErrorKind::ClauseArity => "ClauseArityError",
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
fn raise_core(e: CoreError) -> ! {
|
|
235
|
-
VM::raise_ex(exception(error_class(e.kind), &e.message));
|
|
176
|
+
/// Raise `e` from a raw method. Callers hold no Rust values needing `Drop`
|
|
177
|
+
/// at this point: `VM::raise_ex` `longjmp`s past the frame.
|
|
178
|
+
fn raise(e: impl IntoException) -> ! {
|
|
179
|
+
VM::raise_ex(e.into_exception());
|
|
236
180
|
unreachable!()
|
|
237
181
|
}
|
|
238
182
|
|
|
239
183
|
fn raise_arg(msg: &str) -> ! {
|
|
240
|
-
|
|
241
|
-
unreachable!()
|
|
184
|
+
raise(argument_error(msg))
|
|
242
185
|
}
|
|
243
186
|
|
|
244
187
|
fn inspect(v: Value) -> String {
|
|
@@ -254,41 +197,33 @@ fn class_name(v: Value) -> String {
|
|
|
254
197
|
.unwrap_or_else(|| "?".into())
|
|
255
198
|
}
|
|
256
199
|
|
|
200
|
+
/// Report a failed match from a raw method: a `HaskellMatch` exception, an
|
|
201
|
+
/// `ArgumentError`, or the resumption of a pending Ruby exception or jump.
|
|
257
202
|
fn raise_rt(name: &str, e: RtError) -> ! {
|
|
258
203
|
match e {
|
|
259
204
|
RtError::Ruby(state) => unsafe { rb_jump_tag(state) },
|
|
260
|
-
RtError::NoMatch =>
|
|
261
|
-
"
|
|
262
|
-
|
|
263
|
-
)),
|
|
264
|
-
RtError::TypeMismatch { expected, got } =>
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
inspect(got)
|
|
281
|
-
),
|
|
282
|
-
)),
|
|
283
|
-
RtError::Arity { expected, got } => VM::raise_ex(AnyException::new(
|
|
284
|
-
"ArgumentError",
|
|
285
|
-
Some(&format!(
|
|
286
|
-
"wrong number of arguments (given {}, expected {})",
|
|
287
|
-
got, expected
|
|
288
|
-
)),
|
|
205
|
+
RtError::NoMatch => raise(HmError::NoMatch(format!(
|
|
206
|
+
"Non-exhaustive patterns in {}",
|
|
207
|
+
name
|
|
208
|
+
))),
|
|
209
|
+
RtError::TypeMismatch { expected, got } => raise(HmError::TypeMismatch(format!(
|
|
210
|
+
"{}: expected a value of type {} but got {} ({})",
|
|
211
|
+
name,
|
|
212
|
+
expected,
|
|
213
|
+
inspect(got),
|
|
214
|
+
class_name(got)
|
|
215
|
+
))),
|
|
216
|
+
RtError::LazyFailed { clause, got } => raise(HmError::IrrefutablePattern(format!(
|
|
217
|
+
"{}: irrefutable pattern failed for clause {}: {} does not match",
|
|
218
|
+
name,
|
|
219
|
+
clause + 1,
|
|
220
|
+
inspect(got)
|
|
221
|
+
))),
|
|
222
|
+
RtError::Arity { expected, got } => raise_arg(&format!(
|
|
223
|
+
"wrong number of arguments (given {}, expected {})",
|
|
224
|
+
got, expected
|
|
289
225
|
)),
|
|
290
226
|
}
|
|
291
|
-
unreachable!()
|
|
292
227
|
}
|
|
293
228
|
|
|
294
229
|
fn raise_panic(payload: Box<dyn std::any::Any + Send>) -> ! {
|
|
@@ -300,22 +235,10 @@ fn raise_panic(payload: Box<dyn std::any::Any + Send>) -> ! {
|
|
|
300
235
|
"unknown panic".to_string()
|
|
301
236
|
};
|
|
302
237
|
drop(payload);
|
|
303
|
-
|
|
238
|
+
raise(AnyException::new(
|
|
304
239
|
"RuntimeError",
|
|
305
240
|
Some(&format!("haskell_match internal error: {}", msg)),
|
|
306
|
-
))
|
|
307
|
-
unreachable!()
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
fn str_arg(v: &AnyObject, what: &str) -> Result<String, String> {
|
|
311
|
-
v.try_convert_to::<RString>()
|
|
312
|
-
.map(|s| s.to_string())
|
|
313
|
-
.map_err(|_| format!("{} must be a String, got {}", what, inspect(v.value())))
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
fn array_arg(v: &AnyObject, what: &str) -> Result<Array, String> {
|
|
317
|
-
v.try_convert_to::<Array>()
|
|
318
|
-
.map_err(|_| format!("{} must be an Array, got {}", what, inspect(v.value())))
|
|
241
|
+
))
|
|
319
242
|
}
|
|
320
243
|
|
|
321
244
|
fn rstrings(items: &[String]) -> Array {
|
|
@@ -326,247 +249,6 @@ fn rstrings(items: &[String]) -> Array {
|
|
|
326
249
|
a
|
|
327
250
|
}
|
|
328
251
|
|
|
329
|
-
// ---------------------------------------------------------------------------
|
|
330
|
-
// module functions
|
|
331
|
-
// ---------------------------------------------------------------------------
|
|
332
|
-
|
|
333
|
-
methods!(
|
|
334
|
-
Native,
|
|
335
|
-
_rtself,
|
|
336
|
-
// parse_data(decl) -> [name, tyvars, [[con, arity, fields_or_nil, types], ...], deriving]
|
|
337
|
-
fn native_parse_data(decl: RString) -> Array {
|
|
338
|
-
let decl = decl.unwrap_or_else(|_| raise_arg("data declaration must be a String"));
|
|
339
|
-
let d = parse_data(&decl.to_string()).unwrap_or_else(|e| raise_core(e));
|
|
340
|
-
let mut cons = Array::with_capacity(d.cons.len());
|
|
341
|
-
for c in &d.cons {
|
|
342
|
-
let mut row = Array::with_capacity(4);
|
|
343
|
-
row.push(RString::new_utf8(&c.name));
|
|
344
|
-
row.push(Integer::new(c.arity as i64));
|
|
345
|
-
match &c.fields {
|
|
346
|
-
Some(fs) => row.push(rstrings(fs).to_any_object()),
|
|
347
|
-
None => row.push(NilClass::new().to_any_object()),
|
|
348
|
-
};
|
|
349
|
-
row.push(rstrings(&c.types));
|
|
350
|
-
cons.push(row);
|
|
351
|
-
}
|
|
352
|
-
let mut out = Array::with_capacity(4);
|
|
353
|
-
out.push(RString::new_utf8(&d.name));
|
|
354
|
-
out.push(rstrings(&d.tyvars));
|
|
355
|
-
out.push(cons);
|
|
356
|
-
out.push(rstrings(&d.deriving));
|
|
357
|
-
out
|
|
358
|
-
},
|
|
359
|
-
// new_scope -> Integer: a type scope seeded from the global registry
|
|
360
|
-
fn native_new_scope() -> Integer {
|
|
361
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
362
|
-
let seed = scopes[0].clone();
|
|
363
|
-
scopes.push(seed);
|
|
364
|
-
Integer::new((scopes.len() - 1) as i64)
|
|
365
|
-
},
|
|
366
|
-
// import_scope_in(dst, src, names_or_nil) -> [type names imported]
|
|
367
|
-
fn native_import_scope(dst: Integer, src: Integer, names: AnyObject) -> AnyObject {
|
|
368
|
-
let dst = scope_arg(dst);
|
|
369
|
-
let src = scope_arg(src);
|
|
370
|
-
let names = names.unwrap_or_else(|_| raise_arg("names must be an Array or nil"));
|
|
371
|
-
let wanted: Option<Vec<String>> = if names.is_nil() {
|
|
372
|
-
None
|
|
373
|
-
} else {
|
|
374
|
-
let arr = array_arg(&names, "type names").unwrap_or_else(|m| raise_arg(&m));
|
|
375
|
-
Some(
|
|
376
|
-
arr.into_iter()
|
|
377
|
-
.map(|n| str_arg(&n, "type name").unwrap_or_else(|m| raise_arg(&m)))
|
|
378
|
-
.collect(),
|
|
379
|
-
)
|
|
380
|
-
};
|
|
381
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
382
|
-
if src >= scopes.len() || dst >= scopes.len() {
|
|
383
|
-
raise_arg("unknown type scope");
|
|
384
|
-
}
|
|
385
|
-
let types = scopes[src].user_types();
|
|
386
|
-
let env = &mut scopes[dst];
|
|
387
|
-
let mut imported = Vec::new();
|
|
388
|
-
for (name, cons) in types {
|
|
389
|
-
if let Some(w) = &wanted {
|
|
390
|
-
if !w.contains(&name) {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if env.has_same_type(&name, &cons) {
|
|
395
|
-
imported.push(name);
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
match env.register(&name, &cons) {
|
|
399
|
-
Ok(_) => imported.push(name),
|
|
400
|
-
Err(e) => raise_core(e),
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
rstrings(&imported).to_any_object()
|
|
404
|
-
},
|
|
405
|
-
// register_type(name, [[con_name, arity, fields_or_nil, klass], ...], scope) -> nil
|
|
406
|
-
fn native_register_type(name: RString, specs: Array, scope: Integer) -> NilClass {
|
|
407
|
-
let name = name
|
|
408
|
-
.unwrap_or_else(|_| raise_arg("type name must be a String"))
|
|
409
|
-
.to_string();
|
|
410
|
-
let specs = specs.unwrap_or_else(|_| raise_arg("constructor specs must be an Array"));
|
|
411
|
-
let scope = scope_arg(scope);
|
|
412
|
-
let outcome = (|| -> Result<Result<(), CoreError>, String> {
|
|
413
|
-
let mut cons = Vec::new();
|
|
414
|
-
for spec in specs.into_iter() {
|
|
415
|
-
let row = array_arg(&spec, "constructor spec")?;
|
|
416
|
-
if row.length() != 4 {
|
|
417
|
-
return Err("constructor spec must be [name, arity, fields, class]".into());
|
|
418
|
-
}
|
|
419
|
-
let cname = str_arg(&row.at(0), "constructor name")?;
|
|
420
|
-
let arity = row
|
|
421
|
-
.at(1)
|
|
422
|
-
.try_convert_to::<Integer>()
|
|
423
|
-
.map_err(|_| "constructor arity must be an Integer".to_string())?
|
|
424
|
-
.to_i64();
|
|
425
|
-
if arity < 0 {
|
|
426
|
-
return Err("constructor arity must not be negative".into());
|
|
427
|
-
}
|
|
428
|
-
let fields_obj = row.at(2);
|
|
429
|
-
let fields = if fields_obj.is_nil() {
|
|
430
|
-
None
|
|
431
|
-
} else {
|
|
432
|
-
let fa = array_arg(&fields_obj, "field names")?;
|
|
433
|
-
let mut names = Vec::new();
|
|
434
|
-
for f in fa.into_iter() {
|
|
435
|
-
names.push(str_arg(&f, "field name")?);
|
|
436
|
-
}
|
|
437
|
-
Some(names)
|
|
438
|
-
};
|
|
439
|
-
let klass = row.at(3);
|
|
440
|
-
if klass.is_nil() {
|
|
441
|
-
return Err("constructor class must not be nil".into());
|
|
442
|
-
}
|
|
443
|
-
cons.push(ConSpec {
|
|
444
|
-
name: cname,
|
|
445
|
-
arity: arity as usize,
|
|
446
|
-
fields,
|
|
447
|
-
handle: klass.value().value,
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
451
|
-
let env = scope_env(&mut scopes, scope)?;
|
|
452
|
-
let r = env.register(&name, &cons);
|
|
453
|
-
if r.is_ok() {
|
|
454
|
-
for c in &cons {
|
|
455
|
-
// keep constructor classes alive for as long as the
|
|
456
|
-
// process runs: compiled matchers refer to them by VALUE
|
|
457
|
-
unsafe {
|
|
458
|
-
rutie::rubysys::gc::rb_gc_register_mark_object(Value::from(c.handle))
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
Ok(r.map(|_| ()))
|
|
463
|
-
})();
|
|
464
|
-
match outcome {
|
|
465
|
-
Err(msg) => raise_arg(&msg),
|
|
466
|
-
Ok(Err(e)) => raise_core(e),
|
|
467
|
-
Ok(Ok(())) => NilClass::new(),
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
// render_pattern(src) -> canonical Haskell rendering (checks the pattern)
|
|
471
|
-
fn native_render_pattern(src: RString, scope: Integer) -> RString {
|
|
472
|
-
let src = src
|
|
473
|
-
.unwrap_or_else(|_| raise_arg("pattern must be a String"))
|
|
474
|
-
.to_string();
|
|
475
|
-
let scope = scope_arg(scope);
|
|
476
|
-
let result = (|| -> Result<String, CoreError> {
|
|
477
|
-
let raw = parse_pattern(&src)?;
|
|
478
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
479
|
-
let env = scope_env(&mut scopes, scope).unwrap_or_else(|m| raise_arg(&m));
|
|
480
|
-
let (pats, b) = resolve_clause(env, &[raw])?;
|
|
481
|
-
Ok(render_pat(env, &pats[0], &b.names))
|
|
482
|
-
})();
|
|
483
|
-
match result {
|
|
484
|
-
Ok(s) => RString::new_utf8(&s),
|
|
485
|
-
Err(e) => raise_core(e),
|
|
486
|
-
}
|
|
487
|
-
},
|
|
488
|
-
// stack_segment -> Integer (0 = disabled)
|
|
489
|
-
fn native_stack_segment() -> Integer {
|
|
490
|
-
Integer::new(STACK_SEGMENT.load(std::sync::atomic::Ordering::Relaxed) as i64)
|
|
491
|
-
},
|
|
492
|
-
// stack_segment = n
|
|
493
|
-
fn native_set_stack_segment(n: Integer) -> Integer {
|
|
494
|
-
let n = n.map(|i| i.to_i64()).unwrap_or(-1);
|
|
495
|
-
if n < 0 {
|
|
496
|
-
raise_arg("stack_segment must be a non-negative Integer (0 disables segmentation)");
|
|
497
|
-
}
|
|
498
|
-
STACK_SEGMENT.store(n as usize, std::sync::atomic::Ordering::Relaxed);
|
|
499
|
-
Integer::new(n)
|
|
500
|
-
},
|
|
501
|
-
// max_depth -> Integer (0 = unlimited)
|
|
502
|
-
fn native_max_depth() -> Integer {
|
|
503
|
-
Integer::new(MAX_DEPTH.load(std::sync::atomic::Ordering::Relaxed) as i64)
|
|
504
|
-
},
|
|
505
|
-
// max_depth = n
|
|
506
|
-
fn native_set_max_depth(n: Integer) -> Integer {
|
|
507
|
-
let n = n.map(|i| i.to_i64()).unwrap_or(-1);
|
|
508
|
-
if n < 0 {
|
|
509
|
-
raise_arg("max_depth must be a non-negative Integer (0 means unlimited)");
|
|
510
|
-
}
|
|
511
|
-
MAX_DEPTH.store(n as usize, std::sync::atomic::Ordering::Relaxed);
|
|
512
|
-
Integer::new(n)
|
|
513
|
-
},
|
|
514
|
-
// parse_haskell(source) -> JSON string of the module AST
|
|
515
|
-
fn native_parse_haskell(src: RString) -> RString {
|
|
516
|
-
let src = src
|
|
517
|
-
.unwrap_or_else(|_| raise_arg("Haskell source must be a String"))
|
|
518
|
-
.to_string();
|
|
519
|
-
match crate::core::hs::parse_module(&src) {
|
|
520
|
-
Ok(m) => RString::new_utf8(&crate::core::hs::json::module(&m)),
|
|
521
|
-
Err(e) => raise_core(e),
|
|
522
|
-
}
|
|
523
|
-
},
|
|
524
|
-
// constructor_info(name) -> [type_name, arity, fields_or_nil] or nil
|
|
525
|
-
fn native_constructor_info(name: RString, scope: Integer) -> AnyObject {
|
|
526
|
-
let name = name
|
|
527
|
-
.unwrap_or_else(|_| raise_arg("constructor name must be a String"))
|
|
528
|
-
.to_string();
|
|
529
|
-
let scope = scope_arg(scope);
|
|
530
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
531
|
-
let env = scope_env(&mut scopes, scope).unwrap_or_else(|m| raise_arg(&m));
|
|
532
|
-
match env.lookup_con(&name) {
|
|
533
|
-
Some(c) => {
|
|
534
|
-
let con = env.con(c);
|
|
535
|
-
let mut row = Array::with_capacity(3);
|
|
536
|
-
row.push(RString::new_utf8(&env.ty(con.ty).name));
|
|
537
|
-
row.push(Integer::new(con.arity as i64));
|
|
538
|
-
match &con.fields {
|
|
539
|
-
Some(fs) => row.push(rstrings(fs).to_any_object()),
|
|
540
|
-
None => row.push(NilClass::new().to_any_object()),
|
|
541
|
-
};
|
|
542
|
-
row.to_any_object()
|
|
543
|
-
}
|
|
544
|
-
None => NilClass::new().to_any_object(),
|
|
545
|
-
}
|
|
546
|
-
},
|
|
547
|
-
// constructors(type_name) -> [names] or nil
|
|
548
|
-
fn native_constructors(name: RString, scope: Integer) -> AnyObject {
|
|
549
|
-
let name = name
|
|
550
|
-
.unwrap_or_else(|_| raise_arg("type name must be a String"))
|
|
551
|
-
.to_string();
|
|
552
|
-
let scope = scope_arg(scope);
|
|
553
|
-
let mut scopes = SCOPES.lock().unwrap_or_else(|p| p.into_inner());
|
|
554
|
-
let env = scope_env(&mut scopes, scope).unwrap_or_else(|m| raise_arg(&m));
|
|
555
|
-
match env.lookup_type(&name) {
|
|
556
|
-
Some(t) => {
|
|
557
|
-
let names: Vec<String> = env
|
|
558
|
-
.ty(t)
|
|
559
|
-
.cons
|
|
560
|
-
.iter()
|
|
561
|
-
.map(|c| env.con(*c).name.clone())
|
|
562
|
-
.collect();
|
|
563
|
-
rstrings(&names).to_any_object()
|
|
564
|
-
}
|
|
565
|
-
None => NilClass::new().to_any_object(),
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
);
|
|
569
|
-
|
|
570
252
|
// ---------------------------------------------------------------------------
|
|
571
253
|
// Matcher construction
|
|
572
254
|
// ---------------------------------------------------------------------------
|
|
@@ -578,35 +260,22 @@ struct Built {
|
|
|
578
260
|
redundant: Vec<usize>,
|
|
579
261
|
}
|
|
580
262
|
|
|
581
|
-
enum BuildError {
|
|
582
|
-
Arg(String),
|
|
583
|
-
Core(CoreError),
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
impl From<CoreError> for BuildError {
|
|
587
|
-
fn from(e: CoreError) -> Self {
|
|
588
|
-
BuildError::Core(e)
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
263
|
fn build(
|
|
593
264
|
name: String,
|
|
594
265
|
clauses: &Array,
|
|
595
266
|
guarded: &Array,
|
|
596
267
|
limit: usize,
|
|
597
268
|
scope: usize,
|
|
598
|
-
) -> Result<Built,
|
|
269
|
+
) -> Result<Built, AnyException> {
|
|
599
270
|
let n = clauses.length();
|
|
600
271
|
if n == 0 {
|
|
601
|
-
return Err(
|
|
272
|
+
return Err(argument_error("at least one clause is required"));
|
|
602
273
|
}
|
|
603
274
|
if guarded.length() != n {
|
|
604
|
-
return Err(
|
|
605
|
-
"guard flags must have one entry per clause".into(),
|
|
606
|
-
));
|
|
275
|
+
return Err(argument_error("guard flags must have one entry per clause"));
|
|
607
276
|
}
|
|
608
|
-
let mut scopes =
|
|
609
|
-
let env = scope_env(&mut scopes, scope)
|
|
277
|
+
let mut scopes = state::scopes();
|
|
278
|
+
let env = state::scope_env(&mut scopes, scope)?;
|
|
610
279
|
let mut resolved: Vec<(Vec<crate::core::ast::Pat>, bool)> = Vec::with_capacity(n);
|
|
611
280
|
let mut names: Vec<Vec<String>> = Vec::with_capacity(n);
|
|
612
281
|
let mut guard_flags: Vec<bool> = Vec::with_capacity(n);
|
|
@@ -722,63 +391,84 @@ fn matcher_of(obj: Value) -> &'static Matcher {
|
|
|
722
391
|
}
|
|
723
392
|
|
|
724
393
|
// ---------------------------------------------------------------------------
|
|
725
|
-
// Matcher methods (
|
|
394
|
+
// Matcher methods (`methods!`, returning `Result`s)
|
|
726
395
|
// ---------------------------------------------------------------------------
|
|
727
396
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
name
|
|
732
|
-
clauses
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
397
|
+
methods!(
|
|
398
|
+
RMatcher,
|
|
399
|
+
rtself,
|
|
400
|
+
// Matcher.new(name, clauses, guarded_flags, witness_limit, scope):
|
|
401
|
+
// compiles the clauses and wraps the matcher in an instance of the
|
|
402
|
+
// receiver (`Matcher` or a subclass such as `Function`), with the
|
|
403
|
+
// exhaustiveness report in `@missing`, `@missing_truncated` and
|
|
404
|
+
// `@redundant`.
|
|
405
|
+
fn matcher_new(
|
|
406
|
+
name: RString,
|
|
407
|
+
clauses: Array,
|
|
408
|
+
guarded: Array,
|
|
409
|
+
limit: Integer,
|
|
410
|
+
scope: Integer,
|
|
411
|
+
) -> Result<AnyObject, AnyException> {
|
|
412
|
+
let name = name?.to_string();
|
|
413
|
+
let clauses = clauses?;
|
|
414
|
+
let guarded = guarded?;
|
|
415
|
+
let limit = limit.map(|i| i.to_i64()).unwrap_or(20).max(1) as usize;
|
|
416
|
+
let scope = scope?.to_i64();
|
|
417
|
+
if scope < 0 {
|
|
418
|
+
return Err(argument_error("scope must not be negative"));
|
|
419
|
+
}
|
|
420
|
+
let built = build(name, &clauses, &guarded, limit, scope as usize)?;
|
|
421
|
+
let klass = Class::from(rtself.value());
|
|
422
|
+
let mut obj: AnyObject = klass.wrap_data(built.matcher, &*MATCHER_TYPE);
|
|
423
|
+
obj.instance_variable_set("@missing", rstrings(&built.missing));
|
|
424
|
+
obj.instance_variable_set("@missing_truncated", Boolean::new(built.truncated));
|
|
425
|
+
let mut redundant = Array::with_capacity(built.redundant.len());
|
|
426
|
+
for r in built.redundant {
|
|
427
|
+
redundant.push(Integer::new(r as i64));
|
|
428
|
+
}
|
|
429
|
+
obj.instance_variable_set("@redundant", redundant);
|
|
430
|
+
Ok(obj)
|
|
431
|
+
},
|
|
432
|
+
// matcher.arity -> number of arguments
|
|
433
|
+
fn matcher_arity() -> Integer {
|
|
434
|
+
Integer::new(matcher_of(rtself.value()).arity as i64)
|
|
435
|
+
},
|
|
436
|
+
// matcher.name -> the function's name
|
|
437
|
+
fn matcher_name() -> RString {
|
|
438
|
+
RString::new_utf8(&matcher_of(rtself.value()).name)
|
|
439
|
+
},
|
|
440
|
+
// matcher.names -> variables bound by each clause, in order
|
|
441
|
+
fn matcher_names() -> Array {
|
|
442
|
+
let m = matcher_of(rtself.value());
|
|
443
|
+
let mut out = Array::with_capacity(m.names.len());
|
|
444
|
+
for ns in &m.names {
|
|
445
|
+
out.push(rstrings(ns));
|
|
446
|
+
}
|
|
447
|
+
out
|
|
448
|
+
},
|
|
449
|
+
// matcher.guarded -> whether each clause has a guard
|
|
450
|
+
fn matcher_guarded() -> Array {
|
|
451
|
+
let m = matcher_of(rtself.value());
|
|
452
|
+
let mut out = Array::with_capacity(m.guarded.len());
|
|
453
|
+
for g in &m.guarded {
|
|
454
|
+
out.push(Boolean::new(*g));
|
|
779
455
|
}
|
|
456
|
+
out
|
|
457
|
+
},
|
|
458
|
+
// matcher.slots -> value slots of the decision tree
|
|
459
|
+
fn matcher_slots() -> Integer {
|
|
460
|
+
Integer::new(matcher_of(rtself.value()).n_slots as i64)
|
|
461
|
+
},
|
|
462
|
+
// matcher.tree -> human-readable dump of the decision tree
|
|
463
|
+
fn matcher_tree() -> RString {
|
|
464
|
+
let m = matcher_of(rtself.value());
|
|
465
|
+
RString::new_utf8(&runtime::dump(&m.env, &m.tree, &m.names, 0))
|
|
780
466
|
}
|
|
781
|
-
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
// Matcher hot-path methods (raw extern "C")
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
782
472
|
|
|
783
473
|
/// Outcome of the pure-Rust part of a call: the body to invoke and where its
|
|
784
474
|
/// arguments are.
|
|
@@ -979,37 +669,16 @@ unsafe extern "C" fn matcher_prepare(argc: c_int, argv: *const Value, rtself: Va
|
|
|
979
669
|
};
|
|
980
670
|
array::rb_ary_push(out, p.body);
|
|
981
671
|
// Count this level now; the Ruby caller pops it after the body.
|
|
982
|
-
let depth =
|
|
983
|
-
|
|
672
|
+
let depth = match state::depth_push() {
|
|
673
|
+
Ok(depth) => depth,
|
|
674
|
+
Err(e) => raise(e),
|
|
675
|
+
};
|
|
676
|
+
array::rb_ary_push(out, Integer::new(depth as i64).value());
|
|
984
677
|
out
|
|
985
678
|
}
|
|
986
679
|
}
|
|
987
680
|
}
|
|
988
681
|
|
|
989
|
-
/// Native.depth_push -> the new nesting depth (raises StackOverflowError past
|
|
990
|
-
/// max_depth); Native.depth_pop undoes it. Used by deep-mode functions,
|
|
991
|
-
/// whose body invocation happens in Ruby.
|
|
992
|
-
unsafe extern "C" fn native_depth_push(_rtself: Value) -> Value {
|
|
993
|
-
let depth = DEPTH.with(|d| d.get());
|
|
994
|
-
let max = MAX_DEPTH.load(std::sync::atomic::Ordering::Relaxed);
|
|
995
|
-
if max > 0 && depth >= max {
|
|
996
|
-
VM::raise_ex(exception(
|
|
997
|
-
"StackOverflowError",
|
|
998
|
-
&format!(
|
|
999
|
-
"recursion deeper than HaskellMatch.max_depth ({}); use tail calls, defer, or raise the limit",
|
|
1000
|
-
max
|
|
1001
|
-
),
|
|
1002
|
-
));
|
|
1003
|
-
}
|
|
1004
|
-
DEPTH.with(|d| d.set(depth + 1));
|
|
1005
|
-
Integer::new((depth + 1) as i64).value()
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
unsafe extern "C" fn native_depth_pop(_rtself: Value) -> Value {
|
|
1009
|
-
DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
|
|
1010
|
-
Value::from(rutie::rubysys::value::RubySpecialConsts::Nil as usize)
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
682
|
/// matcher.select(*args) -> [clause_index, [values]] or nil (guards from
|
|
1014
683
|
/// `@guards`).
|
|
1015
684
|
unsafe extern "C" fn matcher_select(argc: c_int, argv: *const Value, rtself: Value) -> Value {
|
|
@@ -1104,41 +773,6 @@ unsafe extern "C" fn matcher_run(
|
|
|
1104
773
|
}
|
|
1105
774
|
}
|
|
1106
775
|
|
|
1107
|
-
unsafe extern "C" fn matcher_arity(rtself: Value) -> Value {
|
|
1108
|
-
Integer::new(matcher_of(rtself).arity as i64).value()
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
unsafe extern "C" fn matcher_name(rtself: Value) -> Value {
|
|
1112
|
-
RString::new_utf8(&matcher_of(rtself).name).value()
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
unsafe extern "C" fn matcher_names(rtself: Value) -> Value {
|
|
1116
|
-
let m = matcher_of(rtself);
|
|
1117
|
-
let mut out = Array::with_capacity(m.names.len());
|
|
1118
|
-
for ns in &m.names {
|
|
1119
|
-
out.push(rstrings(ns));
|
|
1120
|
-
}
|
|
1121
|
-
out.value()
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
unsafe extern "C" fn matcher_guarded(rtself: Value) -> Value {
|
|
1125
|
-
let m = matcher_of(rtself);
|
|
1126
|
-
let mut out = Array::with_capacity(m.guarded.len());
|
|
1127
|
-
for g in &m.guarded {
|
|
1128
|
-
out.push(Boolean::new(*g));
|
|
1129
|
-
}
|
|
1130
|
-
out.value()
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
unsafe extern "C" fn matcher_slots(rtself: Value) -> Value {
|
|
1134
|
-
Integer::new(matcher_of(rtself).n_slots as i64).value()
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
unsafe extern "C" fn matcher_tree(rtself: Value) -> Value {
|
|
1138
|
-
let m = matcher_of(rtself);
|
|
1139
|
-
RString::new_utf8(&runtime::dump(&m.env, &m.tree, &m.names, 0)).value()
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
776
|
unsafe fn def_method(klass: Value, name: &str, f: CallbackPtr, argc: c_int) {
|
|
1143
777
|
let cname = CString::new(name).unwrap();
|
|
1144
778
|
rclass::rb_define_method(klass, cname.as_ptr(), f, argc);
|
|
@@ -1148,48 +782,28 @@ pub fn init() {
|
|
|
1148
782
|
// Nothing here keeps per-Ractor state: the type registry is behind a
|
|
1149
783
|
// mutex and matchers are immutable once built.
|
|
1150
784
|
unsafe { VM::ext_ractor_safe(true) };
|
|
1151
|
-
let mut
|
|
1152
|
-
|
|
1153
|
-
unsafe {
|
|
1154
|
-
let n = native.value();
|
|
1155
|
-
let push = CString::new("depth_push").unwrap();
|
|
1156
|
-
let pop = CString::new("depth_pop").unwrap();
|
|
1157
|
-
rclass::rb_define_singleton_method(n, push.as_ptr(), native_depth_push as CallbackPtr, 0);
|
|
1158
|
-
rclass::rb_define_singleton_method(n, pop.as_ptr(), native_depth_pop as CallbackPtr, 0);
|
|
1159
|
-
}
|
|
1160
|
-
native.define(|m| {
|
|
1161
|
-
m.def_self("parse_data", native_parse_data);
|
|
1162
|
-
m.def_self("new_scope", native_new_scope);
|
|
1163
|
-
m.def_self("import_scope_in", native_import_scope);
|
|
1164
|
-
m.def_self("register_type_in", native_register_type);
|
|
1165
|
-
m.def_self("render_pattern_in", native_render_pattern);
|
|
1166
|
-
m.def_self("constructors_in", native_constructors);
|
|
1167
|
-
m.def_self("parse_haskell", native_parse_haskell);
|
|
1168
|
-
m.def_self("constructor_info_in", native_constructor_info);
|
|
1169
|
-
m.def_self("stack_segment", native_stack_segment);
|
|
1170
|
-
m.def_self("stack_segment=", native_set_stack_segment);
|
|
1171
|
-
m.def_self("max_depth", native_max_depth);
|
|
1172
|
-
m.def_self("max_depth=", native_set_max_depth);
|
|
1173
|
-
});
|
|
785
|
+
let mut native = native::functions::define_ruby();
|
|
786
|
+
HmError::define_ruby();
|
|
1174
787
|
let object = Class::from_existing("Object");
|
|
1175
788
|
let mut matcher = native.define_nested_class("Matcher", Some(&object));
|
|
1176
789
|
// Instances are only ever created through `new` with wrapped data.
|
|
1177
790
|
matcher.undef_alloc_func();
|
|
791
|
+
matcher.define(|k| {
|
|
792
|
+
k.def_self("new", matcher_new);
|
|
793
|
+
k.def("arity", matcher_arity);
|
|
794
|
+
k.def("name", matcher_name);
|
|
795
|
+
k.def("names", matcher_names);
|
|
796
|
+
k.def("guarded", matcher_guarded);
|
|
797
|
+
k.def("slots", matcher_slots);
|
|
798
|
+
k.def("tree", matcher_tree);
|
|
799
|
+
});
|
|
1178
800
|
unsafe {
|
|
1179
801
|
let k = matcher.value();
|
|
1180
|
-
let new_name = CString::new("new").unwrap();
|
|
1181
|
-
rclass::rb_define_singleton_method(k, new_name.as_ptr(), matcher_new as CallbackPtr, 5);
|
|
1182
802
|
def_method(k, "call", matcher_call as CallbackPtr, -1);
|
|
1183
803
|
def_method(k, "prepare", matcher_prepare as CallbackPtr, -1);
|
|
1184
804
|
def_method(k, "select", matcher_select as CallbackPtr, -1);
|
|
1185
805
|
def_method(k, "select_with", matcher_select_with as CallbackPtr, 2);
|
|
1186
806
|
def_method(k, "run", matcher_run as CallbackPtr, 3);
|
|
1187
|
-
def_method(k, "arity", matcher_arity as CallbackPtr, 0);
|
|
1188
|
-
def_method(k, "name", matcher_name as CallbackPtr, 0);
|
|
1189
|
-
def_method(k, "names", matcher_names as CallbackPtr, 0);
|
|
1190
|
-
def_method(k, "guarded", matcher_guarded as CallbackPtr, 0);
|
|
1191
|
-
def_method(k, "slots", matcher_slots as CallbackPtr, 0);
|
|
1192
|
-
def_method(k, "tree", matcher_tree as CallbackPtr, 0);
|
|
1193
807
|
}
|
|
1194
808
|
ids();
|
|
1195
809
|
}
|