Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions crates/plotnik-lib/src/compile/capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ impl Compiler<'_> {
effects
}

/// Check if a quantifier body needs Node effect before Push.
///
/// For scalar array elements (simple named nodes, not structs/enums/refs),
/// we need [Node, Push] to capture the matched node value.
/// For structured elements, EndObj/EndEnum/Call already provides the value.
pub(super) fn quantifier_needs_node_for_push(&self, expr: &Expr) -> bool {
if let Expr::QuantifiedExpr(quant) = expr
&& let Some(body) = quant.inner()
{
!inner_creates_scope(&body) && !self.is_ref_returning_structured(&body)
} else {
true
}
}

/// Check if expr is (or wraps) a ref returning a structured type.
///
/// For such refs, we skip the Node effect in captures - the Call leaves
Expand Down
50 changes: 45 additions & 5 deletions crates/plotnik-lib/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ mod sequences;
use indexmap::IndexMap;
use plotnik_core::{Interner, NodeFieldId, NodeTypeId, Symbol};

use crate::bytecode::ir::{Instruction, Label, ReturnIR};
use crate::bytecode::Nav;
use crate::bytecode::ir::{EffectIR, Instruction, Label, MatchIR, ReturnIR};
use crate::bytecode::{EffectOpcode, Nav};
use crate::parser::ast::Expr;

use crate::emit::StringTableBuilder;
use crate::analyze::symbol_table::SymbolTable;
use crate::analyze::type_check::{DefId, TypeContext};
use crate::analyze::type_check::{DefId, TypeContext, TypeShape};

pub use capture::CaptureEffects;
use scope::StructScope;
Expand Down Expand Up @@ -157,8 +157,48 @@ impl<'a> Compiler<'a> {
self.instructions
.push(Instruction::Return(ReturnIR { label: return_label }));

// Compile body with root scope, targeting return instruction
let body_entry = if let Some(type_id) = self.type_ctx.get_def_type(def_id) {
// Check if definition returns a struct type - if so, wrap in Obj/EndObj
// This ensures recursive calls properly scope their captures
let def_returns_struct = self
.type_ctx
.get_def_type(def_id)
.and_then(|tid| self.type_ctx.get_type(tid))
.is_some_and(|shape| matches!(shape, TypeShape::Struct(_)));

let body_entry = if def_returns_struct {
let type_id = self.type_ctx.get_def_type(def_id).expect("checked above");

// Emit EndObj → Return
let endobj_label = self.fresh_label();
self.instructions.push(Instruction::Match(MatchIR {
label: endobj_label,
nav: Nav::Stay,
node_type: None,
node_field: None,
pre_effects: vec![],
neg_fields: vec![],
post_effects: vec![EffectIR::simple(EffectOpcode::EndObj, 0)],
successors: vec![return_label],
}));

// Compile body with scope, targeting EndObj
let inner_entry = self.with_scope(type_id, |this| this.compile_expr(body, endobj_label));

// Emit Obj → inner_entry
let obj_label = self.fresh_label();
self.instructions.push(Instruction::Match(MatchIR {
label: obj_label,
nav: Nav::Stay,
node_type: None,
node_field: None,
pre_effects: vec![EffectIR::simple(EffectOpcode::Obj, 0)],
neg_fields: vec![],
post_effects: vec![],
successors: vec![inner_entry],
}));

obj_label
} else if let Some(type_id) = self.type_ctx.get_def_type(def_id) {
self.with_scope(type_id, |this| this.compile_expr(body, return_label))
} else {
self.compile_expr(body, return_label)
Expand Down
10 changes: 8 additions & 2 deletions crates/plotnik-lib/src/compile/quantifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,15 @@ impl Compiler<'_> {
let match_endarr = self.emit_endarr_step(&capture_effects, &outer_capture.post, match_exit);
let skip_endarr = self.emit_endarr_step(&capture_effects, &outer_capture.post, skip_exit);

// Compile inner star with Push effects and split exits
let push_effects = CaptureEffects {
post: vec![EffectIR::simple(EffectOpcode::Push, 0)],
post: if self.quantifier_needs_node_for_push(inner) {
vec![
EffectIR::simple(EffectOpcode::Node, 0),
EffectIR::simple(EffectOpcode::Push, 0),
]
} else {
vec![EffectIR::simple(EffectOpcode::Push, 0)]
},
};
let inner_entry =
self.compile_star_for_array_with_exits(inner, match_endarr, skip_endarr, nav_override, push_effects);
Expand Down
9 changes: 8 additions & 1 deletion crates/plotnik-lib/src/compile/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,14 @@ impl Compiler<'_> {
}));

