method-ray 0.1.9 → 0.1.10
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 +20 -0
- data/README.md +9 -11
- data/core/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +0 -280
- data/core/src/analyzer/blocks.rs +0 -190
- data/core/src/analyzer/calls.rs +3 -32
- data/core/src/analyzer/conditionals.rs +0 -348
- data/core/src/analyzer/definitions.rs +11 -526
- data/core/src/analyzer/dispatch.rs +54 -1000
- data/core/src/analyzer/exceptions.rs +0 -454
- data/core/src/analyzer/literals.rs +0 -54
- data/core/src/analyzer/loops.rs +0 -207
- data/core/src/analyzer/mod.rs +0 -15
- data/core/src/analyzer/operators.rs +0 -205
- data/core/src/analyzer/parameters.rs +4 -227
- data/core/src/analyzer/parentheses.rs +0 -88
- data/core/src/analyzer/returns.rs +0 -152
- data/core/src/analyzer/super_calls.rs +1 -212
- data/core/src/analyzer/variables.rs +5 -25
- data/core/src/checker.rs +0 -13
- data/core/src/diagnostics/diagnostic.rs +0 -41
- data/core/src/diagnostics/formatter.rs +0 -38
- data/core/src/env/box_manager.rs +0 -30
- data/core/src/env/global_env.rs +52 -79
- data/core/src/env/local_env.rs +0 -50
- data/core/src/env/method_registry.rs +52 -233
- data/core/src/env/scope.rs +0 -375
- data/core/src/env/vertex_manager.rs +0 -73
- data/core/src/graph/box.rs +20 -439
- data/core/src/graph/change_set.rs +0 -65
- data/core/src/graph/vertex.rs +0 -69
- data/core/src/parser.rs +0 -77
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +5 -4
data/core/src/graph/box.rs
CHANGED
|
@@ -54,6 +54,8 @@ pub struct MethodCallBox {
|
|
|
54
54
|
arg_vtxs: Vec<VertexId>,
|
|
55
55
|
kwarg_vtxs: Option<HashMap<String, VertexId>>,
|
|
56
56
|
location: Option<SourceLocation>, // Source code location
|
|
57
|
+
/// Whether this is a safe navigation call (`&.`)
|
|
58
|
+
safe_navigation: bool,
|
|
57
59
|
/// Number of times this box has been rescheduled
|
|
58
60
|
reschedule_count: u8,
|
|
59
61
|
}
|
|
@@ -62,6 +64,7 @@ pub struct MethodCallBox {
|
|
|
62
64
|
const MAX_RESCHEDULE_COUNT: u8 = 3;
|
|
63
65
|
|
|
64
66
|
impl MethodCallBox {
|
|
67
|
+
#[allow(clippy::too_many_arguments)]
|
|
65
68
|
pub fn new(
|
|
66
69
|
id: BoxId,
|
|
67
70
|
recv: VertexId,
|
|
@@ -70,6 +73,7 @@ impl MethodCallBox {
|
|
|
70
73
|
arg_vtxs: Vec<VertexId>,
|
|
71
74
|
kwarg_vtxs: Option<HashMap<String, VertexId>>,
|
|
72
75
|
location: Option<SourceLocation>,
|
|
76
|
+
safe_navigation: bool,
|
|
73
77
|
) -> Self {
|
|
74
78
|
Self {
|
|
75
79
|
id,
|
|
@@ -79,6 +83,7 @@ impl MethodCallBox {
|
|
|
79
83
|
arg_vtxs,
|
|
80
84
|
kwarg_vtxs,
|
|
81
85
|
location,
|
|
86
|
+
safe_navigation,
|
|
82
87
|
reschedule_count: 0,
|
|
83
88
|
}
|
|
84
89
|
}
|
|
@@ -100,6 +105,13 @@ impl MethodCallBox {
|
|
|
100
105
|
genv: &mut GlobalEnv,
|
|
101
106
|
changes: &mut ChangeSet,
|
|
102
107
|
) {
|
|
108
|
+
// Safe navigation (`&.`): skip nil receiver entirely.
|
|
109
|
+
// Ruby's &. short-circuits: no method resolution, no argument evaluation, no error.
|
|
110
|
+
// The nil return type is added in run() after processing all receiver types.
|
|
111
|
+
if self.safe_navigation && matches!(recv_ty, Type::Nil) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
103
115
|
if let Some(method_info) = genv.resolve_method(recv_ty, &self.method_name) {
|
|
104
116
|
if let Some(return_vtx) = method_info.return_vertex {
|
|
105
117
|
// User-defined method: connect body's return vertex to call site
|
|
@@ -186,8 +198,14 @@ impl BoxTrait for MethodCallBox {
|
|
|
186
198
|
return;
|
|
187
199
|
}
|
|
188
200
|
|
|
189
|
-
for recv_ty in recv_types {
|
|
190
|
-
self.process_recv_type(
|
|
201
|
+
for recv_ty in &recv_types {
|
|
202
|
+
self.process_recv_type(recv_ty, genv, changes);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Safe navigation (`&.`): if receiver can be nil, return type includes nil
|
|
206
|
+
if self.safe_navigation && recv_types.iter().any(|t| matches!(t, Type::Nil)) {
|
|
207
|
+
let nil_src = genv.new_source(Type::Nil);
|
|
208
|
+
changes.add_edge(nil_src, self.ret);
|
|
191
209
|
}
|
|
192
210
|
}
|
|
193
211
|
}
|
|
@@ -327,440 +345,3 @@ impl BoxTrait for BlockParameterTypeBox {
|
|
|
327
345
|
}
|
|
328
346
|
}
|
|
329
347
|
}
|
|
330
|
-
|
|
331
|
-
#[cfg(test)]
|
|
332
|
-
mod tests {
|
|
333
|
-
use super::*;
|
|
334
|
-
use crate::env::GlobalEnv;
|
|
335
|
-
use crate::types::Type;
|
|
336
|
-
|
|
337
|
-
#[test]
|
|
338
|
-
fn test_method_call_box() {
|
|
339
|
-
let mut genv = GlobalEnv::new();
|
|
340
|
-
|
|
341
|
-
// Register String#upcase
|
|
342
|
-
genv.register_builtin_method(Type::string(), "upcase", Type::string());
|
|
343
|
-
|
|
344
|
-
// x = "hello" (Source<String> -> Vertex)
|
|
345
|
-
let x_vtx = genv.new_vertex();
|
|
346
|
-
let str_src = genv.new_source(Type::string());
|
|
347
|
-
genv.add_edge(str_src, x_vtx);
|
|
348
|
-
|
|
349
|
-
// x.upcase
|
|
350
|
-
let ret_vtx = genv.new_vertex();
|
|
351
|
-
let box_id = BoxId(0);
|
|
352
|
-
let mut call_box = MethodCallBox::new(
|
|
353
|
-
box_id,
|
|
354
|
-
x_vtx,
|
|
355
|
-
"upcase".to_string(),
|
|
356
|
-
ret_vtx,
|
|
357
|
-
vec![],
|
|
358
|
-
None,
|
|
359
|
-
None, // No location in test
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
// Execute Box
|
|
363
|
-
let mut changes = ChangeSet::new();
|
|
364
|
-
call_box.run(&mut genv, &mut changes);
|
|
365
|
-
genv.apply_changes(changes);
|
|
366
|
-
|
|
367
|
-
// Check return type
|
|
368
|
-
let ret_vertex = genv.get_vertex(ret_vtx).unwrap();
|
|
369
|
-
assert_eq!(ret_vertex.show(), "String");
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
#[test]
|
|
373
|
-
fn test_method_call_box_undefined() {
|
|
374
|
-
let mut genv = GlobalEnv::new();
|
|
375
|
-
|
|
376
|
-
// Don't register method
|
|
377
|
-
|
|
378
|
-
let x_vtx = genv.new_vertex();
|
|
379
|
-
let str_src = genv.new_source(Type::string());
|
|
380
|
-
genv.add_edge(str_src, x_vtx);
|
|
381
|
-
|
|
382
|
-
// x.unknown_method (undefined)
|
|
383
|
-
let ret_vtx = genv.new_vertex();
|
|
384
|
-
let box_id = BoxId(0);
|
|
385
|
-
let mut call_box = MethodCallBox::new(
|
|
386
|
-
box_id,
|
|
387
|
-
x_vtx,
|
|
388
|
-
"unknown_method".to_string(),
|
|
389
|
-
ret_vtx,
|
|
390
|
-
vec![],
|
|
391
|
-
None,
|
|
392
|
-
None, // No location in test
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
let mut changes = ChangeSet::new();
|
|
396
|
-
call_box.run(&mut genv, &mut changes);
|
|
397
|
-
genv.apply_changes(changes);
|
|
398
|
-
|
|
399
|
-
// Return value is untyped
|
|
400
|
-
let ret_vertex = genv.get_vertex(ret_vtx).unwrap();
|
|
401
|
-
assert_eq!(ret_vertex.show(), "untyped");
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
#[test]
|
|
405
|
-
fn test_block_param_type_box_simple() {
|
|
406
|
-
let mut genv = GlobalEnv::new();
|
|
407
|
-
|
|
408
|
-
// Register String#each_char with block param type String
|
|
409
|
-
genv.register_builtin_method_with_block(
|
|
410
|
-
Type::string(),
|
|
411
|
-
"each_char",
|
|
412
|
-
Type::string(),
|
|
413
|
-
Some(vec![Type::string()]),
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
// Create receiver vertex with String type
|
|
417
|
-
let recv_vtx = genv.new_vertex();
|
|
418
|
-
let str_src = genv.new_source(Type::string());
|
|
419
|
-
genv.add_edge(str_src, recv_vtx);
|
|
420
|
-
|
|
421
|
-
// Create block parameter vertex
|
|
422
|
-
let param_vtx = genv.new_vertex();
|
|
423
|
-
|
|
424
|
-
// Create and run BlockParameterTypeBox
|
|
425
|
-
let box_id = genv.alloc_box_id();
|
|
426
|
-
let block_box = BlockParameterTypeBox::new(
|
|
427
|
-
box_id,
|
|
428
|
-
recv_vtx,
|
|
429
|
-
"each_char".to_string(),
|
|
430
|
-
vec![param_vtx],
|
|
431
|
-
);
|
|
432
|
-
genv.register_box(box_id, Box::new(block_box));
|
|
433
|
-
|
|
434
|
-
// Run all boxes
|
|
435
|
-
genv.run_all();
|
|
436
|
-
|
|
437
|
-
// Block parameter should now have String type
|
|
438
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "String");
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
#[test]
|
|
442
|
-
fn test_block_param_type_variable_skipped() {
|
|
443
|
-
let mut genv = GlobalEnv::new();
|
|
444
|
-
|
|
445
|
-
// Register Array#each with block param type Elem (type variable)
|
|
446
|
-
genv.register_builtin_method_with_block(
|
|
447
|
-
Type::array(),
|
|
448
|
-
"each",
|
|
449
|
-
Type::array(),
|
|
450
|
-
Some(vec![Type::instance("Elem")]),
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
let recv_vtx = genv.new_vertex();
|
|
454
|
-
let arr_src = genv.new_source(Type::array());
|
|
455
|
-
genv.add_edge(arr_src, recv_vtx);
|
|
456
|
-
|
|
457
|
-
let param_vtx = genv.new_vertex();
|
|
458
|
-
|
|
459
|
-
let box_id = genv.alloc_box_id();
|
|
460
|
-
let block_box = BlockParameterTypeBox::new(
|
|
461
|
-
box_id,
|
|
462
|
-
recv_vtx,
|
|
463
|
-
"each".to_string(),
|
|
464
|
-
vec![param_vtx],
|
|
465
|
-
);
|
|
466
|
-
genv.register_box(box_id, Box::new(block_box));
|
|
467
|
-
|
|
468
|
-
genv.run_all();
|
|
469
|
-
|
|
470
|
-
// Block parameter should remain untyped (type variable skipped)
|
|
471
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "untyped");
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
#[test]
|
|
475
|
-
fn test_block_param_multiple_params() {
|
|
476
|
-
let mut genv = GlobalEnv::new();
|
|
477
|
-
|
|
478
|
-
// Register a method with multiple block params
|
|
479
|
-
genv.register_builtin_method_with_block(
|
|
480
|
-
Type::string(),
|
|
481
|
-
"each_with_index",
|
|
482
|
-
Type::string(),
|
|
483
|
-
Some(vec![Type::string(), Type::integer()]),
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
let recv_vtx = genv.new_vertex();
|
|
487
|
-
let str_src = genv.new_source(Type::string());
|
|
488
|
-
genv.add_edge(str_src, recv_vtx);
|
|
489
|
-
|
|
490
|
-
let param1_vtx = genv.new_vertex();
|
|
491
|
-
let param2_vtx = genv.new_vertex();
|
|
492
|
-
|
|
493
|
-
let box_id = genv.alloc_box_id();
|
|
494
|
-
let block_box = BlockParameterTypeBox::new(
|
|
495
|
-
box_id,
|
|
496
|
-
recv_vtx,
|
|
497
|
-
"each_with_index".to_string(),
|
|
498
|
-
vec![param1_vtx, param2_vtx],
|
|
499
|
-
);
|
|
500
|
-
genv.register_box(box_id, Box::new(block_box));
|
|
501
|
-
|
|
502
|
-
genv.run_all();
|
|
503
|
-
|
|
504
|
-
// Both params should have their types
|
|
505
|
-
assert_eq!(genv.get_vertex(param1_vtx).unwrap().show(), "String");
|
|
506
|
-
assert_eq!(genv.get_vertex(param2_vtx).unwrap().show(), "Integer");
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
#[test]
|
|
510
|
-
fn test_block_param_type_variable_resolved() {
|
|
511
|
-
let mut genv = GlobalEnv::new();
|
|
512
|
-
|
|
513
|
-
// Register Array#each with block param type Elem (type variable)
|
|
514
|
-
genv.register_builtin_method_with_block(
|
|
515
|
-
Type::array(),
|
|
516
|
-
"each",
|
|
517
|
-
Type::array(),
|
|
518
|
-
Some(vec![Type::instance("Elem")]),
|
|
519
|
-
);
|
|
520
|
-
|
|
521
|
-
// Create receiver vertex with Array[Integer] type
|
|
522
|
-
let recv_vtx = genv.new_vertex();
|
|
523
|
-
let arr_src = genv.new_source(Type::array_of(Type::integer()));
|
|
524
|
-
genv.add_edge(arr_src, recv_vtx);
|
|
525
|
-
|
|
526
|
-
let param_vtx = genv.new_vertex();
|
|
527
|
-
|
|
528
|
-
let box_id = genv.alloc_box_id();
|
|
529
|
-
let block_box = BlockParameterTypeBox::new(
|
|
530
|
-
box_id,
|
|
531
|
-
recv_vtx,
|
|
532
|
-
"each".to_string(),
|
|
533
|
-
vec![param_vtx],
|
|
534
|
-
);
|
|
535
|
-
genv.register_box(box_id, Box::new(block_box));
|
|
536
|
-
|
|
537
|
-
genv.run_all();
|
|
538
|
-
|
|
539
|
-
// Block parameter should be Integer (resolved from Array[Integer])
|
|
540
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "Integer");
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
#[test]
|
|
544
|
-
fn test_hash_type_variable_resolved() {
|
|
545
|
-
let mut genv = GlobalEnv::new();
|
|
546
|
-
|
|
547
|
-
// Register Hash#each with block param types K, V
|
|
548
|
-
genv.register_builtin_method_with_block(
|
|
549
|
-
Type::hash(),
|
|
550
|
-
"each",
|
|
551
|
-
Type::hash(),
|
|
552
|
-
Some(vec![Type::instance("K"), Type::instance("V")]),
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
// Create receiver vertex with Hash[String, Integer] type
|
|
556
|
-
let recv_vtx = genv.new_vertex();
|
|
557
|
-
let hash_src = genv.new_source(Type::hash_of(Type::string(), Type::integer()));
|
|
558
|
-
genv.add_edge(hash_src, recv_vtx);
|
|
559
|
-
|
|
560
|
-
let key_vtx = genv.new_vertex();
|
|
561
|
-
let value_vtx = genv.new_vertex();
|
|
562
|
-
|
|
563
|
-
let box_id = genv.alloc_box_id();
|
|
564
|
-
let block_box = BlockParameterTypeBox::new(
|
|
565
|
-
box_id,
|
|
566
|
-
recv_vtx,
|
|
567
|
-
"each".to_string(),
|
|
568
|
-
vec![key_vtx, value_vtx],
|
|
569
|
-
);
|
|
570
|
-
genv.register_box(box_id, Box::new(block_box));
|
|
571
|
-
|
|
572
|
-
genv.run_all();
|
|
573
|
-
|
|
574
|
-
// Block parameters should be resolved from Hash[String, Integer]
|
|
575
|
-
assert_eq!(genv.get_vertex(key_vtx).unwrap().show(), "String");
|
|
576
|
-
assert_eq!(genv.get_vertex(value_vtx).unwrap().show(), "Integer");
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
#[test]
|
|
580
|
-
fn test_method_call_box_user_defined_method() {
|
|
581
|
-
let mut genv = GlobalEnv::new();
|
|
582
|
-
|
|
583
|
-
// Simulate: def name; "Alice"; end
|
|
584
|
-
let body_src = genv.new_source(Type::string());
|
|
585
|
-
|
|
586
|
-
// Register user-defined method User#name with return_vertex
|
|
587
|
-
genv.register_user_method(Type::instance("User"), "name", body_src, vec![], None);
|
|
588
|
-
|
|
589
|
-
// Simulate: user.name (receiver has type User)
|
|
590
|
-
let recv_vtx = genv.new_vertex();
|
|
591
|
-
let recv_src = genv.new_source(Type::instance("User"));
|
|
592
|
-
genv.add_edge(recv_src, recv_vtx);
|
|
593
|
-
|
|
594
|
-
let ret_vtx = genv.new_vertex();
|
|
595
|
-
let box_id = genv.alloc_box_id();
|
|
596
|
-
let call_box = MethodCallBox::new(
|
|
597
|
-
box_id,
|
|
598
|
-
recv_vtx,
|
|
599
|
-
"name".to_string(),
|
|
600
|
-
ret_vtx,
|
|
601
|
-
vec![],
|
|
602
|
-
None,
|
|
603
|
-
None,
|
|
604
|
-
);
|
|
605
|
-
genv.register_box(box_id, Box::new(call_box));
|
|
606
|
-
|
|
607
|
-
genv.run_all();
|
|
608
|
-
|
|
609
|
-
// Return type should be String (propagated from body's last expression)
|
|
610
|
-
assert_eq!(genv.get_vertex(ret_vtx).unwrap().show(), "String");
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
#[test]
|
|
614
|
-
fn test_method_call_box_param_type_propagation() {
|
|
615
|
-
let mut genv = GlobalEnv::new();
|
|
616
|
-
|
|
617
|
-
// Simulate: def format(value); value.to_s; end
|
|
618
|
-
// 1. Create parameter vertex for 'value'
|
|
619
|
-
let param_vtx = genv.new_vertex();
|
|
620
|
-
|
|
621
|
-
// 2. Register Integer#to_s -> String (builtin)
|
|
622
|
-
genv.register_builtin_method(Type::integer(), "to_s", Type::string());
|
|
623
|
-
|
|
624
|
-
// 3. Create MethodCallBox for value.to_s (inside method body)
|
|
625
|
-
let inner_ret_vtx = genv.new_vertex();
|
|
626
|
-
let inner_box_id = genv.alloc_box_id();
|
|
627
|
-
let inner_call = MethodCallBox::new(
|
|
628
|
-
inner_box_id,
|
|
629
|
-
param_vtx,
|
|
630
|
-
"to_s".to_string(),
|
|
631
|
-
inner_ret_vtx,
|
|
632
|
-
vec![],
|
|
633
|
-
None,
|
|
634
|
-
None,
|
|
635
|
-
);
|
|
636
|
-
genv.register_box(inner_box_id, Box::new(inner_call));
|
|
637
|
-
|
|
638
|
-
// 4. Register user-defined method Formatter#format with return_vertex and param_vertices
|
|
639
|
-
genv.register_user_method(
|
|
640
|
-
Type::instance("Formatter"),
|
|
641
|
-
"format",
|
|
642
|
-
inner_ret_vtx,
|
|
643
|
-
vec![param_vtx],
|
|
644
|
-
None,
|
|
645
|
-
);
|
|
646
|
-
|
|
647
|
-
// 5. Simulate call: Formatter.new.format(42)
|
|
648
|
-
let recv_vtx = genv.new_vertex();
|
|
649
|
-
let recv_src = genv.new_source(Type::instance("Formatter"));
|
|
650
|
-
genv.add_edge(recv_src, recv_vtx);
|
|
651
|
-
|
|
652
|
-
let arg_vtx = genv.new_source(Type::integer()); // argument: 42
|
|
653
|
-
|
|
654
|
-
let call_ret_vtx = genv.new_vertex();
|
|
655
|
-
let call_box_id = genv.alloc_box_id();
|
|
656
|
-
let call_box = MethodCallBox::new(
|
|
657
|
-
call_box_id,
|
|
658
|
-
recv_vtx,
|
|
659
|
-
"format".to_string(),
|
|
660
|
-
call_ret_vtx,
|
|
661
|
-
vec![arg_vtx],
|
|
662
|
-
None,
|
|
663
|
-
None,
|
|
664
|
-
);
|
|
665
|
-
genv.register_box(call_box_id, Box::new(call_box));
|
|
666
|
-
|
|
667
|
-
// Run all boxes
|
|
668
|
-
genv.run_all();
|
|
669
|
-
|
|
670
|
-
// param_vtx should have Integer type (propagated from argument)
|
|
671
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "Integer");
|
|
672
|
-
|
|
673
|
-
// Return type should be String (Integer#to_s -> String)
|
|
674
|
-
assert_eq!(genv.get_vertex(call_ret_vtx).unwrap().show(), "String");
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
#[test]
|
|
678
|
-
fn test_keyword_arg_propagation() {
|
|
679
|
-
let mut genv = GlobalEnv::new();
|
|
680
|
-
|
|
681
|
-
// Simulate: def greet(name:); name; end
|
|
682
|
-
let param_vtx = genv.new_vertex();
|
|
683
|
-
let mut kw_params = HashMap::new();
|
|
684
|
-
kw_params.insert("name".to_string(), param_vtx);
|
|
685
|
-
|
|
686
|
-
genv.register_user_method(
|
|
687
|
-
Type::instance("Greeter"),
|
|
688
|
-
"greet",
|
|
689
|
-
param_vtx, // return vertex = param (returns name)
|
|
690
|
-
vec![],
|
|
691
|
-
Some(kw_params),
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
// Simulate call: Greeter.new.greet(name: "Alice")
|
|
695
|
-
let recv_vtx = genv.new_vertex();
|
|
696
|
-
let recv_src = genv.new_source(Type::instance("Greeter"));
|
|
697
|
-
genv.add_edge(recv_src, recv_vtx);
|
|
698
|
-
|
|
699
|
-
let arg_vtx = genv.new_source(Type::string());
|
|
700
|
-
let mut kwarg_vtxs = HashMap::new();
|
|
701
|
-
kwarg_vtxs.insert("name".to_string(), arg_vtx);
|
|
702
|
-
|
|
703
|
-
let ret_vtx = genv.new_vertex();
|
|
704
|
-
let box_id = genv.alloc_box_id();
|
|
705
|
-
let call_box = MethodCallBox::new(
|
|
706
|
-
box_id,
|
|
707
|
-
recv_vtx,
|
|
708
|
-
"greet".to_string(),
|
|
709
|
-
ret_vtx,
|
|
710
|
-
vec![],
|
|
711
|
-
Some(kwarg_vtxs),
|
|
712
|
-
None,
|
|
713
|
-
);
|
|
714
|
-
genv.register_box(box_id, Box::new(call_box));
|
|
715
|
-
|
|
716
|
-
genv.run_all();
|
|
717
|
-
|
|
718
|
-
// param_vtx should have String type (propagated from keyword argument)
|
|
719
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "String");
|
|
720
|
-
assert_eq!(genv.get_vertex(ret_vtx).unwrap().show(), "String");
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
#[test]
|
|
724
|
-
fn test_keyword_arg_name_mismatch_skipped() {
|
|
725
|
-
let mut genv = GlobalEnv::new();
|
|
726
|
-
|
|
727
|
-
let param_vtx = genv.new_vertex();
|
|
728
|
-
let mut kw_params = HashMap::new();
|
|
729
|
-
kw_params.insert("name".to_string(), param_vtx);
|
|
730
|
-
|
|
731
|
-
genv.register_user_method(
|
|
732
|
-
Type::instance("Greeter"),
|
|
733
|
-
"greet",
|
|
734
|
-
param_vtx,
|
|
735
|
-
vec![],
|
|
736
|
-
Some(kw_params),
|
|
737
|
-
);
|
|
738
|
-
|
|
739
|
-
let recv_vtx = genv.new_vertex();
|
|
740
|
-
let recv_src = genv.new_source(Type::instance("Greeter"));
|
|
741
|
-
genv.add_edge(recv_src, recv_vtx);
|
|
742
|
-
|
|
743
|
-
// Wrong keyword name: "title" instead of "name"
|
|
744
|
-
let arg_vtx = genv.new_source(Type::string());
|
|
745
|
-
let mut kwarg_vtxs = HashMap::new();
|
|
746
|
-
kwarg_vtxs.insert("title".to_string(), arg_vtx);
|
|
747
|
-
|
|
748
|
-
let ret_vtx = genv.new_vertex();
|
|
749
|
-
let box_id = genv.alloc_box_id();
|
|
750
|
-
let call_box = MethodCallBox::new(
|
|
751
|
-
box_id,
|
|
752
|
-
recv_vtx,
|
|
753
|
-
"greet".to_string(),
|
|
754
|
-
ret_vtx,
|
|
755
|
-
vec![],
|
|
756
|
-
Some(kwarg_vtxs),
|
|
757
|
-
None,
|
|
758
|
-
);
|
|
759
|
-
genv.register_box(box_id, Box::new(call_box));
|
|
760
|
-
|
|
761
|
-
genv.run_all();
|
|
762
|
-
|
|
763
|
-
// param_vtx should remain untyped (name mismatch → no propagation)
|
|
764
|
-
assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "untyped");
|
|
765
|
-
}
|
|
766
|
-
}
|
|
@@ -76,68 +76,3 @@ pub enum EdgeUpdate {
|
|
|
76
76
|
Add { src: VertexId, dst: VertexId },
|
|
77
77
|
Remove { src: VertexId, dst: VertexId },
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
#[cfg(test)]
|
|
81
|
-
mod tests {
|
|
82
|
-
use super::*;
|
|
83
|
-
|
|
84
|
-
#[test]
|
|
85
|
-
fn test_change_set_default() {
|
|
86
|
-
let mut cs = ChangeSet::default();
|
|
87
|
-
cs.add_edge(VertexId(1), VertexId(2));
|
|
88
|
-
let updates = cs.reinstall();
|
|
89
|
-
assert_eq!(updates.len(), 1);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
#[test]
|
|
93
|
-
fn test_change_set_add() {
|
|
94
|
-
let mut cs = ChangeSet::new();
|
|
95
|
-
|
|
96
|
-
cs.add_edge(VertexId(1), VertexId(2));
|
|
97
|
-
cs.add_edge(VertexId(2), VertexId(3));
|
|
98
|
-
|
|
99
|
-
let updates = cs.reinstall();
|
|
100
|
-
|
|
101
|
-
assert_eq!(updates.len(), 2);
|
|
102
|
-
assert!(updates.contains(&EdgeUpdate::Add {
|
|
103
|
-
src: VertexId(1),
|
|
104
|
-
dst: VertexId(2)
|
|
105
|
-
}));
|
|
106
|
-
assert!(updates.contains(&EdgeUpdate::Add {
|
|
107
|
-
src: VertexId(2),
|
|
108
|
-
dst: VertexId(3)
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
#[test]
|
|
113
|
-
fn test_change_set_dedup() {
|
|
114
|
-
let mut cs = ChangeSet::new();
|
|
115
|
-
|
|
116
|
-
cs.add_edge(VertexId(1), VertexId(2));
|
|
117
|
-
cs.add_edge(VertexId(1), VertexId(2)); // Duplicate
|
|
118
|
-
|
|
119
|
-
let updates = cs.reinstall();
|
|
120
|
-
|
|
121
|
-
assert_eq!(updates.len(), 1); // Duplicates removed
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
#[test]
|
|
125
|
-
fn test_change_set_remove() {
|
|
126
|
-
let mut cs = ChangeSet::new();
|
|
127
|
-
|
|
128
|
-
// First commit
|
|
129
|
-
cs.add_edge(VertexId(1), VertexId(2));
|
|
130
|
-
cs.add_edge(VertexId(2), VertexId(3));
|
|
131
|
-
cs.reinstall();
|
|
132
|
-
|
|
133
|
-
// Second time: keep only (1,2)
|
|
134
|
-
cs.add_edge(VertexId(1), VertexId(2));
|
|
135
|
-
let updates = cs.reinstall();
|
|
136
|
-
|
|
137
|
-
assert_eq!(updates.len(), 1);
|
|
138
|
-
assert!(updates.contains(&EdgeUpdate::Remove {
|
|
139
|
-
src: VertexId(2),
|
|
140
|
-
dst: VertexId(3)
|
|
141
|
-
}));
|
|
142
|
-
}
|
|
143
|
-
}
|
data/core/src/graph/vertex.rs
CHANGED
|
@@ -96,72 +96,3 @@ impl Default for Vertex {
|
|
|
96
96
|
Self::new()
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
#[cfg(test)]
|
|
101
|
-
mod tests {
|
|
102
|
-
use super::*;
|
|
103
|
-
|
|
104
|
-
#[test]
|
|
105
|
-
fn test_source_type() {
|
|
106
|
-
let src = Source::new(Type::string());
|
|
107
|
-
assert_eq!(src.ty.show(), "String");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
#[test]
|
|
111
|
-
fn test_vertex_empty() {
|
|
112
|
-
let vtx = Vertex::new();
|
|
113
|
-
assert_eq!(vtx.show(), "untyped");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
#[test]
|
|
117
|
-
fn test_vertex_single_type() {
|
|
118
|
-
let mut vtx = Vertex::new();
|
|
119
|
-
|
|
120
|
-
// Add String type
|
|
121
|
-
let propagations = vtx.on_type_added(VertexId(1), vec![Type::string()]);
|
|
122
|
-
assert_eq!(vtx.show(), "String");
|
|
123
|
-
assert_eq!(propagations.len(), 0); // No propagation since no connections
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
#[test]
|
|
127
|
-
fn test_vertex_union_type() {
|
|
128
|
-
let mut vtx = Vertex::new();
|
|
129
|
-
|
|
130
|
-
// Add String type
|
|
131
|
-
vtx.on_type_added(VertexId(1), vec![Type::string()]);
|
|
132
|
-
assert_eq!(vtx.show(), "String");
|
|
133
|
-
|
|
134
|
-
// Add Integer type → becomes Union type
|
|
135
|
-
vtx.on_type_added(VertexId(1), vec![Type::integer()]);
|
|
136
|
-
assert_eq!(vtx.show(), "(Integer | String)");
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
#[test]
|
|
140
|
-
fn test_vertex_propagation() {
|
|
141
|
-
let mut vtx = Vertex::new();
|
|
142
|
-
|
|
143
|
-
// Add connections
|
|
144
|
-
vtx.add_next(VertexId(3));
|
|
145
|
-
vtx.add_next(VertexId(4));
|
|
146
|
-
|
|
147
|
-
// Add type → propagated to connections
|
|
148
|
-
let propagations = vtx.on_type_added(VertexId(1), vec![Type::string()]);
|
|
149
|
-
|
|
150
|
-
assert_eq!(propagations.len(), 2);
|
|
151
|
-
assert!(propagations.contains(&(VertexId(3), vec![Type::string()])));
|
|
152
|
-
assert!(propagations.contains(&(VertexId(4), vec![Type::string()])));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
#[test]
|
|
156
|
-
fn test_vertex_no_duplicate_propagation() {
|
|
157
|
-
let mut vtx = Vertex::new();
|
|
158
|
-
vtx.add_next(VertexId(3));
|
|
159
|
-
|
|
160
|
-
// Add same type twice → only first time propagates
|
|
161
|
-
let prop1 = vtx.on_type_added(VertexId(1), vec![Type::string()]);
|
|
162
|
-
assert_eq!(prop1.len(), 1);
|
|
163
|
-
|
|
164
|
-
let prop2 = vtx.on_type_added(VertexId(1), vec![Type::string()]);
|
|
165
|
-
assert_eq!(prop2.len(), 0); // No propagation since already exists
|
|
166
|
-
}
|
|
167
|
-
}
|