gabelang/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#![doc(html_favicon_url = "https://buxogabriel.vercel.app/favicon.ico")]
#![doc(html_logo_url = "https://buxogabriel.vercel.app/apple-touch-icon.png")]

use std::{collections::HashMap, fs, io};

mod ast;
/// The repl module contains a start function run an isolated instance of the gabelang repl
pub mod repl;
/// The lexer module contains the Lexer struct which creates an iterator of tokens that may be used
/// by the gabelang parser when given a string 
pub mod lexer;
/// The parser module contains the Parser struct which contains a lexer and creates an AST(Abstract Syntax Tree) when given a string
pub mod parser;
/// The evaluator module contains the GabrEnv struct which provides an environment in order to run gabelang code
pub mod evaluator;

use evaluator::GabrEnv;
use parser::Parser;

/// Config for running the gabelang interpretter
#[derive(Default)]
pub struct Config {
    flags: HashMap<String, String>
}

impl Config {
    /// Constructs a Config from a list of args
    ///
    /// Expects args to come in flag, value pairs
    ///
    /// Currently the only supported flag is file, which expects a filename to interpret
    ///
    /// No flags/empty arguments implys default behavior which is running the repl
    ///
    /// # Examples
    ///
    /// Getting args from the command line to generate config
    /// ```
    /// use std::env;
    /// use gabelang::Config;
    /// let args: Vec<String> = env::args().collect();
    /// let config = Config::build(&args);
    /// ```
    ///
    /// Manually setting a file argument
    /// ```
    /// use gabelang::Config;
    /// let config = Config::build(&[String::from("--file"), String::from("index.gabe")]);
    /// ```
    pub fn build(args: &[String]) -> Result<Config, &'static str> {
        let flags = Self::parse_flags(args);
        Ok(Config { flags })
    }

    fn parse_flags(args: &[String]) -> HashMap<String, String> {
        let mut map = HashMap::<String, String>::new();
        args.windows(2).for_each(|pair| {
            match pair[0].clone().split_once("--") {
                Some((_, flag)) => {
                    map.insert(flag.to_string(), pair[1].clone());
                },
                None => {},
            }
        });
        map
    }

    fn get_flag(&self, flag: &str) -> Option<String> {
        self.flags.get(flag).map(|f| f.clone())
    }
}

/// Runs the gabelang interpretter by taking in a config and either running the repl or interpretting a .gabe file based on args
/// 
/// Config can be generated using gabelang::Config::build
/// 
/// # Errors
///
/// run can only generate errors if it is interpretting a file and either the file fails
///
/// # Example
///
/// ```
/// use gabelang::Config;
/// gabelang::run(Config::default()).expect("Application ran into an unexpected error");
/// ```
pub fn run(config: Config) -> io::Result<()> {
    if let Some(file_name) = config.get_flag("file") {
        let contents = fs::read_to_string(&file_name)?;
        println!("parsing {}", &file_name);
        let program = Parser::new(&contents).parse_program();
        match program {
            Ok(program) => {
                println!("parsing complete! Running {}", &file_name);
                let mut env = GabrEnv::new();
                match env.eval_program(&program) {
                    Ok(res) => println!("{res}"),
                    Err(err) => println!("Execution Failed: {err}")
                }
            },
            Err(e) => println!("{e}"),
        };
        return Ok(())
    }
    repl::start();
    Ok(())
}