alpha-shell 0.3.0

A transpiler for the AlphaShell language
use std::{fs::File, io::Read, path::Path};

use super::{
  error::{Error, ParserResult},
  node::Node,
  parse_helper::ParseHelper,
};
use crate::{
  check_token,
  types::{Token, TT},
};

fn read_file(path: &Path, token: &Token) -> Result<String, Error> {
  let mut file = match File::open(path) {
    Ok(path) => path,
    Err(e) => {
      return Err(Error::new(
        &format!("Couldn't open file '{path:?}', error: '{e}'"),
        Some(token),
      ))
    }
  };

  let mut contents = String::new();

  match file.read_to_string(&mut contents) {
    Ok(_) => Ok(contents),
    Err(e) => Err(Error::new(
      &format!("Error reading file '{path:?}', error: '{e}'"),
      Some(token),
    )),
  }
}

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

  let token = ph.get(0).cloned().unwrap();

  ph.advance();

  let mut files = Vec::new();

  match ph.peek(0) {
    Some(TT::String(string)) => files.push(string.clone()),
    Some(_) => return Err(Error::unexpected(ph)),
    None => return Err(Error::end(ph)),
  };

  ph.advance();

  loop {
    match ph.peek(0) {
      Some(TT::Comma) => {}
      Some(TT::Semicolon) => break,
      Some(_) => return Err(Error::unexpected(ph)),
      None => return Err(Error::end(ph)),
    };

    ph.advance();

    match ph.peek(0) {
      Some(TT::String(string)) => files.push(string.clone()),
      Some(_) => return Err(Error::unexpected(ph)),
      None => return Err(Error::end(ph)),
    };

    ph.advance();
  }

  ph.advance();

  if token.r#type == TT::Import {
    macro_rules! unwrap_or_error {
      ($input:expr, $file:ident) => {
        match $input {
          Ok(a) => a,
          Err(e) => {
            return Err(Error::new(
              &format!("Error while importing {}:\n{e}", $file),
              Some(&token),
            ))
          }
        }
      };
    }

    files
      .into_iter()
      .map(|file| {
        let contents = read_file(Path::new(&file), &token)?;

        let tokens = unwrap_or_error!(crate::tokenize(&contents), file);
        let tree = unwrap_or_error!(crate::parse(&tokens), file);

        Ok(tree)
      })
      .collect::<ParserResult<Vec<Vec<Node>>>>()
      .map(|trees| trees.into_iter().flatten().collect())
  } else {
    Ok(files.into_iter().map(Node::Source).collect())
  }
}