use super::*;
use crate::compiler::Compiler;
use crate::reader::Offset;
use crate::value::{Object, RefValue, Value};
use crate::Error;
use num::ToPrimitive;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone, PartialEq, Eq)]
pub(in crate::compiler) enum ImlValue {
Void,
Shared(Rc<RefCell<ImlValue>>),
Value(RefValue), Local(usize), Global(usize), Parselet(Rc<RefCell<ImlParselet>>),
Name {
offset: Option<Offset>, generic: bool, name: String, },
Instance {
offset: Option<Offset>, target: Box<ImlValue>, config: Vec<(Option<Offset>, Option<String>, ImlValue)>, },
}
impl ImlValue {
pub fn try_resolve(mut self, compiler: &mut Compiler) -> Self {
if self.resolve(compiler) {
return self;
}
let shared = Self::Shared(Rc::new(RefCell::new(self)));
compiler.usages.push(shared.clone());
shared
}
pub fn resolve(&mut self, compiler: &mut Compiler) -> bool {
match self {
Self::Shared(value) => value.borrow_mut().resolve(compiler),
Self::Name { name, .. } => {
if let Some(value) = compiler.get(&name) {
*self = value;
true
} else {
false
}
}
_ => true,
}
}
pub fn into_refvalue(self) -> RefValue {
match self {
Self::Value(value) => value,
_ => unreachable!("{:?} cannot be unwrapped", self),
}
}
pub fn is_callable(&self, without_arguments: bool) -> bool {
match self {
Self::Shared(value) => value.borrow().is_callable(without_arguments),
Self::Value(value) => value.is_callable(without_arguments),
Self::Parselet(parselet) => {
let parselet = parselet.borrow();
if without_arguments {
parselet.signature.len() == 0
|| parselet
.signature
.iter()
.all(|arg| !matches!(arg.1, Self::Void))
} else {
true
}
}
_ => false,
}
}
pub fn is_consuming(&self) -> bool {
match self {
Self::Shared(value) => value.borrow().is_consuming(),
Self::Name { name, .. } => crate::utils::identifier_is_consumable(name),
Self::Value(value) => value.is_consuming(),
Self::Parselet(parselet) => parselet.borrow().consuming,
_ => false,
}
}
pub fn compile_load(&self, program: &mut ImlProgram, ops: &mut Vec<Op>) {
match self {
ImlValue::Shared(value) => return value.borrow().compile_load(program, ops),
ImlValue::Value(value) => match &*value.borrow() {
Value::Void => return ops.push(Op::PushVoid),
Value::Null => return ops.push(Op::PushNull),
Value::True => return ops.push(Op::PushTrue),
Value::False => return ops.push(Op::PushFalse),
Value::Int(i) => match i.to_i64() {
Some(0) => return ops.push(Op::Push0),
Some(1) => return ops.push(Op::Push1),
_ => {}
},
_ => {}
},
ImlValue::Parselet(_) => {}
ImlValue::Local(addr) => return ops.push(Op::LoadFast(*addr)),
ImlValue::Global(addr) => return ops.push(Op::LoadGlobal(*addr)),
ImlValue::Name { name, .. } => {
program.errors.push(Error::new(
None,
format!("Use of unresolved symbol '{}'", name),
));
return;
}
_ => todo!(),
}
ops.push(Op::LoadStatic(program.register(self)))
}
pub fn compile_call(
&self,
program: &mut ImlProgram,
args: Option<(usize, bool)>,
ops: &mut Vec<Op>,
) {
match self {
ImlValue::Shared(value) => return value.borrow().compile_call(program, args, ops),
ImlValue::Local(addr) => ops.push(Op::LoadFast(*addr)),
ImlValue::Global(addr) => ops.push(Op::LoadGlobal(*addr)),
ImlValue::Name { name, .. } => {
program.errors.push(Error::new(
None,
format!("Call to unresolved symbol '{}'", name),
));
return;
}
value => {
let idx = program.register(value);
match args {
Some((args, nargs)) => {
if args == 0 && !nargs {
ops.push(Op::CallStatic(idx));
} else if args > 0 && !nargs {
ops.push(Op::CallStaticArg(Box::new((idx, args))));
} else {
ops.push(Op::CallStaticArgNamed(Box::new((idx, args))));
}
}
None => {
if value.is_callable(true) {
ops.push(Op::CallStatic(idx));
} else {
ops.push(Op::LoadStatic(idx));
}
}
}
return;
}
_ => todo!(),
}
match args {
Some((args, nargs)) => {
if args == 0 && nargs == false {
ops.push(Op::Call);
} else if args > 0 && nargs == false {
ops.push(Op::CallArg(args));
} else {
ops.push(Op::CallArgNamed(args));
}
}
None => ops.push(Op::CallOrCopy),
}
}
}
impl std::fmt::Debug for ImlValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Void => write!(f, "void"),
Self::Shared(value) => value.borrow().fmt(f),
Self::Value(v) => v.borrow().fmt(f),
Self::Parselet { .. } => write!(f, "{}", self),
Self::Local(addr) => write!(f, "local@{}", addr),
Self::Global(addr) => write!(f, "global@{}", addr),
Self::Name { name, .. } => write!(f, "{}", name),
_ => todo!(),
}
}
}
impl std::fmt::Display for ImlValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Void => write!(f, "void"),
Self::Shared(value) => value.borrow().fmt(f),
Self::Value(value) => write!(f, "{}", value.repr()),
Self::Parselet(parselet) => {
write!(
f,
"{}",
parselet
.borrow()
.name
.as_deref()
.unwrap_or("<anonymous parselet>")
)?;
Ok(())
}
Self::Name { name, .. } => write!(f, "{}", name),
_ => todo!(),
}
}
}
impl std::hash::Hash for ImlValue {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Value(v) => {
state.write_u8('v' as u8);
v.hash(state)
}
Self::Parselet(parselet) => {
state.write_u8('p' as u8);
parselet.borrow().hash(state);
}
other => unreachable!("{:?} is unhashable", other),
}
}
}
impl From<RefValue> for ImlValue {
fn from(value: RefValue) -> Self {
Self::Value(value)
}
}