use std::cell::RefCell;
use std::fmt::Display;
use std::rc::Rc;
use react_estree::{BinaryOperator, JsValue};
use crate::{Function, IdentifierId, InstructionId, ScopeId, Type};
#[derive(Debug)]
pub struct Instruction {
pub id: InstructionId,
pub lvalue: IdentifierOperand,
pub value: InstructionValue,
}
impl Instruction {
pub fn each_lvalue<F>(&mut self, mut f: F)
where
F: FnMut(&mut IdentifierOperand),
{
match &mut self.value {
InstructionValue::DeclareContext(instr) => {
f(&mut instr.lvalue.identifier);
}
InstructionValue::DeclareLocal(instr) => {
f(&mut instr.lvalue.identifier);
}
InstructionValue::StoreLocal(instr) => {
f(&mut instr.lvalue.identifier);
}
InstructionValue::Destructure(instr) => {
instr.pattern.each_operand(&mut f);
}
InstructionValue::Array(_)
| InstructionValue::Binary(_)
| InstructionValue::Call(_)
| InstructionValue::LoadContext(_)
| InstructionValue::LoadGlobal(_)
| InstructionValue::LoadLocal(_)
| InstructionValue::Primitive(_)
| InstructionValue::Function(_)
| InstructionValue::JSXElement(_)
| InstructionValue::Tombstone => {}
}
f(&mut self.lvalue);
}
pub fn try_each_lvalue<F, E>(&mut self, mut f: F) -> Result<(), E>
where
F: FnMut(&mut IdentifierOperand) -> Result<(), E>,
{
match &mut self.value {
InstructionValue::DeclareContext(instr) => {
f(&mut instr.lvalue.identifier)?;
}
InstructionValue::DeclareLocal(instr) => {
f(&mut instr.lvalue.identifier)?;
}
InstructionValue::StoreLocal(instr) => {
f(&mut instr.lvalue.identifier)?;
}
InstructionValue::Destructure(instr) => instr.pattern.try_each_operand(&mut f)?,
InstructionValue::Array(_)
| InstructionValue::Binary(_)
| InstructionValue::Call(_)
| InstructionValue::LoadContext(_)
| InstructionValue::LoadGlobal(_)
| InstructionValue::LoadLocal(_)
| InstructionValue::Primitive(_)
| InstructionValue::Function(_)
| InstructionValue::JSXElement(_)
| InstructionValue::Tombstone => {}
}
f(&mut self.lvalue)?;
Ok(())
}
pub fn each_rvalue<F>(&mut self, mut f: F)
where
F: FnMut(&mut IdentifierOperand),
{
match &mut self.value {
InstructionValue::Array(value) => {
for item in &mut value.elements {
match item {
Some(PlaceOrSpread::Place(item)) => f(item),
Some(PlaceOrSpread::Spread(item)) => f(item),
None => {}
}
}
}
InstructionValue::Binary(value) => {
f(&mut value.left);
f(&mut value.right);
}
InstructionValue::Call(value) => {
f(&mut value.callee);
for arg in &mut value.arguments {
match arg {
PlaceOrSpread::Place(item) => f(item),
PlaceOrSpread::Spread(item) => f(item),
}
}
}
InstructionValue::StoreLocal(value) => {
f(&mut value.value);
}
InstructionValue::Function(value) => {
for dep in &mut value.dependencies {
f(dep)
}
}
InstructionValue::JSXElement(value) => {
f(&mut value.tag);
for attr in &mut value.props {
match attr {
JSXAttribute::Spread { argument } => f(argument),
JSXAttribute::Attribute { name: _, value } => f(value),
}
}
if let Some(children) = &mut value.children {
for child in children {
f(child)
}
}
}
InstructionValue::Destructure(value) => {
f(&mut value.value);
}
InstructionValue::LoadLocal(value) => {
f(&mut value.place);
}
InstructionValue::DeclareContext(_)
| InstructionValue::LoadContext(_)
| InstructionValue::LoadGlobal(_)
| InstructionValue::DeclareLocal(_)
| InstructionValue::Primitive(_)
| InstructionValue::Tombstone => {}
}
}
}
#[derive(Debug)]
pub enum InstructionValue {
Array(Array),
Binary(Binary),
Call(Call),
DeclareContext(DeclareContext),
DeclareLocal(DeclareLocal),
Destructure(Destructure),
Function(FunctionExpression),
JSXElement(JSXElement),
LoadContext(LoadContext),
LoadGlobal(LoadGlobal),
LoadLocal(LoadLocal),
Primitive(Primitive),
StoreLocal(StoreLocal),
Tombstone,
}
#[derive(Debug)]
pub struct Array {
pub elements: Vec<Option<PlaceOrSpread>>,
}
#[derive(Debug)]
pub enum PlaceOrSpread {
Place(IdentifierOperand),
Spread(IdentifierOperand),
}
#[derive(Debug)]
pub struct Binary {
pub left: IdentifierOperand,
pub operator: BinaryOperator,
pub right: IdentifierOperand,
}
#[derive(Debug)]
pub struct Call {
pub callee: IdentifierOperand,
pub arguments: Vec<PlaceOrSpread>,
}
#[derive(Debug)]
pub struct FunctionExpression {
pub dependencies: Vec<IdentifierOperand>,
pub lowered_function: Box<Function>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Primitive {
pub value: JsValue,
}
#[derive(Debug)]
pub struct LoadLocal {
pub place: IdentifierOperand,
}
#[derive(Debug)]
pub struct LoadContext {
pub place: IdentifierOperand,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct LoadGlobal {
pub name: String,
}
#[derive(Debug)]
pub struct DeclareLocal {
pub lvalue: LValue,
}
#[derive(Debug)]
pub struct DeclareContext {
pub lvalue: LValue,
}
#[derive(Debug)]
pub struct StoreLocal {
pub lvalue: LValue,
pub value: IdentifierOperand,
}
#[derive(Debug)]
pub struct JSXElement {
pub tag: IdentifierOperand,
pub props: Vec<JSXAttribute>,
pub children: Option<Vec<IdentifierOperand>>,
}
#[derive(Debug)]
pub enum JSXAttribute {
Spread {
argument: IdentifierOperand,
},
Attribute {
name: String,
value: IdentifierOperand,
},
}
#[derive(Debug)]
pub struct Destructure {
pub kind: InstructionKind,
pub pattern: DestructurePattern,
pub value: IdentifierOperand,
}
#[derive(Debug)]
pub enum DestructurePattern {
Array(Vec<ArrayDestructureItem>),
Object(Vec<ObjectDestructureItem>),
}
impl DestructurePattern {
pub fn try_each_operand<E, F>(&mut self, f: &mut F) -> Result<(), E>
where
F: FnMut(&mut IdentifierOperand) -> Result<(), E>,
{
match self {
Self::Array(elements) => {
for item in elements {
match item {
ArrayDestructureItem::Hole => { }
ArrayDestructureItem::Value(item) => f(item)?,
ArrayDestructureItem::Spread(item) => f(item)?,
}
}
}
Self::Object(properties) => {
for property in properties {
match property {
ObjectDestructureItem::Property(property) => {
f(&mut property.value)?;
}
ObjectDestructureItem::Spread(property) => f(property)?,
}
}
}
}
Ok(())
}
pub fn each_operand<F>(&mut self, f: &mut F)
where
F: FnMut(&mut IdentifierOperand),
{
match self {
Self::Array(elements) => {
for item in elements {
match item {
ArrayDestructureItem::Hole => { }
ArrayDestructureItem::Value(item) => f(item),
ArrayDestructureItem::Spread(item) => f(item),
}
}
}
Self::Object(properties) => {
for property in properties {
match property {
ObjectDestructureItem::Property(property) => {
f(&mut property.value);
}
ObjectDestructureItem::Spread(property) => f(property),
}
}
}
}
}
}
#[derive(Debug)]
pub enum ArrayDestructureItem {
Hole,
Value(IdentifierOperand),
Spread(IdentifierOperand),
}
#[derive(Debug)]
pub enum ObjectDestructureItem {
Property(ObjectDestructureProperty),
Spread(IdentifierOperand),
}
#[derive(Debug)]
pub struct ObjectDestructureProperty {
pub name: String,
pub value: IdentifierOperand,
}
#[derive(Clone, Debug)]
pub struct IdentifierOperand {
pub identifier: Identifier,
pub effect: Option<Effect>,
}
#[derive(Debug)]
pub struct LValue {
pub identifier: IdentifierOperand,
pub kind: InstructionKind,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum InstructionKind {
Const,
Let,
Reassign,
}
impl Display for InstructionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Const => f.write_str("Const"),
Self::Let => f.write_str("Let"),
Self::Reassign => f.write_str("Reassign"),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Effect {
Freeze,
Read,
Capture,
ConditionallyMutate,
Mutate,
Store,
}
impl Effect {
pub fn is_mutable(self) -> bool {
match self {
Self::Capture | Self::Store | Self::ConditionallyMutate | Self::Mutate => true,
Self::Read | Self::Freeze => false,
}
}
}
impl Display for Effect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Effect::Capture => "capture",
Effect::ConditionallyMutate => "mutate?",
Effect::Freeze => "freeze",
Effect::Mutate => "mutate",
Effect::Read => "read",
Effect::Store => "store",
})
}
}
#[derive(Clone, Debug)]
pub struct Identifier {
pub id: IdentifierId,
pub name: Option<String>,
pub data: Rc<RefCell<IdentifierData>>,
}
#[derive(Debug)]
pub struct IdentifierData {
pub mutable_range: MutableRange,
pub scope: Option<ReactiveScope>,
pub type_: Type,
}
#[derive(Clone, Debug)]
pub struct MutableRange {
pub start: InstructionId,
pub end: InstructionId,
}
impl MutableRange {
pub fn new() -> Self {
Self {
start: InstructionId(0),
end: InstructionId(0),
}
}
}
impl Default for MutableRange {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct ReactiveScope {
pub id: ScopeId,
pub range: MutableRange,
}