brono 0.0.0

A programming language that compiles down to Brainfuck.
Documentation
mod parser;
mod messages;

use pest::Parser;
use pest::iterators::Pair;
use hashbrown::HashMap;

use parser::*;
use messages::*;

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MacroArgType {
    Nut, // Just an internal joke! :)
    Ident,
    Text,
    Number,
    Block,
}

impl MacroArgType {
    pub fn from_str(arg_type: &str) -> Option<Self> {
        return Some(match arg_type.trim() {
            "ident" => MacroArgType::Ident,
            "text" => MacroArgType::Text,
            "number" => MacroArgType::Number,
            "block" => MacroArgType::Block,
            _ => return None,
        });
    }
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Compiler<'a> {
    pub consts: HashMap<String, u8>,
    pub imports: Vec<String>,
    pub functions: HashMap<String, (Vec<String>, Vec<Pair<'a, Rule>>)>,
    pub macros: HashMap<String, (Vec<(String, MacroArgType)>, Vec<Pair<'a, Rule>>)>,
}

impl<'a> Compiler<'a> {
    pub fn new() -> Self {
        return Self {
            consts: HashMap::new(),
            imports: Vec::new(),
            functions: HashMap::new(),
            macros: HashMap::new(),
        };
    }

    pub fn compile(&mut self, code: &'a str) -> Option<String> {
        let mut bf: Vec<char> = Vec::new(); // Brainfuck code. :)

        let ast = match BronoParser::parse(Rule::file, code) {
            Ok(o) => o,
            Err(_) => return None,
        };

        // This literally makes no sense. (It just gets the file contents.)
        for (file_i, file) in ast.enumerate() {
            // Now we're talkin'!
            for record in file.into_inner() {
                match record.as_rule() {
                    Rule::k_const => {
                        let pairs: Vec<_> = record.into_inner().into_iter().collect();

                        let name = pairs.get(0).unwrap().as_str();
                        let value: u8 = pairs.get(1).unwrap().as_str().trim().parse().unwrap();

                        self.consts.insert(name.to_string(), value);
                    },
                    Rule::k_import => {
                        let pairs: Vec<_> = record.into_inner().into_iter().collect();

                        let library = pairs.get(0).unwrap().as_str();

                        self.imports.push(library.to_string());
                    },
                    Rule::k_fn => {
                        let pairs: Vec<_> = record.into_inner().into_iter().collect();

                        let name = pairs.get(0).unwrap().as_str();

                        let arguments_pre = pairs.get(1).unwrap().clone().into_inner();
                        let mut arguments: Vec<String> = Vec::new();
                        for i in arguments_pre {
                            arguments.push(i.as_str().to_string());
                        }

                        let block = pairs.get(2).unwrap().clone();

                        self.functions.insert(name.to_string(), (arguments, block.into_inner().into_iter().collect()));
                    },
                    Rule::k_macro => {
                        let pairs: Vec<_> = record.into_inner().into_iter().collect();

                        let name = pairs.get(0).unwrap().as_str();

                        let arguments_pre = pairs.get(1).unwrap().clone().into_inner();
                        let mut arguments: Vec<(String, MacroArgType)> = Vec::new();
                        let mut carg = "";
                        for (i, a) in arguments_pre.enumerate() {
                            if i & 1 == 0 {
                                carg = a.as_str();
                            }

                            else {
                                arguments.push((carg.to_string(), MacroArgType::from_str(a.as_str()).unwrap()));
                            }
                        }

                        let block = pairs.get(2).unwrap().clone();

                        self.macros.insert(name.to_string(), (arguments, block.into_inner().into_iter().collect()));
                    },
                    Rule::EOI => (),
                    _ => unreachable!(),
                };

                println!("");
            }

            if file_i > 0 {
                panic!("There should only be one file, and one file only! Fix your shit!");
            }
        }

        let file_name = "main.brono";

        let main_func = match self.functions.get("main") {
            Some(s) => s,
            None => {
                let message = Message::new(
                    "Missing main() function!",
                    MessageType::Error,
                    MessageLocation::File(file_name, MessageSpan::empty(), None),
                );

                message.print();

                return None;
            },
        };

        if main_func.0.len() != 0 {
            let message = Message::new(
                "main() function doesn't take any parameters! It is the program entry!",
                MessageType::Error,
                MessageLocation::File(file_name, MessageSpan::empty(), None),
            );

            message.print();

            return None;
        }

        return Some(bf.into_iter().map(|x| x.to_string()).collect::<Vec<_>>().concat());
    }
}