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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Normally owned original setup syntax, attached by the selected owner itself.
use super::{
NativeRouteState, NativeTemplateFile, NativeTemplateIssue, NativeTemplateIssueKind as Kind,
NativeTemplateOwner,
};
use crate::file::ScriptUnitId;
use alloc::boxed::Box;
use vize_l1::embed::{
EmbedSource,
syntax::{NativeSyntax, ProgramOptions, parse_program_once},
};
mod bindings;
mod identity;
mod prepare;
mod receipt;
pub use receipt::{NativeSelectedSetup, NativeSetupIssue, NativeSetupIssueKind};
#[cfg(test)]
mod tests;
impl<'a> NativeTemplateOwner<'a> {
/// Parse only this owner's actual selected setup, using its original allocator.
/// The whole stock observation is parked before declaration admission. There
/// is no caller syntax/source/allocator, and either setup route consumes the
/// one selected slot. This grants no whole-SFC or target completion.
///
/// ```compile_fail
/// use vize_l1::embed::syntax::NativeSyntax;
/// use vize_l2::lang::js::NativeTemplateOwner;
/// fn pair<'a>(owner: &mut NativeTemplateOwner<'a>, syntax: NativeSyntax<'a>) {
/// owner.parse_setup_program(syntax);
/// }
/// ```
/// ```compile_fail
/// use vize_l0::Allocator;
/// use vize_l2::lang::js::NativeTemplateOwner;
/// fn replace<'a>(owner: &mut NativeTemplateOwner<'a>, arena: &'a Allocator) {
/// owner.parse_setup_program(arena);
/// }
/// ```
/// The original parser arena remains borrowed by its normal owner:
/// ```compile_fail
/// use vize_l0::{Allocator, config::{VueDialect, VueVersion}};
/// use vize_l1::{container::{Vue, vue::DescriptorOptions}, markup::{SurfaceParseOptions, NativeTemplateComponent}};
/// use vize_l2::lang::js::NativeTemplateOwner;
/// let arena = Allocator::default();
/// let source = "<template></template><script setup>const x=1</script>";
/// let descriptor = Vue.observe_descriptor(&arena, source, DescriptorOptions {
/// version: VueVersion::V3, dialect: VueDialect::Vue, template: SurfaceParseOptions::default(),
/// });
/// let selected = NativeTemplateComponent::parse_in(&arena, descriptor.admitted().unwrap()).unwrap().unwrap();
/// let mut owner = NativeTemplateOwner::new(selected).unwrap_or_else(|_| panic!("owner"));
/// owner.parse_setup_program().unwrap();
/// drop(arena);
/// let _ = owner.retained_setup();
/// ```
pub fn parse_setup_program(&mut self) -> Result<ScriptUnitId, NativeTemplateIssue> {
let span = self.selected.component().block().span();
if !matches!(self.state, NativeRouteState::Scripts) {
return self.refuse(span, Kind::Interrupted);
}
if self.setup.is_some() || self.retained_setup.is_some() {
return self.refuse(span, Kind::DuplicateProgram);
}
if self.selected.ordinary().is_some() {
return self.refuse(span, Kind::UnsupportedOrdinaryScript);
}
if self.selected.has_styles() {
return self.refuse(span, Kind::UnsupportedStyle);
}
let Some(script) = self.selected.setup() else {
return self.refuse(span, Kind::MissingProgram);
};
let block = script.block();
let lang = script.lang();
// A caught parser or declaration-walk unwind cannot revive this slot.
self.state = NativeRouteState::Interrupted;
let source = match EmbedSource::authored(block.root_source(), block.span()) {
Ok(source) => source,
Err(error) => return self.refuse(block.span(), Kind::SetupSource(error)),
};
let syntax = parse_program_once(
self.selected.component().allocator(),
source,
ProgramOptions::module(lang),
);
self.retained_setup = Some(Box::new(syntax));
#[cfg(test)]
tests::after_park();
let Some(syntax) = self.retained_setup.as_deref() else {
return self.refuse(block.span(), Kind::MissingProgram);
};
let Some(admitted) = syntax.admitted_program() else {
let issue = NativeTemplateIssue {
span: block.span(),
kind: Kind::SetupSyntax(
syntax
.hole()
.unwrap_or(vize_l1::embed::syntax::EmbedHole::UnsupportedShape),
),
};
self.state = NativeRouteState::Refused(issue);
return Err(issue);
};
self.state = NativeRouteState::Scripts;
super::program::Parts {
selected: &self.selected,
producer: &mut self.producer,
state: &mut self.state,
ordinary: &mut self.ordinary,
setup: &mut self.setup,
}
.attach(admitted, true)
}
/// Original stock syntax remains available after syntax/admission failure.
/// This getter alone grants no completed File or setup eligibility.
#[must_use]
pub fn retained_setup(&self) -> Option<&NativeSyntax<'a>> {
self.retained_setup.as_deref()
}
}
impl<'a> NativeTemplateFile<'a> {
/// The same normally owned Program, comments and full diagnostics after move.
#[must_use]
pub fn retained_setup(&self) -> Option<&NativeSyntax<'a>> {
self.retained_setup.as_deref()
}
/// Join the original Program to this exact normally completed selected File.
/// The borrowed setup route cannot mint this receipt.
pub fn setup(&self) -> Result<NativeSelectedSetup<'_, 'a>, NativeSetupIssue> {
receipt::checked(self)
}
}