use std::collections::HashMap; use std::path::{PathBuf, Path}; use color_eyre::Result; use eyre::eyre; use crate::constants::{Token, Loc, OpType, TokenType, KeywordType, InstructionType}; use crate::lexer::lex; use crate::{lerror, lnote, Args, warn}; use crate::parser::lookup_word; #[derive(Debug)] pub struct Macro { pub loc: Loc, pub tokens: Vec } pub fn preprocess(tokens: Vec, args: &Args) -> Result>{ let mut program: Vec = Vec::new(); let mut macros: HashMap = HashMap::new(); let mut rtokens = tokens; rtokens.reverse(); while !rtokens.is_empty() { let token = rtokens.pop().unwrap(); let op_type = lookup_word(&token.text, &token.loc()); match token.clone() { _ if op_type == OpType::Keyword(KeywordType::Macro) => { if rtokens.is_empty(){ lerror!(&token.loc(), "Macro name not found, expected {} but found nothing", TokenType::Word.human()); return Err(eyre!("")); } let macro_name = rtokens.pop().unwrap(); if macro_name.typ != TokenType::Word { lerror!(¯o_name.loc(), "Bad macro name, expected {} but found {}", TokenType::Word.human(), macro_name.typ.human()); return Err(eyre!("")); } let word = lookup_word(¯o_name.text, ¯o_name.loc()); if word != OpType::Instruction(InstructionType::None) { lerror!(¯o_name.loc(), "Macro name cannot be a built in word, got '{}'", word.human()); return Err(eyre!("")); } if macros.get(¯o_name.text.clone()).is_some() && crate::constants::ALLOW_MACRO_REDEFINITION { lerror!(¯o_name.loc(), "Macro redefinition is not allowed"); lnote!(¯os.get(¯o_name.text).unwrap().loc, "First definition here"); return Err(eyre!("")); } let mut macr = Macro{ loc: macro_name.loc(), tokens: Vec::new() }; let mut depth = 0; while !rtokens.is_empty() { let t = rtokens.pop().unwrap(); let typ = lookup_word(&t.text, &t.loc()); if typ == OpType::Keyword(KeywordType::End) && depth == 0 { break; } else if typ == OpType::Keyword(KeywordType::End) && depth != 0 { depth -= 1; macr.tokens.push(t); } else if typ == OpType::Keyword(KeywordType::If) || typ == OpType::Keyword(KeywordType::Do) { macr.tokens.push(t); depth += 1; } else { macr.tokens.push(t); } } macros.insert(macro_name.text, macr); } _ if op_type == OpType::Keyword(KeywordType::Include) => { if rtokens.is_empty() { lerror!(&token.loc(), "Include path not found, expected {} but found nothing", TokenType::String.human()); return Err(eyre!("")); } let include_path = rtokens.pop().unwrap(); if include_path.typ != TokenType::String { lerror!(&include_path.loc(), "Bad include path, expected {} but found {}", TokenType::String.human(), include_path.typ.human()); return Err(eyre!("")); } let mut in_paths = args.include.clone(); in_paths.append(&mut crate::DEFAULT_INCLUDES.to_vec().clone().iter().map(|f| (*f).to_string()).collect::>()); let mut include_code = String::new(); if include_path.text.chars().collect::>()[0] == '.' { let p = Path::new(include_path.file.as_str()); let p = p.parent().unwrap(); let p = p.join(&include_path.text); include_code = std::fs::read_to_string(p)?; } else { for path in in_paths { let p = PathBuf::from(path); let p = p.join(&include_path.text); if p.exists() { include_code = std::fs::read_to_string(p)?; } } } if include_code.is_empty() { lerror!(&include_path.loc(), "Include file in path '{}' was not found or is empty", include_path.text); return Err(eyre!("")); } let mut code = lex(&include_code, &include_path.text, args, false)?; code.reverse(); rtokens.append(&mut code); } _ => { program.push(token); } } } //* Feel free to fix this horrifying shit //* i wanna kms let mut times = 0; while program.iter().map(|f| { if f.typ == TokenType::Word { lookup_word(&f.text, &f.loc()) } else { OpType::Instruction(InstructionType::PushInt) // i hate myself, this is a randomly picked optype so its happy and works } }).collect::>().contains(&OpType::Instruction(InstructionType::None)){ if times >= 50 { warn!("File import depth maxed out, if the program crashes try reducing the import depth, good luck youll need it"); break } program = expand_macros(program, ¯os)?; times += 1; } Ok(program) } pub fn expand_macros(tokens: Vec, macros: &HashMap) -> Result> { let mut program: Vec = Vec::new(); let mut rtokens = tokens; rtokens.reverse(); while !rtokens.is_empty() { let op = rtokens.pop().unwrap(); let op_type = lookup_word(&op.text, &op.loc()); if op.typ == TokenType::Word { match op_type { OpType::Instruction(InstructionType::None) => { let m = macros.get(&op.text); if m.is_some() { if let Some(m) = m { program.append(&mut m.tokens.clone()); } } else { lerror!(&op.loc(), "Unknown word '{}'", op.text.clone()); return Err(eyre!("")); } } _ => { program.push(op); } } } else { program.push(op); } } Ok(program) }