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
@@ -82,6 +82,19 @@ impl Compiler {
}
}
pub(in crate::emitter) fn emit_sequence_expression(
&self, seq: &oxide_parser::SequenceExpression, ctx: &mut CompileCtx,
) -> Result<u8, String> {
// Comma operator: evaluate each expression in order, value is the last.
// Emits exactly one expression per element (no extra instruction), so the
// projected-PC count in count_sequence_expression stays aligned.
let mut last = 0u8;
for e in &seq.expressions {
last = self.emit_expression(e, ctx)?;
}
Ok(last)
}
pub(in crate::emitter) fn emit_unsupported_expression(
&self, expr: &Expression, ctx: &mut CompileCtx,
) -> Result<u8, String> {
+5 -2
View File
@@ -967,12 +967,14 @@ impl Compiler {
Statement::ForInStatement(_) => self.emit_for_in_statement(stmt, ctx),
Statement::ForOfStatement(_) => self.emit_for_of_statement(stmt, ctx),
Statement::SwitchStatement(_) => self.emit_switch_statement(stmt, ctx),
Statement::BreakStatement(_) => self.emit_break_statement(ctx),
Statement::ContinueStatement(_) => self.emit_continue_statement(ctx),
Statement::BreakStatement(b) => self.emit_break_statement(b, ctx),
Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx),
Statement::FunctionDeclaration(_) => self.emit_function_declaration_statement(stmt, ctx),
Statement::ClassDeclaration(_) => self.emit_class_declaration_statement(stmt, ctx),
Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx),
Statement::TryStatement(_) => self.emit_try_statement(stmt, ctx),
Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx),
Statement::EmptyStatement(_) => Ok(None),
_ => Ok(None),
}
}
@@ -1006,6 +1008,7 @@ impl Compiler {
Expression::CallExpression(call) => self.emit_call_expression(call, ctx),
Expression::ThisExpression(_) => self.emit_this_expression(ctx),
Expression::RegExpLiteral(lit) => self.emit_reg_exp_literal_expression(lit, ctx),
Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx),
_ => self.emit_unsupported_expression(expr, ctx),
}
}
@@ -28,8 +28,16 @@ impl Compiler {
Ok(None)
}
pub(in crate::emitter) fn emit_break_statement(&self, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let break_label = if let Some(sw_label) = ctx.current_switch() {
pub(in crate::emitter) fn emit_break_statement(
&self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> {
let break_label = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
scope.break_label
} else if let Some(sw_label) = ctx.current_switch() {
*sw_label
} else {
let (bl, _) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
@@ -42,12 +50,56 @@ impl Compiler {
Ok(None)
}
pub(in crate::emitter) fn emit_continue_statement(&self, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
let (_, continue_label) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
let continue_pos = ctx.resolve_label(*continue_label)?;
pub(in crate::emitter) fn emit_continue_statement(
&self, stmt: &oxide_parser::ContinueStatement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> {
let continue_label = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
scope.continue_label.ok_or_else(|| {
format!("SyntaxError: Illegal continue statement: '{name}' does not denote an iteration statement")
})?
} else {
let (_, cl) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
*cl
};
let continue_pos = ctx.resolve_label(continue_label)?;
let offset = (continue_pos as isize) - (ctx.bytecode.len() as isize);
let offset = ctx.checked_jump_offset(offset);
ctx.emit(opcode::encode_jmp(offset));
Ok(None)
}
pub(in crate::emitter) fn emit_labeled_statement(
&self, stmt: &oxide_parser::LabeledStatement, ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> {
let name = stmt.label.name.as_str();
if Self::is_iteration_statement(&stmt.body) {
// Bind the label to the loop's own continue/break targets (the loop
// drains the queue and pops the scope after its body).
ctx.queue_loop_label(name)?;
self.emit_statement(&stmt.body, ctx)?;
} else {
// Non-loop labeled statement: only `break label` is valid; it targets
// the LabeledEnd placed (by the count pass) right after the body.
let id = ctx.next_label_id();
ctx.push_label_scope(name, Label::LabeledEnd(id), None)?;
self.emit_statement(&stmt.body, ctx)?;
ctx.pop_label_scope();
}
Ok(None)
}
pub(in crate::emitter) fn is_iteration_statement(stmt: &Statement) -> bool {
matches!(
stmt,
Statement::WhileStatement(_)
| Statement::DoWhileStatement(_)
| Statement::ForStatement(_)
| Statement::ForInStatement(_)
| Statement::ForOfStatement(_)
)
}
}
@@ -12,6 +12,7 @@ impl Compiler {
let end_label = Label::WhileEnd(id);
ctx.push_loop(end_label, start_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let test_reg = self.emit_expression(&wh.test, ctx)?;
let end_pos = ctx.resolve_label(end_label)?;
@@ -26,6 +27,7 @@ impl Compiler {
let offset = ctx.checked_jump_offset(offset);
ctx.emit(opcode::encode_jmp(offset));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}
@@ -41,6 +43,7 @@ impl Compiler {
let end_label = Label::DoWhileEnd(id);
ctx.push_loop(end_label, start_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
self.emit_statement(&dw.body, ctx)?;
let test_reg = self.emit_expression(&dw.test, ctx)?;
@@ -49,6 +52,7 @@ impl Compiler {
let offset = ctx.checked_jump_offset(offset);
ctx.emit(opcode::encode_jmp_if_true(test_reg, offset));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}
@@ -65,6 +69,7 @@ impl Compiler {
let end_label = Label::ForEnd(id);
ctx.push_loop(end_label, update_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
if let Some(init) = &fr.init {
if let Some(expr) = init.as_expression() {
@@ -102,6 +107,7 @@ impl Compiler {
let offset = ctx.checked_jump_offset(offset);
ctx.emit(opcode::encode_jmp(offset));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
@@ -121,6 +127,7 @@ impl Compiler {
ctx.emit(opcode::encode(OpCode::FOR_IN_INIT, 0, obj_reg, 0));
ctx.push_loop(end_label, start_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let done_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::FOR_IN_DONE, done_reg, 0, 0));
@@ -164,6 +171,7 @@ impl Compiler {
ctx.emit(opcode::encode(OpCode::FOR_IN_CLEANUP, 0, 0, 0));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}
@@ -181,6 +189,7 @@ impl Compiler {
let iter_src_reg = self.emit_expression(&fo.right, ctx)?;
ctx.emit(opcode::encode(OpCode::FOR_OF_INIT, 0, iter_src_reg, 0));
ctx.push_loop(end_label, start_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let has_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::FOR_OF_DONE, has_reg, 0, 0));
@@ -217,6 +226,7 @@ impl Compiler {
let offset = ctx.checked_jump_offset(offset);
ctx.emit(opcode::encode_jmp(offset));
ctx.emit(opcode::encode(OpCode::FOR_OF_CLOSE, 0, 0, 0));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}