alpha-shell 0.3.0

A transpiler for the AlphaShell language
use super::{
  block::{self, Block},
  error::{Error, ParserResult},
  node::Node,
  parse_helper::ParseHelper,
  value::{self, Value},
};
use crate::{check_token, types::TT};

#[derive(Debug, PartialEq, Clone)]
pub struct For {
  pub start: i32,
  pub end: i32,
  pub step: i32,
  pub variable: String,
  pub block: Box<Node>,
}

impl For {
  pub fn new(start: i32, end: i32, step: i32, variable: String, block: Box<Node>) -> Self {
    Self {
      start,
      end,
      step,
      variable,
      block,
    }
  }
}

#[derive(Debug, PartialEq, Clone)]
pub struct Foreach {
  pub variable: String,
  pub iterable: Value,
  pub block: Block,
}

impl Foreach {
  pub fn new(iterable: Value, variable: String, block: Block) -> Self {
    Self {
      variable,
      iterable,
      block,
    }
  }
}

fn parse_for(ph: &mut ParseHelper, variable: String) -> ParserResult<Node> {
  let start = match ph.peek(0) {
    Some(TT::Integer(num)) => *num,
    Some(_) => return Err(Error::unexpected(ph)),
    None => return Err(Error::end(ph)),
  };

  ph.advance();
  check_token!(ph, TT::Range);
  ph.advance();

  let end = match ph.peek(0) {
    Some(TT::Integer(num)) => *num,
    Some(_) => return Err(Error::unexpected(ph)),
    None => return Err(Error::end(ph)),
  };

  ph.advance();

  let step = if let Some(TT::Range) = ph.peek(0) {
    ph.advance();

    let step = match ph.peek(0) {
      Some(TT::Integer(num)) => *num,
      Some(_) => return Err(Error::unexpected(ph)),
      None => return Err(Error::end(ph)),
    };

    ph.advance();

    step
  } else {
    1
  };

  let mut variables = ph.variables.clone();
  variables.insert(variable.clone());

  let block = Box::new(block::parse(ph, variables)?);

  let node = Node::For(For::new(start, end, step, variable, block));

  Ok(node)
}

fn parse_foreach(ph: &mut ParseHelper, variable: String) -> ParserResult<Node> {
  let iterable = value::parse_inner(ph)?;

  let mut variables = ph.variables.clone();
  variables.insert(variable.clone());

  let block = block::parse_inner(ph, variables)?;

  let node = Node::Foreach(Foreach::new(iterable, variable, block));

  Ok(node)
}

pub fn parse(ph: &mut ParseHelper) -> ParserResult<Node> {
  check_token!(ph, TT::For);

  ph.advance();

  let variable = match ph.peek(0) {
    Some(TT::Identifier(variable)) => variable.clone(),
    Some(_) => return Err(Error::unexpected(ph)),
    None => return Err(Error::end(ph)),
  };

  ph.advance();

  check_token!(ph, TT::In);

  ph.advance();

  let node = match ph.peek(0) {
    Some(TT::Integer(_)) => parse_for(ph, variable)?,
    Some(TT::At | TT::LBracket | TT::Identifier(..)) => parse_foreach(ph, variable)?,
    Some(_) => return Err(Error::unexpected(ph)),
    None => return Err(Error::end(ph)),
  };

  Ok(node)
}