use std::io::{self, Write};
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::path::{ Path , PathBuf};
use crate::error::{RadError, ErrorLogger, LoggerLines};
use crate::models::{MacroMap, MacroRule, RuleFile, UnbalancedChecker, WriteOption};
use crate::utils::Utils;
use crate::consts::*;
use crate::lexor::*;
use crate::arg_parser::ArgParser;
#[derive(Debug)]
struct MacroFragment {
pub whole_string: String,
pub name: String,
pub args: String,
pub pipe: bool,
pub greedy: bool,
pub preceding: bool,
pub yield_literal : bool,
pub trimmed : bool,
}
impl MacroFragment {
fn new() -> Self {
MacroFragment {
whole_string : String::new(),
name : String::new(),
args : String::new(),
pipe: false,
greedy: false,
preceding: false,
yield_literal : false,
trimmed: false,
}
}
fn clear(&mut self) {
self.whole_string.clear();
self.name.clear();
self.args.clear();
self.pipe = false;
self.greedy = false;
self.yield_literal = false;
self.trimmed= false;
}
fn is_empty(&self) -> bool {
self.whole_string.len() == 0
}
}
enum ParseResult {
FoundMacro(String),
Printable(String),
NoPrint,
EOI,
}
struct SandboxBackup {
current_input: String,
local_macro_map: HashMap<String,String>,
logger_lines: LoggerLines,
}
pub struct Processor{
current_input : String, map: MacroMap,
define_parse: DefineParser,
write_option: WriteOption,
error_logger: ErrorLogger,
checker: UnbalancedChecker,
pub(crate) pipe_value: String,
pub(crate) newline: String,
pub(crate) paused: bool,
pub(crate) redirect: bool,
sandbox: bool,
purge: bool,
strict: bool,
always_greedy: bool,
temp_target: (PathBuf,File),
}
impl Processor {
pub fn new() -> Self {
let temp_path= std::env::temp_dir().join("rad.txt");
let temp_target = (temp_path.to_owned(),OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&temp_path)
.unwrap());
Self {
current_input: String::from("stdin"),
map : MacroMap::new(),
write_option: WriteOption::Stdout,
define_parse: DefineParser::new(),
error_logger: ErrorLogger::new(Some(WriteOption::Stdout)),
checker : UnbalancedChecker::new(),
newline : LINE_ENDING.to_owned(),
pipe_value: String::new(),
paused: false,
redirect: false,
purge: false,
strict: false,
sandbox : false,
always_greedy: false,
temp_target,
}
}
pub fn write_to_file(&mut self, target_file: Option<PathBuf>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let target_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(target_file)?;
self.write_option = WriteOption::File(target_file);
}
Ok(self)
}
pub fn error_to_file(&mut self, target_file: Option<PathBuf>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let target_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(target_file)?;
self.error_logger = ErrorLogger::new(Some(WriteOption::File(target_file)));
}
Ok(self)
}
pub fn unix_new_line(&mut self, use_unix_new_line: bool) -> &mut Self {
if use_unix_new_line {
self.newline = "\n".to_owned();
}
self
}
pub fn greedy(&mut self, greedy: bool) -> &mut Self {
if greedy {
self.always_greedy = true;
}
self
}
pub fn purge(&mut self, purge: bool) -> &mut Self {
if purge {
self.purge = true;
self.strict = false;
}
self
}
pub fn strict(&mut self, strict: bool) -> &mut Self {
if strict {
self.strict = true;
self.purge = false;
}
self
}
pub fn silent(&mut self, silent: bool) -> &mut Self {
if silent {
self.error_logger = ErrorLogger::new(None);
}
self
}
pub fn custom_rules(&mut self, paths: Option<Vec<PathBuf>>) -> Result<&mut Self, RadError> {
if let Some(paths) = paths {
let mut rule_file = RuleFile::new(None);
for p in paths.iter() {
rule_file.melt(p)?;
}
self.map.custom.extend(rule_file.rules);
}
Ok(self)
}
pub fn build(&mut self) -> Self {
std::mem::replace(self, Processor::new())
}
#[allow(dead_code)]
pub fn print_result(&mut self) -> Result<(), RadError> {
self.error_logger.print_result()?;
Ok(())
}
pub(crate) fn get_map(&mut self) -> &mut MacroMap {
&mut self.map
}
pub(crate) fn set_temp_file(&mut self, path: &Path) {
self.temp_target = (path.to_owned(),OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.unwrap());
}
pub(crate) fn set_sandbox(&mut self) {
self.sandbox = true;
}
pub(crate) fn get_temp_path(&self) -> &Path {
&self.temp_target.0
}
pub(crate) fn get_temp_file(&self) -> &File {
&self.temp_target.1
}
fn backup(&self) -> SandboxBackup {
SandboxBackup {
current_input: self.current_input.clone(),
local_macro_map: self.map.local.clone(),
logger_lines: self.error_logger.backup_lines(),
}
}
fn recover(&mut self, backup: SandboxBackup) {
self.error_logger.set_file(&backup.current_input);
self.current_input = backup.current_input;
self.map.local= backup.local_macro_map;
self.error_logger.recover_lines(backup.logger_lines);
}
pub fn freeze_to_file(&self, path: &Path) -> Result<(), RadError> {
RuleFile::new(Some(self.map.custom.clone())).freeze(path)?;
Ok(())
}
pub fn from_string(&mut self, content: &str) -> Result<String, RadError> {
let backup = if self.sandbox { Some(self.backup()) } else { None };
let mut line_iter = Utils::full_lines(content.as_bytes());
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut content = String::new();
let mut container = if self.sandbox { Some(&mut content) } else { None };
loop {
let result = self.parse_line(&mut line_iter, &mut lexor ,&mut frag)?;
match result {
ParseResult::Printable(remainder) => {
self.write_to(&remainder, &mut container)?;
if &frag.whole_string != "" {
frag = MacroFragment::new();
}
}
ParseResult::FoundMacro(remainder) => {
self.write_to(&remainder, &mut container)?;
}
ParseResult::NoPrint => (), ParseResult::EOI => break,
}
}
if let Some(backup) = backup { self.recover(backup); self.sandbox = false; }
Ok(content)
}
pub fn from_stdin(&mut self) -> Result<String, RadError> {
let backup = if self.sandbox { Some(self.backup()) } else { None };
let stdin = io::stdin();
let mut line_iter = Utils::full_lines(stdin.lock());
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut content = String::new();
let mut container = if self.sandbox { Some(&mut content) } else { None };
loop {
let result = self.parse_line(&mut line_iter, &mut lexor ,&mut frag)?;
match result {
ParseResult::Printable(remainder) => {
self.write_to(&remainder, &mut container)?;
if &frag.whole_string != "" {
frag = MacroFragment::new();
}
}
ParseResult::FoundMacro(remainder) => {
self.write_to(&remainder, &mut container)?;
}
ParseResult::NoPrint => (), ParseResult::EOI => break,
}
}
if let Some(backup) = backup { self.recover(backup); self.sandbox = false; }
Ok(content)
}
pub fn from_file(&mut self, path :&Path) -> Result<String, RadError> {
let backup = if self.sandbox { Some(self.backup()) } else { None };
self.set_file(path.to_str().unwrap())?;
let file_stream = File::open(path)?;
let reader = io::BufReader::new(file_stream);
let mut line_iter = Utils::full_lines(reader);
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut content = String::new();
let mut container = if self.sandbox { Some(&mut content) } else { None };
loop {
let result = self.parse_line(&mut line_iter, &mut lexor ,&mut frag)?;
match result {
ParseResult::Printable(remainder) => {
self.write_to(&remainder, &mut container)?;
if &frag.whole_string != "" {
frag = MacroFragment::new();
}
}
ParseResult::FoundMacro(remainder) => {
self.write_to(&remainder, &mut container)?;
}
ParseResult::NoPrint => (), ParseResult::EOI => break,
}
}
if let Some(backup) = backup { self.recover(backup); self.sandbox = false; }
Ok(content)
}
fn parse_line(&mut self, lines :&mut impl std::iter::Iterator<Item = std::io::Result<String>>, lexor : &mut Lexor ,frag : &mut MacroFragment) -> Result<ParseResult, RadError> {
self.error_logger.add_line_number();
if let Some(line) = lines.next() {
let line = line?;
let remainder = self.parse(lexor, frag, &line, 0, MAIN_CALLER)?;
self.map.clear_local();
if remainder.len() != 0 {
if !frag.is_empty() {
Ok(ParseResult::FoundMacro(remainder))
}
else {
Ok(ParseResult::Printable(remainder))
}
}
else {
Ok(ParseResult::NoPrint)
}
} else {
Ok(ParseResult::EOI)
}
}
pub(crate) fn parse_chunk(&mut self, level: usize, caller: &str, chunk: &str) -> Result<String, RadError> {
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut result = self.parse(&mut lexor, &mut frag, chunk, level, caller)?;
if !frag.is_empty() {
result.push_str(&frag.whole_string);
}
return Ok(result);
}
fn parse(&mut self,lexor: &mut Lexor, frag: &mut MacroFragment, line: &str, level: usize, caller: &str) -> Result<String, RadError> {
self.error_logger.reset_char_number();
let mut remainder = String::new();
lexor.escape_nl = false;
for ch in line.chars() {
self.error_logger.add_char_number();
let lex_result = lexor.lex(ch)?;
match lex_result {
LexResult::Ignore => frag.whole_string.push(ch),
LexResult::Literal(cursor) => {
self.lex_branch_literal(ch, frag, &mut remainder, cursor);
}
LexResult::StartFrag => {
self.lex_branch_start_frag(ch, frag, &mut remainder, lexor);
},
LexResult::EmptyName => {
self.lex_branch_empty_name(ch, frag, &mut remainder, lexor);
}
LexResult::AddToRemainder => {
self.lex_branch_add_to_remainder(ch, &mut remainder)?;
}
LexResult::AddToFrag(cursor) => {
self.lex_branch_add_to_frag(ch, frag, cursor);
}
LexResult::EndFrag => {
self.lex_branch_end_frag(ch,frag,&mut remainder, lexor, level, caller)?;
}
LexResult::ExitFrag => {
self.lex_branch_exit_frag(ch,frag,&mut remainder);
}
}
} Ok(remainder)
}
fn lex_branch_literal(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, cursor: Cursor) {
match cursor {
Cursor::Name => {
frag.whole_string.push(ch);
remainder.push_str(&frag.whole_string);
frag.clear();
}
Cursor::None => { remainder.push(ch); }
Cursor::Arg => {
frag.args.push(ch);
frag.whole_string.push(ch);
}
}
}
fn lex_branch_start_frag(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor) {
frag.whole_string.push(ch);
if self.paused && frag.name != "pause" {
lexor.reset();
remainder.push_str(&frag.whole_string);
frag.clear();
}
}
fn lex_branch_empty_name(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor) {
frag.whole_string.push(ch);
self.error_logger.freeze_number();
if self.paused {
lexor.reset();
remainder.push_str(&frag.whole_string);
frag.clear();
}
}
fn lex_branch_add_to_remainder(&mut self, ch: char,remainder: &mut String) -> Result<(), RadError> {
if !self.checker.check(ch) {
self.error_logger.freeze_number();
self.log_warning("Unbalanced parenthesis detected.")?;
}
remainder.push(ch);
Ok(())
}
fn lex_branch_add_to_frag(&mut self, ch: char,frag: &mut MacroFragment, cursor: Cursor) {
match cursor{
Cursor::Name => {
if frag.name.len() == 0 {
self.error_logger.freeze_number();
}
match ch {
'|' => frag.pipe = true,
'+' => frag.greedy = true,
'*' => frag.yield_literal = true,
'^' => frag.trimmed = true,
_ => frag.name.push(ch)
}
},
Cursor::Arg => {
frag.args.push(ch)
},
_ => unreachable!(),
}
frag.whole_string.push(ch);
}
fn lex_branch_end_frag(&mut self, ch: char, frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor, level: usize, caller: &str) -> Result<(), RadError> {
frag.whole_string.push(ch);
if frag.name == "define" {
self.add_rule(frag, remainder)?;
lexor.escape_nl = true;
frag.clear()
} else { let evaluation_result = self.evaluate(level, caller, &frag.name, &frag.args, frag.greedy || self.always_greedy);
if let Err(error) = evaluation_result {
if let RadError::Panic = error{
();
} else {
self.log_error(&format!("{}", error))?;
}
return Err(RadError::Panic);
}
if let Some(mut content) = evaluation_result.unwrap() {
if frag.pipe {
self.pipe_value = content;
lexor.escape_nl = true;
}
else if content.len() == 0 {
lexor.escape_nl = true;
} else {
if frag.trimmed {
content = Utils::trim(&content)?;
}
if frag.yield_literal {
content = format!("\\*{}*\\", content);
}
remainder.push_str(&content);
}
} else {
if self.strict {
self.log_error(&format!("Failed to invoke a macro : \"{}\"", frag.name))?;
return Err(RadError::StrictPanic);
}
if !self.purge {
self.log_error(&format!("Failed to invoke a macro : \"{}\"", frag.name))?;
remainder.push_str(&frag.whole_string);
} else {
lexor.escape_nl = true;
}
}
frag.clear()
}
Ok(())
}
fn lex_branch_exit_frag(&mut self,ch: char, frag: &mut MacroFragment, remainder: &mut String) {
frag.whole_string.push(ch);
remainder.push_str(&frag.whole_string);
frag.clear();
}
fn add_rule(&mut self, frag: &mut MacroFragment, remainder: &mut String) -> Result<(), RadError> {
if let Some((name,args,body)) = self.define_parse.parse_define(&frag.args) {
self.map.register(&name, &args, &body)?;
} else {
self.log_error(&format!(
"Failed to register a macro : \"{}\"",
frag.args.split(',').collect::<Vec<&str>>()[0]
))?;
remainder.push_str(&frag.whole_string);
}
frag.clear();
Ok(())
}
fn evaluate(&mut self,level: usize, caller: &str, name: &str, args: &str, greedy: bool) -> Result<Option<String>, RadError> {
let level = level + 1;
let args = self.parse_chunk(level, name, args)?;
let mut temp_level = level;
while temp_level > 0 {
if caller == name {
self.log_warning(&format!("Calling self, which is \"{}\", can possibly trigger infinite loop", name))?;
}
if let Some(local) = self.map.local.get(&Utils::local_name(temp_level, &name)) {
return Ok(Some(local.to_owned()));
}
temp_level = temp_level - 1;
}
if self.map.custom.contains_key(name) {
if let Some(result) = self.invoke_rule(level, name, &args, greedy)? {
return Ok(Some(result));
} else {
return Ok(None);
}
}
else if self.map.basic.contains(&name) {
let final_result = self.map.basic.clone().call(name, &args, greedy, self)?;
return Ok(Some(final_result));
}
else {
return Ok(None);
}
}
fn invoke_rule(&mut self,level: usize ,name: &str, arg_values: &str, greedy: bool) -> Result<Option<String>, RadError> {
let rule = self.map.custom.get(name).unwrap().clone();
let arg_types = &rule.args;
let args: Vec<String>;
if let Some(content) = ArgParser::new().args_with_len(arg_values, arg_types.len(), greedy) {
args = content;
} else {
self.log_error(&format!("{}'s arguments are not sufficient. Given {}, but needs {}", name, arg_values.len(), arg_types.len()))?;
return Ok(None);
}
for (idx, arg_type) in arg_types.iter().enumerate() {
self.map.new_local(level + 1, arg_type ,&args[idx]);
}
let result = self.parse_chunk(level, &name, &rule.body)?;
Ok(Some(result))
}
fn write_to(&mut self, content: &str, container: &mut Option<&mut String>) -> Result<(), RadError> {
if content.len() == 0 { return Ok(()); }
if let Some(container) = container {
container.push_str(content);
}
else {
if self.redirect {
self.temp_target.1.write(content.as_bytes())?;
} else {
match &mut self.write_option {
WriteOption::File(f) => f.write_all(content.as_bytes())?,
WriteOption::Stdout => print!("{}", content),
}
}
}
Ok(())
}
pub fn log_error(&mut self, log : &str) -> Result<(), RadError> {
self.error_logger.elog(log)?;
Ok(())
}
pub fn log_warning(&mut self, log : &str) -> Result<(), RadError> {
self.error_logger.wlog(log)?;
Ok(())
}
fn set_file(&mut self, file: &str) -> Result<(), RadError> {
let path = &Path::new(file);
if !path.exists() {
Err(RadError::InvalidCommandOption(format!("File, \"{}\" doesn't exist, therefore cannot be read by r4d.", path.display())))
} else {
self.current_input = file.to_owned();
self.error_logger.set_file(file);
Ok(())
}
}
#[allow(dead_code)]
pub fn add_custom_rules(&mut self, rules: HashMap<String, MacroRule>) {
self.map.custom.extend(rules.into_iter());
}
}
struct DefineParser{
arg_cursor :DefineCursor,
name: String,
args: String,
body: String,
bind: bool,
container: String,
}
impl DefineParser {
fn new() -> Self {
Self {
arg_cursor : DefineCursor::Name,
name : String::new(),
args : String::new(),
body : String::new(),
bind : false,
container : String::new(),
}
}
fn clear(&mut self) {
self.arg_cursor = DefineCursor::Name;
self.name.clear();
self.args.clear();
self.body.clear();
self.bind = false;
self.container.clear();
}
fn parse_define(&mut self, text: &str) -> Option<(String, String, String)> {
self.clear(); let mut char_iter = text.chars().peekable();
while let Some(ch) = char_iter.next() {
match self.arg_cursor {
DefineCursor::Name => {
if let ParseIgnore::Ignore = self.branch_name(ch) {continue;}
if !self.is_valid_name(ch) { return None; }
}
DefineCursor::Args => {
if let ParseIgnore::Ignore = self.branch_args(ch) {continue;}
if !self.is_valid_name(ch) { return None; }
}
DefineCursor::Body => ()
}
self.container.push(ch);
}
if self.args.len() == 0 && !self.bind {
return None;
}
self.body.push_str(&self.container);
Some((self.name.clone(), self.args.clone(), self.body.clone()))
}
fn is_valid_name(&mut self, ch : char) -> bool {
if self.container.len() == 0 { if !ch.is_alphabetic() {
return false;
}
} else { if !ch.is_alphanumeric() && ch != '_' {
return false;
}
}
true
}
fn branch_name(&mut self, ch: char) -> ParseIgnore {
if ch == '=' {
self.name.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Body;
self.bind = true;
ParseIgnore::Ignore
}
else if Utils::is_blank_char(ch) {
if self.name.len() != 0 {
self.container.clear();
ParseIgnore::None
} else {
ParseIgnore::Ignore
}
}
else if ch == ',' {
self.name.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Args;
ParseIgnore::Ignore
} else {
ParseIgnore::None
}
}
fn branch_args(&mut self, ch: char) -> ParseIgnore {
if Utils::is_blank_char(ch) && self.name.len() != 0 {
self.args.push_str(&self.container);
self.args.push(' ');
self.container.clear();
ParseIgnore::Ignore
}
else if ch == '=' {
self.args.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Body;
ParseIgnore::Ignore
}
else {
ParseIgnore::None
}
}
}
enum DefineCursor {
Name,
Args,
Body,
}
enum ParseIgnore {
Ignore,
None
}