maid-lang 1.0.5

Maid Programming Language
Documentation
mod errors;
mod interpreting;
mod lexing;
mod nodes;
mod package_manager;
mod parsing;
mod syntax;
mod values;
use crate::{
    errors::standard_error::StandardError,
    interpreting::{context::Context, interpreter::Interpreter},
    lexing::lexer::Lexer,
    parsing::parser::Parser,
};
pub use package_manager::{
    logs::{log_error, log_header, log_message, log_package_status},
    packages::{
        add_package, create_package_dir, is_package_installed, remove_package, update_package,
    },
    paths::get_package_path,
};
use simply_colored::*;
use std::{
    cell::RefCell,
    fs,
    io::{Write, stdin, stdout},
    path::Path,
    rc::Rc,
    time::Instant,
};

pub fn run(filename: &str, code: Option<String>) -> Option<StandardError> {
    let contents = if filename == "<stdin>" {
        code.unwrap_or_default()
    } else {
        match fs::read_to_string(filename) {
            Ok(s) => s,
            Err(e) => {
                println!("{DIM_RED}Failed to read provided '.maid' file: {e}{RESET}");

                return None;
            }
        }
    };

    let start = Instant::now();

    let mut lexer = Lexer::new(filename, contents.clone());
    let token_result = lexer.make_tokens();

    if token_result.is_err() {
        return token_result.err();
    }

    let mut parser = Parser::new(&token_result.ok().unwrap());
    let ast = parser.parse();

    if ast.error.is_some() {
        return ast.error;
    }

    let mut interpreter = Interpreter::new();
    let context = Rc::new(RefCell::new(Context::new(
        "<program>".to_string(),
        None,
        None,
    )));
    context.borrow_mut().symbol_table = Some(interpreter.global_symbol_table.clone());

    if let Some(e) = interpreter.evaluate(
        "fetch _env(\"MAID_STD\") + \"/default/lib.maid\";",
        context.clone(),
    ) {
        return Some(e);
    }

    let result = interpreter.visit(ast.node.unwrap(), context.clone());

    if cfg!(feature = "benchmark") {
        println!("Time elapsed: {:?}ms", start.elapsed().as_millis());
    }

    result.error
}

pub fn launch_repl(version: &str) {
    println!("Maid Code {version}\nType '/exit' to exit");

    loop {
        let mut code = String::new();

        print!(">>> ");
        let _ = stdout().flush();

        stdin()
            .read_line(&mut code)
            .expect("Input text (stdin) was not a valid string");

        if code.trim() == "/exit" {
            break;
        }

        let error = run("<stdin>", Some(code));

        if let Some(e) = error {
            println!("{e}");

            continue;
        }
    }
}

pub fn new_project(dir_name: &Path, init: bool) {
    if !init {
        fs::create_dir(dir_name).expect("Cannot create directory (invalid name)");
    }

    fs::create_dir(dir_name.join("src")).expect("'src/' directory already exists");

    let _ = fs::write(
        dir_name.join("main.maid"),
        "func main() {\n    greet(\"Hello world!\");\n}\n\nmain();",
    );
    let _ = fs::write(
        dir_name.join("README.md"),
        "# Welcome to MaidCode!\nTo get started, see our documentation here.",
    );
}