use std::{collections::HashMap, fmt, path::PathBuf};
use qvnt::qasm::{Ast, Int, Sym};
use crate::{
int_tree::IntTree,
lines::{self, Command, Line},
utils::owned_errors::ToOwnedError,
};
#[derive(Debug)]
pub enum Error {
Inner,
#[allow(dead_code)]
Unimplemented,
Dyn(Box<dyn std::error::Error>),
Quit,
}
const ON_UNEXPECTED: &str = "This error should never occur, contact developpers and provide the way to reproduce this error";
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Inner => write!(f, "Inner functional error. {}", ON_UNEXPECTED),
Error::Unimplemented => write!(f, "Unimplemented function. {}", ON_UNEXPECTED),
Error::Dyn(err) => write!(f, "{}", err),
Error::Quit => write!(f, "Quit signal. {}", ON_UNEXPECTED),
}
}
}
impl<E: std::error::Error + 'static> From<E> for Error {
fn from(e: E) -> Self {
Self::Dyn(e.into())
}
}
pub type Result<T = ()> = std::result::Result<T, Error>;
pub struct Process<'t> {
head: Int<'t>,
int: Int<'t>,
sym: Sym,
storage: HashMap<PathBuf, Ast<'t>>,
}
impl<'t> Process<'t> {
pub fn new(int: Int<'t>) -> Self {
Self {
head: Int::default(),
int: int.clone(),
sym: Sym::new(int),
storage: HashMap::new(),
}
}
pub fn int(&self) -> Int<'t> {
let int = self.int.clone();
unsafe { int.append_int(self.head.clone()) }
}
fn reset(&mut self, int: Int<'t>) {
self.head = Int::default();
self.int = int;
}
fn sym_update(&mut self) {
let int = self.int();
self.sym.init(int);
}
fn sym_go(&mut self) {
self.sym_update();
self.sym.reset();
self.sym.finish();
}
pub fn process(&mut self, int_set: &mut IntTree<'t>, line: String) -> Result {
match line.parse::<Line>() {
Ok(Line::Qasm) => self.process_qasm(crate::program::leak_string(line)),
Ok(Line::Commands(cmds)) => self.process_cmd(int_set, cmds.into_iter()),
Err(err) => Err(err.into()),
}
}
pub fn process_qasm(&mut self, line: &'t str) -> Result {
let ast: Ast<'t> = Ast::from_source(line).map_err(ToOwnedError::own)?;
self.int = self
.int
.clone()
.ast_changes(&mut self.head, ast)
.map_err(ToOwnedError::own)?;
Ok(())
}
pub fn process_cmd(
&mut self,
int_tree: &mut IntTree<'t>,
mut cmds: impl Iterator<Item = Command> + Clone,
) -> Result {
while let Some(cmd) = cmds.next() {
match cmd {
Command::Loop(n) => {
for _ in 0..n {
self.process_cmd(int_tree, cmds.clone())?;
}
break;
}
Command::Tags(tag_cmd) => self.process_tag_cmd(int_tree, tag_cmd)?,
Command::Go => self.sym_go(),
Command::Load(path) => self.load_qasm(int_tree, path)?,
Command::Class => {
self.sym_update();
println!("CReg: {}\n", self.sym.get_class().get())
}
Command::Polar => {
self.sym_update();
println!("QReg polar: {:.4?}\n", self.sym.get_polar_wavefunction());
}
Command::Probs => {
self.sym_update();
println!("QReg probabilities: {:.4?}\n", self.sym.get_probabilities());
}
Command::Ops => println!("Operations: {}\n", self.int().get_ops_tree()),
Command::Names => {
println!(
"QReg: {}\nCReg: {}\n",
self.int().get_q_alias(),
self.int().get_c_alias()
);
}
Command::Help => println!("{}", lines::HELP),
Command::Quit => return Err(Error::Quit),
}
}
Ok(())
}
pub fn process_tag_cmd(
&mut self,
int_tree: &mut IntTree<'t>,
tag_cmd: crate::int_tree::Command,
) -> Result {
use crate::int_tree::Command;
match tag_cmd {
Command::List => println!("{:?}\n", int_tree.keys()),
Command::Create(tag) => {
if !int_tree.commit(&tag, self.head.clone()) {
return Err(lines::Error::ExistedTagName(tag).into());
} else {
unsafe {
self.int = self.int.clone().append_int(std::mem::take(&mut self.head))
};
}
}
Command::Remove(tag) => {
use crate::int_tree::RemoveStatus::*;
match int_tree.remove(&tag) {
Removed => {}
NotFound => return Err(lines::Error::WrongTagName(tag).into()),
IsParent => return Err(lines::Error::TagIsParent(tag).into()),
IsHead => return Err(lines::Error::TagIsHead(tag).into()),
}
}
Command::Checkout(tag) => {
if !int_tree.checkout(&tag) {
return Err(lines::Error::WrongTagName(tag).into());
} else {
let new_int = int_tree.collect_to_head().ok_or(Error::Inner)?;
self.reset(new_int);
}
}
Command::Root => {
if !int_tree.checkout(crate::program::ROOT_TAG) {
return Err(Error::Inner);
} else {
self.reset(Int::default());
}
}
Command::Help => println!("{}", crate::int_tree::HELP),
}
Ok(())
}
pub fn load_qasm(&mut self, int_tree: &mut IntTree<'t>, path: PathBuf) -> Result {
let path_tag = format!("file://{}", path.display());
if int_tree.checkout(&path_tag) {
self.reset(int_tree.collect_to_head().ok_or(Error::Inner)?);
} else {
let default_ast = {
let source = std::fs::read_to_string(path.clone())?;
let source = crate::program::leak_string(source);
Ast::from_source(source).map_err(ToOwnedError::own)?
};
let ast = self.storage.entry(path).or_insert(default_ast).clone();
int_tree.checkout("");
let int = Int::new(ast).map_err(ToOwnedError::own)?;
if !int_tree.commit(&path_tag, int.clone()) {
return Err(Error::Inner);
}
self.reset(int);
}
Ok(())
}
}