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(())
}