use std::fmt::{self, Write};
use crate::{
Comment, DoWhileLoop, Expr, ForLoop, Formatter, IfElse, Switch, Type, Variable, WhileLoop,
};
#[derive(Debug, Clone)]
enum Item {
Comment(Comment),
Variable(Variable),
IfElse(IfElse),
ForLoop(ForLoop),
WhileLoop(WhileLoop),
DoWhileLoop(DoWhileLoop),
Return(Option<Expr>),
Assign(Expr, Expr),
GoTo(String),
Label(String),
Raw(String),
Expr(Expr),
FnCall(String, Vec<Expr>),
MethodCall(Expr, String, Vec<Expr>),
Break,
Continue,
NewLine,
Switch(Switch),
}
#[derive(Debug, Clone)]
pub struct Block {
items: Vec<Item>,
}
impl Block {
pub fn new() -> Self {
Block { items: Vec::new() }
}
pub fn merge(&mut self, other: Block) {
let mut other = other;
self.items.append(other.items.as_mut());
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn clear(&mut self) {
self.items.clear();
}
pub fn empty_line(&mut self) -> &mut Self {
self.items.push(Item::NewLine);
self
}
pub fn break_stmt(&mut self) -> &mut Self {
self.items.push(Item::Break);
self
}
pub fn raw_str(&mut self, raw: &str) -> &mut Self {
self.items.push(Item::Raw(String::from(raw)));
self
}
pub fn raw(&mut self, raw: String) -> &mut Self {
self.items.push(Item::Raw(raw));
self
}
pub fn assign(&mut self, lhs: Expr, rhs: Expr) -> &mut Self {
self.items.push(Item::Assign(lhs, rhs));
self
}
pub fn label(&mut self, label: &str) -> &mut Self {
self.items.push(Item::Label(String::from(label)));
self
}
pub fn goto(&mut self, label: &str) -> &mut Self {
self.items.push(Item::GoTo(String::from(label)));
self
}
pub fn continue_stmt(&mut self) -> &mut Self {
self.items.push(Item::Continue);
self
}
pub fn new_comment(&mut self, comment: &str) -> &mut Self {
self.comment(Comment::with_str(comment));
self
}
pub fn comment(&mut self, comment: Comment) -> &mut Self {
self.items.push(Item::Comment(comment));
self
}
pub fn new_ifelse(&mut self, cond: &Expr) -> &mut IfElse {
let ifelse = IfElse::new(cond);
self.items.push(Item::IfElse(ifelse));
match *self.items.last_mut().unwrap() {
Item::IfElse(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn ifelse(&mut self, s: IfElse) -> &mut Self {
self.items.push(Item::IfElse(s));
self
}
pub fn new_switch(&mut self, cond: &Expr) -> &mut Switch {
let ifelse = Switch::new(cond);
self.items.push(Item::Switch(ifelse));
match *self.items.last_mut().unwrap() {
Item::Switch(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn switch(&mut self, s: Switch) -> &mut Self {
self.items.push(Item::Switch(s));
self
}
pub fn new_for_loop(&mut self, init: &Expr, guard: &Expr, step: &Expr) -> &mut ForLoop {
let forloop = ForLoop::from_expr(init, guard, step);
self.items.push(Item::ForLoop(forloop));
match *self.items.last_mut().unwrap() {
Item::ForLoop(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn for_loop(&mut self, s: ForLoop) -> &mut Self {
self.items.push(Item::ForLoop(s));
self
}
pub fn new_while_loop(&mut self, cond: &Expr) -> &mut WhileLoop {
self.while_loop(WhileLoop::new(cond));
match *self.items.last_mut().unwrap() {
Item::WhileLoop(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn while_loop(&mut self, s: WhileLoop) -> &mut Self {
self.items.push(Item::WhileLoop(s));
self
}
pub fn new_dowhile_loop(&mut self, cond: &Expr) -> &mut DoWhileLoop {
self.dowhile_loop(DoWhileLoop::new(cond));
match *self.items.last_mut().unwrap() {
Item::DoWhileLoop(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn dowhile_loop(&mut self, s: DoWhileLoop) -> &mut Self {
self.items.push(Item::DoWhileLoop(s));
self
}
pub fn new_variable(&mut self, name: &str, ty: Type) -> &mut Variable {
self.variable(Variable::new(name, ty));
match *self.items.last_mut().unwrap() {
Item::Variable(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn variable(&mut self, var: Variable) -> &mut Self {
self.items.push(Item::Variable(var));
self
}
pub fn raw_expr(&mut self, expr: Expr) -> &mut Self {
self.items.push(Item::Expr(expr));
self
}
pub fn new_return(&mut self, expr: Option<&Expr>) -> &mut Self {
if let Some(e) = expr {
self.items.push(Item::Return(Some(e.clone())));
} else {
self.items.push(Item::Return(None));
}
self
}
pub fn return_expr(&mut self, expr: Expr) -> &mut Self {
self.items.push(Item::Return(Some(expr)));
self
}
pub fn return_none(&mut self) -> &mut Self {
self.items.push(Item::Return(None));
self
}
pub fn printf(&mut self, format: &str, vars: Vec<Expr>) -> &mut Self {
let mut vars = vars;
let mut args = vec![Expr::new_str(format)];
args.append(&mut vars);
self.items.push(Item::FnCall(String::from("printf"), args));
self
}
pub fn printstr(&mut self, format: &str) -> &mut Self {
self.items
.push(Item::FnCall(String::from("printf"), vec![Expr::new_str(format)]));
self
}
pub fn fn_call(&mut self, name: &str, args: Vec<Expr>) -> &mut Self {
self.items.push(Item::FnCall(String::from(name), args));
self
}
pub fn method_call(&mut self, obj: Expr, method: &str, args: Vec<Expr>) -> &mut Self {
self.items.push(Item::MethodCall(obj, String::from(method), args));
self
}
pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for item in self.items.iter() {
match &item {
Item::Comment(v) => v.fmt(fmt)?,
Item::NewLine => writeln!(fmt)?,
Item::Break => writeln!(fmt, "break;")?,
Item::Continue => writeln!(fmt, "continue;")?,
Item::Raw(v) => writeln!(fmt, "{v};")?,
Item::Expr(v) => {
v.fmt(fmt)?;
writeln!(fmt, ";")?;
}
Item::Label(v) => writeln!(fmt, "{v}:")?,
Item::GoTo(v) => writeln!(fmt, "goto {v};")?,
Item::Assign(l, r) => {
l.fmt(fmt)?;
write!(fmt, " = ")?;
r.fmt(fmt)?;
writeln!(fmt, ";")?;
}
Item::IfElse(v) => v.fmt(fmt)?,
Item::Switch(v) => v.fmt(fmt)?,
Item::ForLoop(v) => v.fmt(fmt)?,
Item::WhileLoop(v) => v.fmt(fmt)?,
Item::DoWhileLoop(v) => v.fmt(fmt)?,
Item::Variable(v) => v.fmt_def(fmt)?,
Item::Return(None) => writeln!(fmt, "return;")?,
Item::Return(Some(v)) => {
write!(fmt, "return ")?;
v.fmt(fmt)?;
writeln!(fmt, ";")?
}
Item::FnCall(name, args) => {
write!(fmt, "{name}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(fmt, ", ")?;
}
arg.fmt(fmt)?;
}
writeln!(fmt, ");")?
}
Item::MethodCall(obj, method, args) => {
obj.fmt(fmt)?;
if obj.is_ptr() {
write!(fmt, "->{method}(")?;
} else {
write!(fmt, ".{method}(")?;
}
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(fmt, ", ")?;
}
arg.fmt(fmt)?;
}
writeln!(fmt, ");")?
}
}
}
Ok(())
}
}
impl Default for Block {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ret = String::new();
self.fmt(&mut Formatter::new(&mut ret)).unwrap();
write!(f, "{ret}")
}
}