use capitalize::Capitalize;
use std::fs::File;
use std::io::prelude::*;
use crate::rules::Rules;
use crate::compiler::{Token, Lexer, LexerError, LexerErrorType, Metadata, SyntaxModule};
use crate::compiler::logger::{Logger, ErrorDetails};
#[derive(Debug, Clone, PartialEq)]
pub enum SeparatorMode {
Manual,
SemiAutomatic(String),
Automatic(String)
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScopingMode {
Block,
Indent
}
#[derive(Debug, Clone, PartialEq)]
pub struct Compiler {
pub name: String,
pub rules: Rules,
pub code: Option<String>,
pub path: Option<String>,
pub separator_mode: SeparatorMode,
pub scoping_mode: ScopingMode,
debug: bool
}
impl Compiler {
pub fn new<T: AsRef<str>>(name: T, rules: Rules) -> Self {
Compiler {
name: String::from(name.as_ref()),
rules,
code: None,
path: None,
separator_mode: SeparatorMode::Manual,
scoping_mode: ScopingMode::Block,
debug: false
}
}
pub fn use_indents(&mut self) {
self.scoping_mode = ScopingMode::Indent
}
pub fn load_file(mut self, file_path: String) -> std::io::Result<()> {
let mut file = File::open(&file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
self.code = Some(contents);
self.path = Some(file_path);
Ok(())
}
pub fn load<T: AsRef<str>>(&mut self, code: T) {
self.code = Some(String::from(code.as_ref()));
}
pub fn set_path(&mut self, file_path: String) {
self.path = Some(file_path);
}
pub fn tokenize(&self) -> Result<Vec<Token>, LexerError> {
let mut lexer = Lexer::new(&self);
if let Err(data) = lexer.run() {
return Err(data);
}
Ok(lexer.lexem)
}
pub fn debug(&mut self) {
self.debug = true
}
pub fn compile<M: Metadata>(&self, module: &mut impl SyntaxModule<M>) -> Result<M, ErrorDetails> {
match self.tokenize() {
Ok(lexem) => {
let mut meta = M::new(lexem, self.path.clone(), self.code.clone());
if self.debug {
module.parse_debug(&mut meta)?;
} else {
module.parse(&mut meta)?;
}
Ok(meta)
}
Err((kind, details)) => {
let data = details.data.clone().unwrap().capitalize();
let message = match kind {
LexerErrorType::Singleline => format!("{data} cannot be multiline"),
LexerErrorType::Unclosed => format!("{data} unclosed"),
};
let pos = details.get_pos_by_code(&self.code.as_ref().unwrap());
Logger::new_err_at_position(self.path.clone(), self.code.clone(), pos)
.attach_message(message)
.attach_code(self.code.as_ref().unwrap().clone())
.show()
.exit();
Err(details)
}
}
}
}