let push_effects = CaptureEffects {
post: vec![EffectIR::simple(EffectOpcode::Push, 0)],
post: if self.quantifier_needs_node_for_push(inner) {
vec![
EffectIR::simple(EffectOpcode::Node, 0),
EffectIR::simple(EffectOpcode::Push, 0),
]
} else {
vec![EffectIR::simple(EffectOpcode::Push, 0)]
},
};
let inner_entry = if let Expr::QuantifiedExpr(quant) = inner {
self.compile_quantified_for_array(quant, endarr_step, nav_override, push_effects)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ M0: S1 → T0 ; value: <Node>
N0: S2 → T1 ; Test

[entrypoints]
Test = 1 :: T1
Test = 01 :: T1

[transitions]
0 ε
00 ε ◼

Test:
1 ε 2
2 ε 5, 7
4 ▶
5 (identifier) [Node Set(M0)] 4
7 (number) [Node Set(M0)] 4
01 ε 02
02 ε [Obj] 04
04 ε 09, 11
06 ▶
07 ε [EndObj] 06
09 (identifier) [Node Set(M0)] 07
11 (number) [Node Set(M0)] 07
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ Test = 01 :: T4

Test:
01 ε 02
02 ε 09, 15
04 ▶
05 ε [EndEnum Set(M4)] 04
07 (identifier) [Node Set(M0)] 05
09 ε [Enum(M2)] 07
11 ε [EndEnum Set(M4)] 04
13 (number) [Node Set(M1)] 11
15 ε [Enum(M3)] 13
02 ε [Obj] 04
04 ε 13, 19
06 ▶
07 ε [EndObj] 06
09 ε [EndEnum Set(M4)] 07
11 (identifier) [Node Set(M0)] 09
13 ε [Enum(M2)] 11
15 ε [EndEnum Set(M4)] 07
17 (number) [Node Set(M1)] 15
19 ε [Enum(M3)] 17
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,31 @@ Test = 01 :: T6

Test:
01 ε 02
02 (object) 03
03 ε [Arr] 05
05 ε 29, 11
07 ε [EndArr Set(M5)] 09
09 △ 10
10 ▶
11 ε [EndArr Set(M5)] 10
13 ε [EndObj Push] 49
15 ε [EndEnum Set(M4)] 13
17 ▽ (pair) [Node Set(M0)] 15
19 ε [Enum(M2)] 17
21 ε [EndEnum Set(M4)] 13
23 ▽ (shorthand_property_identifier) [Node Set(M1)] 21
25 ε [Enum(M3)] 23
27 ε 19, 25
29 ε [Obj] 27
31 ε [EndObj Push] 49
33 ε [EndEnum Set(M4)] 31
35 ▷ (pair) [Node Set(M0)] 33
37 ε [Enum(M2)] 35
39 ε [EndEnum Set(M4)] 31
41 ▷ (shorthand_property_identifier) [Node Set(M1)] 39
43 ε [Enum(M3)] 41
45 ε 37, 43
47 ε [Obj] 45
49 ε 47, 07
02 ε [Obj] 04
04 (object) 05
05 ε [Arr] 07
07 ε 33, 15
09 ε [EndArr Set(M5)] 11
11 △ 13
12 ▶
13 ε [EndObj] 12
15 ε [EndArr Set(M5)] 13
17 ε [EndObj Push] 53
19 ε [EndEnum Set(M4)] 17
21 ▽ (pair) [Node Set(M0)] 19
23 ε [Enum(M2)] 21
25 ε [EndEnum Set(M4)] 17
27 ▽ (shorthand_property_identifier) [Node Set(M1)] 25
29 ε [Enum(M3)] 27
31 ε 23, 29
33 ε [Obj] 31
35 ε [EndObj Push] 53
37 ε [EndEnum Set(M4)] 35
39 ▷ (pair) [Node Set(M0)] 37
41 ε [Enum(M2)] 39
43 ε [EndEnum Set(M4)] 35
45 ▷ (shorthand_property_identifier) [Node Set(M1)] 43
47 ε [Enum(M3)] 45
49 ε 41, 47
51 ε [Obj] 49
53 ε 51, 09
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ Test = 01 :: T1

Test:
01 ε 02
02 (program) 03
03 ε 06, 08
05 ▶
06 ▽ (identifier) [Node Set(M0)] 10
08 ▽ (number) [Node Set(M0)] 10
10 △ 05
02 ε [Obj] 04
04 (program) 05
05 ε 10, 12
07 ▶
08 ε [EndObj] 07
10 ▽ (identifier) [Node Set(M0)] 14
12 ▽ (number) [Node Set(M0)] 14
14 △ 08
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ Test = 01 :: T1

Test:
01 ε 02
02 ε 07, 11
04 ▶
05 (identifier) [Node Set(M0)] 04
07 ε [Null Set(M1)] 05
09 (number) [Node Set(M1)] 04
11 ε [Null Set(M0)] 09
02 ε [Obj] 04
04 ε 11, 15
06 ▶
07 ε [EndObj] 06
09 (identifier) [Node Set(M0)] 07
11 ε [Null Set(M1)] 09
13 (number) [Node Set(M1)] 07
15 ε [Null Set(M0)] 13
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ Test = 01 :: T1

Test:
01 ε 02
02 ε 07, 11
04 ▶
05 (identifier) [Node Set(M0)] 04
07 ε [Null Set(M1)] 05
09 (string) [Node Set(M1)] 04
11 ε [Null Set(M0)] 09
02 ε [Obj] 04
04 ε 11, 15
06 ▶
07 ε [EndObj] 06
09 (identifier) [Node Set(M0)] 07
11 ε [Null Set(M1)] 09
13 (string) [Node Set(M1)] 07
15 ε [Null Set(M0)] 13
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ Test = 1 :: T1

Test:
1 ε 2
2 (identifier) [Node Set(M0)] 4
4 ▶
2 ε [Obj] 4
4 (identifier) [Node Set(M0)] 6
6 ε [EndObj] 8
8 ▶
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ Test = 01 :: T1

Test:
01 ε 02
02 (a) [Node Set(M0)] 04
04 ▽ (b) [Node Set(M1)] 06
06 ▽ (c) [Node Set(M2)] 08
08 ▽ (d) [Node Set(M3)] 10
10 △ 11
11 △ 12
02 ε [Obj] 04
04 (a) [Node Set(M0)] 06
06 ▽ (b) [Node Set(M1)] 08
08 ▽ (c) [Node Set(M2)] 10
10 ▽ (d) [Node Set(M3)] 12
12 △ 13
13 ▶
13 △ 14
14 △ 15
15 ε [EndObj] 17
17 ▶
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ M3: S2 → T0 ; b: <Node>
N0: S3 → T1 ; Test

[entrypoints]
Test = 1 :: T1
Test = 01 :: T1

[transitions]
0 ε
00 ε ◼

Test:
1 ε 2
2 (binary_expression) 3
3 ▽ (identifier) [Node Set(M0)] 5
5 ▷ (number) [Node Set(M1)] 7
7 △ 8
8 ▶
01 ε 02
02 ε [Obj] 04
04 (binary_expression) 05
05 ▽ (identifier) [Node Set(M0)] 07
07 ▷ (number) [Node Set(M1)] 09
09 △ 10
10 ε [EndObj] 12
12 ▶
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ Test = 01 :: T1

Test:
01 ε 02
02 (a) [Node Set(M0)] 04
04 ▽ (b) [Node Set(M1)] 06
06 ▽ (c) [Node Set(M2)] 08
08 △ 09
09 △ 10
10 ▶
02 ε [Obj] 04
04 (a) [Node Set(M0)] 06
06 ▽ (b) [Node Set(M1)] 08
08 ▽ (c) [Node Set(M2)] 10
10 △ 11
11 △ 12
12 ε [EndObj] 14
14 ▶
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ Test = 01 :: T3
Test:
01 ε 02
02 ε [Obj] 04
04 ε 13, 15
06 ▶
07 ε [EndObj Set(M2)] 06
09 ε [EndObj Set(M1)] 07
11 (identifier) [Node Set(M0)] 09
13 ε [Obj] 11
15 ε [Null Set(M1)] 07
04 ε [Obj] 06
06 ε 17, 19
08 ▶
09 ε [EndObj] 08
11 ε [EndObj Set(M2)] 09
13 ε [EndObj Set(M1)] 11
15 (identifier) [Node Set(M0)] 13
17 ε [Obj] 15
19 ε [Null Set(M1)] 11
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ Test = 01 :: T2
Test:
01 ε 02
02 ε [Obj] 04
04 (a) [Node Set(M0)] 06
06 ▷ (b) [Node Set(M1)] 08
08 ε [EndObj Set(M2)] 10
10 ▶
04 ε [Obj] 06
06 (a) [Node Set(M0)] 08
08 ▷ (b) [Node Set(M1)] 10
10 ε [EndObj Set(M2)] 12
12 ε [EndObj] 14
14 ▶
Loading