mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
feat: 实现 SequenceExpression + 带标签 break/continue + EmptyStatement
This commit is contained in:
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user