feat: 实现 SequenceExpression + 带标签 break/continue + EmptyStatement

This commit is contained in:
2026-06-23 23:00:20 +08:00
parent 5de79b4253
commit 3ea2353194
11 changed files with 284 additions and 9 deletions
+71
View File
@@ -125,6 +125,17 @@ pub(crate) enum Label {
CatchBody(u32),
FinallyBody(u32),
TryEnd(u32),
LabeledEnd(u32),
}
/// A labeled-statement scope active during emission. `break label` targets
/// `break_label`; `continue label` targets `continue_label` (only set when the
/// labeled statement directly wraps an iteration statement).
#[derive(Debug, Clone)]
pub(crate) struct LabelScope {
pub(crate) name: String,
pub(crate) break_label: Label,
pub(crate) continue_label: Option<Label>,
}
pub(crate) struct CompileCtx {
@@ -139,6 +150,10 @@ pub(crate) struct CompileCtx {
pub(crate) loop_stack: Vec<(Label, Label)>,
#[allow(dead_code)]
pub(crate) switch_stack: Vec<Label>,
/// Active labeled-statement scopes (resolves `break label` / `continue label`).
pub(crate) label_scopes: Vec<LabelScope>,
/// Label names awaiting binding to the next emitted loop's continue target.
pub(crate) pending_loop_labels: Vec<String>,
pub(crate) label_counter: u32,
pub(crate) projected_pc: usize,
pub(crate) builtin_reg_map: Vec<(String, u8)>,
@@ -210,6 +225,8 @@ impl CompileCtx {
label_map: HashMap::new(),
loop_stack: Vec::new(),
switch_stack: Vec::new(),
label_scopes: Vec::new(),
pending_loop_labels: Vec::new(),
label_counter: 0,
projected_pc: 0,
builtin_reg_map: Vec::new(),
@@ -468,6 +485,60 @@ impl CompileCtx {
self.switch_stack.last()
}
pub(crate) fn push_label_scope(
&mut self, name: &str, break_label: Label, continue_label: Option<Label>,
) -> Result<(), String> {
if self.label_scopes.iter().any(|s| s.name == name) {
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.label_scopes.push(LabelScope {
name: name.to_string(),
break_label,
continue_label,
});
Ok(())
}
pub(crate) fn pop_label_scope(&mut self) {
self.label_scopes.pop();
}
pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> {
self.label_scopes.iter().rev().find(|s| s.name == name)
}
/// Queue a label name to be bound to the continue target of the next loop
/// emitted as the labeled statement's body. Rejects duplicates in the active
/// or pending sets.
pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> {
if self.label_scopes.iter().any(|s| s.name == name) || self.pending_loop_labels.iter().any(|n| n == name) {
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.pending_loop_labels.push(name.to_string());
Ok(())
}
/// Drain queued loop labels into active scopes bound to this loop's break and
/// continue targets. Returns how many scopes were pushed (to pop after).
pub(crate) fn take_pending_loop_labels(&mut self, break_label: Label, continue_label: Label) -> usize {
let names = std::mem::take(&mut self.pending_loop_labels);
let count = names.len();
for name in names {
self.label_scopes.push(LabelScope {
name,
break_label,
continue_label: Some(continue_label),
});
}
count
}
pub(crate) fn pop_label_scopes(&mut self, n: usize) {
for _ in 0..n {
self.label_scopes.pop();
}
}
pub(crate) fn is_builtin(&self, name: &str) -> bool {
self.builtin_reg_map.iter().any(|(n, _)| n == name)
}