mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
feat: 支持自增自减运算符
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use oxide_parser::{Expression, ForStatementInit, Statement};
|
||||
use oxide_parser::{Expression, ForStatementInit, SimpleAssignmentTarget, Statement};
|
||||
|
||||
use crate::compiler::{is_side_effect_free, CompileCtx, Compiler, Label};
|
||||
|
||||
@@ -314,6 +314,15 @@ impl Compiler {
|
||||
Expression::ParenthesizedExpression(p) => {
|
||||
self.count_expression(&p.expression, ctx);
|
||||
}
|
||||
Expression::UpdateExpression(update) => match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
_ => {
|
||||
ctx.alloc_reg();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST or LOAD_VAR
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::compiler::Label;
|
||||
use crate::opcode::{self, OpCode};
|
||||
use oxide_parser::{
|
||||
AssignmentOperator, Expression, ForStatementInit, ForStatementLeft, Statement, UnaryOperator,
|
||||
AssignmentOperator, Expression, ForStatementInit, ForStatementLeft, SimpleAssignmentTarget,
|
||||
Statement, UnaryOperator, UpdateOperator,
|
||||
};
|
||||
|
||||
use crate::compiler::{is_int_literal, is_side_effect_free, BinaryOperator, CompileCtx, Compiler};
|
||||
@@ -655,6 +656,22 @@ impl Compiler {
|
||||
Err("assignment target not supported".into())
|
||||
}
|
||||
}
|
||||
Expression::UpdateExpression(update) => match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => {
|
||||
let name = id.name.as_str();
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let result_reg = ctx.alloc_reg();
|
||||
let op = match (update.operator, update.prefix) {
|
||||
(UpdateOperator::Increment, true) => OpCode::INC_PRE,
|
||||
(UpdateOperator::Increment, false) => OpCode::INC_POST,
|
||||
(UpdateOperator::Decrement, true) => OpCode::DEC_PRE,
|
||||
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
|
||||
};
|
||||
ctx.emit(opcode::encode(op, var_reg, result_reg, result_reg));
|
||||
Ok(result_reg)
|
||||
}
|
||||
_ => Err("member update not yet supported".into()),
|
||||
},
|
||||
Expression::Identifier(ident) => {
|
||||
let var_reg = ctx.lookup(ident.name.as_str())?;
|
||||
let r = ctx.alloc_reg();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use oxide_parser::{Expression, ForStatementInit, Statement};
|
||||
use oxide_parser::{Expression, ForStatementInit, SimpleAssignmentTarget, Statement};
|
||||
|
||||
pub fn structural_hash(program: &oxide_parser::Program) -> u64 {
|
||||
use std::hash::Hasher;
|
||||
@@ -160,6 +160,26 @@ fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher) {
|
||||
std::mem::discriminant(&assign.operator).hash(h);
|
||||
hash_expression(&assign.right, h);
|
||||
}
|
||||
Expression::UpdateExpression(update) => {
|
||||
12u8.hash(h);
|
||||
std::mem::discriminant(&update.operator).hash(h);
|
||||
update.prefix.hash(h);
|
||||
match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => {
|
||||
0u8.hash(h);
|
||||
}
|
||||
SimpleAssignmentTarget::StaticMemberExpression(member) => {
|
||||
1u8.hash(h);
|
||||
hash_expression(&member.object, h);
|
||||
}
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(member) => {
|
||||
2u8.hash(h);
|
||||
hash_expression(&member.object, h);
|
||||
hash_expression(&member.expression, h);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
std::mem::discriminant(expr).hash(h);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@ pub enum OpCode {
|
||||
JMP = 0x20,
|
||||
JMP_IF_FALSE = 0x21,
|
||||
JMP_IF_TRUE = 0x22,
|
||||
|
||||
// -- Update (0x25-0x28) --
|
||||
INC_PRE = 0x25,
|
||||
INC_POST = 0x26,
|
||||
DEC_PRE = 0x27,
|
||||
DEC_POST = 0x28,
|
||||
|
||||
FOR_IN_INIT = 0x29,
|
||||
FOR_IN_NEXT = 0x2A,
|
||||
FOR_IN_DONE = 0x2B,
|
||||
@@ -132,6 +139,10 @@ impl TryFrom<u8> for OpCode {
|
||||
0x20 => Ok(OpCode::JMP),
|
||||
0x21 => Ok(OpCode::JMP_IF_FALSE),
|
||||
0x22 => Ok(OpCode::JMP_IF_TRUE),
|
||||
0x25 => Ok(OpCode::INC_PRE),
|
||||
0x26 => Ok(OpCode::INC_POST),
|
||||
0x27 => Ok(OpCode::DEC_PRE),
|
||||
0x28 => Ok(OpCode::DEC_POST),
|
||||
0x29 => Ok(OpCode::FOR_IN_INIT),
|
||||
0x2A => Ok(OpCode::FOR_IN_NEXT),
|
||||
0x2B => Ok(OpCode::FOR_IN_DONE),
|
||||
@@ -197,6 +208,10 @@ impl fmt::Display for OpCode {
|
||||
OpCode::JMP => "JMP",
|
||||
OpCode::JMP_IF_FALSE => "JMP_IF_FALSE",
|
||||
OpCode::JMP_IF_TRUE => "JMP_IF_TRUE",
|
||||
OpCode::INC_PRE => "INC_PRE",
|
||||
OpCode::INC_POST => "INC_POST",
|
||||
OpCode::DEC_PRE => "DEC_PRE",
|
||||
OpCode::DEC_POST => "DEC_POST",
|
||||
OpCode::FOR_IN_INIT => "FOR_IN_INIT",
|
||||
OpCode::FOR_IN_NEXT => "FOR_IN_NEXT",
|
||||
OpCode::FOR_IN_DONE => "FOR_IN_DONE",
|
||||
|
||||
@@ -242,3 +242,59 @@ fn compile_compound_exp_no_error() {
|
||||
});
|
||||
assert!(result.is_ok(), "x**=2 should compile without error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_inc_pre() {
|
||||
let module = compile_source("++x");
|
||||
assert!(
|
||||
module
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| opcode::opcode(i) == OpCode::INC_PRE),
|
||||
"++x should emit INC_PRE opcode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_inc_post() {
|
||||
let module = compile_source("x++");
|
||||
assert!(
|
||||
module
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| opcode::opcode(i) == OpCode::INC_POST),
|
||||
"x++ should emit INC_POST opcode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_inc_no_error() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
compile_source("x++");
|
||||
});
|
||||
assert!(result.is_ok(), "x++ should compile without error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_dec_no_error() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
compile_source("x--");
|
||||
});
|
||||
assert!(result.is_ok(), "x-- should compile without error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_inc_dec_diff_opcodes() {
|
||||
let m1 = compile_source("++x");
|
||||
let m2 = compile_source("--x");
|
||||
let has_inc_pre = m1
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| opcode::opcode(i) == OpCode::INC_PRE);
|
||||
let has_dec_pre = m2
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| opcode::opcode(i) == OpCode::DEC_PRE);
|
||||
assert!(has_inc_pre, "++x should emit INC_PRE");
|
||||
assert!(has_dec_pre, "--x should emit DEC_PRE");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user