use crate::auth::{AuthType, AuthFlags, AuthState};
#[cfg(feature = "debug")]
use crate::models::DiffOption;
#[cfg(feature = "debug")]
use crate::debugger::DebugSwitch;
#[cfg(feature = "debug")]
use std::io::Read;
use std::io::{self, BufReader, Write};
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::path::{ Path , PathBuf};
use crate::basic::MacroType;
use crate::closure_map::ClosureMap;
use crate::error::RadError;
use crate::logger::{Logger, LoggerLines};
#[cfg(feature = "debug")]
use crate::debugger::Debugger;
use crate::models::{CommentType, MacroFragment, MacroMap, MacroRule, RuleFile, UnbalancedChecker, WriteOption};
#[cfg(feature = "hook")]
use crate::hookmap::{HookMap, HookType};
use crate::utils::Utils;
use crate::{RadResult, consts::*};
use crate::lexor::*;
use crate::define_parser::DefineParser;
use crate::arg_parser::{ArgParser, GreedyState};
use regex::Regex;
use lazy_static::lazy_static;
lazy_static! {
static ref MAC_NAME : Regex = Regex::new(r#"^[_a-zA-Z]\w*$"#).expect("Failed to create regex expression");
}
pub(crate) struct ProcessorState {
pub always_greedy: bool,
pub auth_flags: AuthFlags,
pub current_input : String,
pub newline: String,
pub nopanic: bool,
pub paused: bool,
pub pipe_value: String,
pub purge: bool,
pub redirect: bool,
pub sandbox: bool,
pub strict: bool,
pub comment_type: CommentType,
pub temp_target: (PathBuf,File),
pub comment_char : Option<char>,
pub macro_char : Option<char>,
}
impl ProcessorState {
pub fn new(temp_target : (PathBuf, File)) -> Self {
Self {
current_input: String::from("stdin"),
auth_flags: AuthFlags::new(),
newline : LINE_ENDING.to_owned(),
pipe_value: String::new(),
paused: false,
redirect: false,
purge: false,
strict: true,
comment_type: CommentType::None,
nopanic: false,
sandbox : false,
always_greedy: false,
temp_target,
comment_char: None,
macro_char: None,
}
}
}
pub struct Processor{
map: MacroMap,
closure_map: ClosureMap,
#[cfg(feature = "hook")]
pub(crate) hook_map: HookMap,
defparser: DefineParser,
write_option: WriteOption,
logger: Logger,
#[cfg(feature = "debug")]
debugger: Debugger,
checker: UnbalancedChecker,
pub(crate) state: ProcessorState,
}
impl Processor {
pub fn new() -> Self {
Self::new_processor(true)
}
pub fn empty() -> Self {
Self::new_processor(false)
}
fn new_processor(use_basic: bool) -> 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()
);
let mut logger = Logger::new();
logger.set_write_options(Some(WriteOption::Terminal));
let map = if use_basic {
MacroMap::new()
} else {
MacroMap::empty()
};
Self {
map,
closure_map: ClosureMap::new(),
#[cfg(feature = "hook")]
hook_map: HookMap::new(),
write_option: WriteOption::Terminal,
defparser: DefineParser::new(),
logger,
#[cfg(feature = "debug")]
debugger : Debugger::new(),
checker : UnbalancedChecker::new(),
state: ProcessorState::new(temp_target),
}
}
pub fn write_to_file(&mut self, target_file: Option<impl AsRef<Path>>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&target_file);
if let Err(_) = file {
return Err(RadError::InvalidCommandOption(format!("Could not create file \"{}\"", target_file.as_ref().display())));
} else {
self.write_option = WriteOption::File(file.unwrap());
}
}
Ok(self)
}
pub fn error_to_file(&mut self, target_file: Option<impl AsRef<Path>>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&target_file);
if let Err(_) = file {
return Err(RadError::InvalidCommandOption(format!("Could not create file \"{}\"", target_file.as_ref().display())));
} else {
self.logger = Logger::new();
self.logger.set_write_options(Some(WriteOption::File(file.unwrap())));
}
}
Ok(self)
}
pub fn custom_comment_char(&mut self, character: char) -> Result<&mut Self, RadError> {
if UNALLOWED_CHARS.is_match(&character.to_string()) {
return Err(RadError::UnallowedChar(format!("\"{}\" is not allowed", character)));
} else if self.get_macro_char() == character {
return Err(RadError::UnallowedChar(format!("\"{}\" is already defined for macro character", character)));
}
self.state.comment_char.replace(character);
Ok(self)
}
pub fn custom_macro_char(&mut self, character: char) -> Result<&mut Self, RadError> {
if UNALLOWED_CHARS.is_match(&character.to_string()) {
return Err(RadError::UnallowedChar(format!("\"{}\" is not allowed", character)));
} else if self.get_comment_char() == character {
return Err(RadError::UnallowedChar(format!("\"{}\" is already defined for comment character", character)));
}
self.state.macro_char.replace(character);
Ok(self)
}
pub fn unix_new_line(&mut self, use_unix_new_line: bool) -> &mut Self {
if use_unix_new_line {
self.state.newline = "\n".to_owned();
}
self
}
pub fn greedy(&mut self, greedy: bool) -> &mut Self {
self.state.always_greedy = greedy;
self
}
pub fn purge(&mut self, purge: bool) -> &mut Self {
self.state.purge = purge;
self
}
pub fn lenient(&mut self, lenient: bool) -> &mut Self {
self.state.strict = !lenient;
self
}
#[deprecated(since = "1.2", note = "Comment is deprecated and will be removed in 2.0 version.")]
pub fn comment(&mut self, comment: bool) -> &mut Self {
if comment { self.state.comment_type = CommentType::Start; }
self
}
pub fn set_comment_type(&mut self, comment_type: CommentType) -> &mut Self {
self.state.comment_type = comment_type;
self
}
pub fn silent(&mut self, silent: bool) -> &mut Self {
if silent {
self.logger.suppress_warning();
}
self
}
pub fn nopanic(&mut self, nopanic: bool) -> &mut Self {
if nopanic {
self.state.nopanic = nopanic;
self.state.strict = false;
}
self
}
pub fn assert(&mut self, assert: bool) -> &mut Self {
if assert {
self.logger.assert();
self.write_option = WriteOption::Discard;
self.state.nopanic = true;
self.state.strict = false;
}
self
}
#[cfg(feature = "debug")]
pub fn debug(&mut self, debug: bool) -> &mut Self {
self.debugger.debug = debug;
self
}
#[cfg(feature = "debug")]
pub fn log(&mut self, log: bool) -> &mut Self {
self.debugger.log = log;
self
}
#[cfg(feature = "debug")]
pub fn diff(&mut self, diff: DiffOption) -> Result<&mut Self, RadError> {
self.debugger.enable_diff(diff)?;
Ok(self)
}
#[cfg(feature = "debug")]
pub fn interactive(&mut self, interactive: bool) -> &mut Self {
if interactive { self.debugger.set_interactive(); }
self
}
pub fn custom_rules(&mut self, paths: Option<Vec<impl AsRef<Path>>>) -> 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.as_ref())?;
}
self.map.custom.extend(rule_file.rules);
}
Ok(self)
}
pub fn allow(&mut self, auth_types : Option<Vec<AuthType>>) -> &mut Self {
if let Some(auth_types) = auth_types {
for auth in auth_types {
self.state.auth_flags.set_state(&auth, AuthState::Open)
}
}
self
}
pub fn allow_with_warning(&mut self, auth_types : Option<Vec<AuthType>>) -> &mut Self {
if let Some(auth_types) = auth_types {
for auth in auth_types {
self.state.auth_flags.set_state(&auth, AuthState::Warn)
}
}
self
}
pub fn discard(&mut self, discard: bool) -> &mut Self {
if discard {
self.write_option = WriteOption::Discard;
}
self
}
pub fn build(&mut self) -> Self {
std::mem::replace(self, Processor::new())
}
#[allow(dead_code)]
pub fn print_permission(&mut self) -> Result<(), RadError> {
if let Some(status) = self.state.auth_flags.get_status_string() {
let mut status_with_header = String::from("Permission granted");
status_with_header.push_str(&status);
self.log_warning(&status_with_header)?;
}
Ok(())
}
#[allow(dead_code)]
pub fn print_result(&mut self) -> Result<(), RadError> {
self.logger.print_result()?;
#[cfg(feature = "debug")]
self.debugger.yield_diff(&mut self.logger)?;
Ok(())
}
pub fn freeze_to_file(&self, path: impl AsRef<Path>) -> Result<(), RadError> {
RuleFile::new(Some(self.map.custom.clone())).freeze(path.as_ref())?;
Ok(())
}
pub fn add_basic_rules(&mut self, basic_rules:Vec<(&str,MacroType)>) {
for (name, macro_ref) in basic_rules {
self.map.basic.add_new_rule(name, macro_ref);
}
}
pub fn add_closure_rule(&mut self, name: &'static str, arg_count: usize, closure : Box<dyn FnMut(Vec<String>) -> Option<String>>) {
self.closure_map.add_new(name, arg_count, closure);
}
pub fn add_custom_rules(&mut self, rules: Vec<(impl AsRef<str>,&str,&str)>) -> Result<(), RadError> {
for (name,args,body) in rules {
let name = name.as_ref();
if !MAC_NAME.is_match(name) {
return Err(RadError::InvalidMacroName(format!("Name : \"{}\" is not a valid macro name", name)));
}
self.map.custom.insert(
name.to_owned(),
MacroRule {
name: name.to_owned(),
args: args.split(' ').map(|s| s.to_owned()).collect::<Vec<String>>(),
body: body.to_owned()
}
);
}
Ok(())
}
pub fn add_static_rules(&mut self, rules: Vec<(impl AsRef<str>,&str)>) -> RadResult<()> {
for (name,body) in rules {
let name = name.as_ref();
if !MAC_NAME.is_match(name) {
return Err(RadError::InvalidMacroName(format!("Name : \"{}\" is not a valid macro name", name)));
}
self.map.custom.insert(
name.to_owned(),
MacroRule {
name: name.to_owned(),
args: vec![],
body: body.to_owned()
}
);
}
Ok(())
}
#[cfg(feature = "hook")]
pub fn register_hook(&mut self, hook_type: HookType, target_macro: &str, invoke_macro: &str, target_count: usize, resetable: bool) -> RadResult<()> {
if target_macro.len() == 0 {
return Err(RadError::InvalidMacroName(format!("Cannot register hook for macro \"{}\"",target_macro)));
}
if invoke_macro.len() == 0 {
return Err(RadError::InvalidMacroName(format!("Cannot register hook which invokes a macro \"{}\"",target_macro)));
}
self.hook_map.add_hook(hook_type, target_macro, invoke_macro, target_count, resetable)?;
Ok(())
}
#[cfg(feature = "hook")]
pub fn deregister_hook(&mut self, hook_type: HookType, target_macro: &str) -> RadResult<()> {
if target_macro.len() == 0 {
return Err(RadError::InvalidMacroName(format!("Cannot deregister hook for macro \"{}\"",target_macro)));
}
self.hook_map.del_hook(hook_type, target_macro)?;
Ok(())
}
pub fn from_string(&mut self, content: &str) -> Result<(), RadError> {
self.set_input("String")?;
let mut reader = content.as_bytes();
self.from_buffer(&mut reader, None, false)?;
Ok(())
}
pub fn from_stdin(&mut self) -> Result<(), RadError> {
let stdin = io::stdin();
#[cfg(feature = "debug")]
if self.is_debug() {
let mut input = String::new();
stdin.lock().read_to_string(&mut input)?;
self.from_buffer(&mut input.as_bytes(),None,false)?;
return Ok(());
}
let mut reader = stdin.lock();
self.from_buffer(&mut reader, None, false)?;
Ok(())
}
pub fn from_file(&mut self, path :impl AsRef<Path>) -> Result<(), RadError> {
let backup = if self.state.sandbox { Some(self.backup()) } else { None };
self.set_file(path.as_ref().to_str().unwrap())?;
let file_stream = File::open(path)?;
let mut reader = BufReader::new(file_stream);
self.from_buffer(&mut reader, backup, false)?;
Ok(())
}
pub(crate) fn from_file_as_chunk(&mut self, path :impl AsRef<Path>) -> Result<Option<String>, RadError> {
let backup = if self.state.sandbox { Some(self.backup()) } else { None };
self.set_file(path.as_ref().to_str().unwrap())?;
let file_stream = File::open(path)?;
let mut reader = BufReader::new(file_stream);
let chunk = self.from_buffer(&mut reader, backup, true);
chunk
}
fn from_buffer(&mut self,buffer: &mut impl std::io::BufRead, backup: Option<SandboxBackup>, use_container: bool) -> Result<Option<String>, RadError> {
let mut line_iter = Utils::full_lines(buffer).peekable();
let mut lexor = Lexor::new(self.get_macro_char(), self.get_comment_char(), &self.state.comment_type);
let mut frag = MacroFragment::new();
let container = String::new(); let mut cont = if use_container{ Some(container) } else { None };
#[cfg(feature = "debug")]
self.debugger.user_input_on_start(&self.state.current_input,&mut self.logger)?;
loop {
#[cfg(feature = "debug")]
if let Some(line) = line_iter.peek() {
let line = line.as_ref().unwrap();
self.debugger.add_line_cache(line);
self.debugger.user_input_on_line(&frag, &mut self.logger)?;
}
let result = self.parse_line(&mut line_iter, &mut lexor ,&mut frag)?;
match result {
ParseResult::Printable(remainder) => {
self.write_to(&remainder,&mut cont)?;
#[cfg(feature = "debug")]
self.debugger.clear_line_cache();
if &frag.whole_string != "" {
frag = MacroFragment::new();
}
}
ParseResult::FoundMacro(remainder) => {
self.write_to(&remainder,&mut cont)?;
}
ParseResult::NoPrint => { }
ParseResult::EOI => {
if use_container {
Utils::pop_newline(cont.as_mut().unwrap());
}
break
}
}
#[cfg(feature = "debug")]
self.debugger.inc_line_number();
}
if let Some(backup) = backup { self.recover(backup)?; self.state.sandbox = false; }
if use_container {
Ok(cont)
} else {
Ok(None)
}
}
#[cfg(feature = "debug")]
fn is_local(&self, mut level: usize, name: &str) -> bool {
while level > 0 {
if self.map.local.contains_key(&Utils::local_name(level, &name)) {
return true;
}
level = level - 1;
}
false
}
#[cfg(feature = "debug")]
fn check_debug_macro(&mut self, frag: &mut MacroFragment, level: usize) -> Result<(), RadError> {
if !self.is_debug() { return Ok(()); }
if level == 0 { self.debugger.user_input_on_macro(&frag, &mut self.logger)?; }
else {self.debugger.user_input_on_step(&frag, &mut self.logger)?;}
if level == 0 {
self.debugger.clear_line_cache();
}
Ok(())
}
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.logger.add_line_number();
if let Some(line) = lines.next() {
let line = line?;
#[cfg(feature = "debug")]
self.debugger.write_to_original(&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_args(&mut self, level: usize, _caller: &str, chunk: &str) -> Result<String, RadError> {
let mut lexor = Lexor::new(self.get_macro_char(), self.get_comment_char(), &self.state.comment_type);
let mut frag = MacroFragment::new();
let mut result = String::new();
let backup = self.logger.backup_lines();
self.logger.set_chunk(true);
for line in Utils::full_lines(chunk.as_bytes()) {
let line = line?;
result.push_str(&self.parse(&mut lexor, &mut frag, &line, level, "")?);
self.logger.add_line_number();
}
if !frag.whole_string.is_empty() {
result.push_str(&frag.whole_string);
}
self.logger.set_chunk(false);
self.logger.recover_lines(backup);
return Ok(result);
}
fn parse_chunk_body(&mut self, level: usize, caller: &str, chunk: &str) -> Result<String, RadError> {
let mut lexor = Lexor::new(self.get_macro_char(), self.get_comment_char(), &self.state.comment_type);
let mut frag = MacroFragment::new();
let backup = self.logger.backup_lines();
let result = self.parse(&mut lexor, &mut frag, &chunk, level, caller)?;
self.logger.recover_lines(backup);
return Ok(result);
}
fn parse(&mut self,lexor: &mut Lexor, frag: &mut MacroFragment, line: &str, level: usize, caller: &str) -> Result<String, RadError> {
self.logger.reset_char_number();
let mut remainder = String::new();
lexor.reset_escape();
if self.state.comment_type != CommentType::None
&& line.starts_with(self.get_comment_char()) {
return Ok(String::new());
}
for ch in line.chars() {
self.logger.add_char_number();
let lex_result = lexor.lex(ch)?;
match lex_result {
LexResult::CommentExit => {
self.lex_branch_comment_exit(frag, &mut remainder);
return Ok(remainder);
}
LexResult::Discard => (),
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::RestartName => {
remainder.push_str(&frag.whole_string);
frag.clear();
frag.whole_string.push(self.get_macro_char());
},
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);
}
}
#[cfg(feature = "hook")]
if frag.is_empty() && level == 0 {
if let Some(mac_name) = self.hook_map.add_char_count(ch) {
let mut hook_frag = MacroFragment::new();
hook_frag.name = mac_name;
let mut hook_mainder = String::new();
let mut hook_lexor = Lexor::new(self.get_macro_char(), self.get_comment_char(), &self.state.comment_type);
self.lex_branch_end_invoke(
&mut hook_lexor,
&mut hook_frag,
&mut hook_mainder,
level,
&frag.name
)?;
if hook_mainder.len() != 0 {
remainder.push_str(&hook_mainder);
}
} }
} Ok(remainder)
}
fn evaluate(&mut self,level: usize, caller: &str, name: &str, raw_args: &str, greedy: bool) -> Result<EvalResult, RadError> {
let level = level + 1;
let mut args : String = raw_args.to_owned();
if !self.map.is_keyword(name) {
args = self.parse_chunk_args(level, name, raw_args)?;
}
if caller == name {
self.log_warning(&format!("Calling self, which is \"{}\", can possibly trigger infinite loop", name))?;
}
if self.map.is_keyword(name) {
let func = self.map.keyword.get(name).unwrap();
let final_result = func(&args, level,self)?;
return Ok(EvalResult::Eval(final_result));
}
let mut temp_level = level;
while temp_level > 0 {
if let Some(local) = self.map.local.get(&Utils::local_name(temp_level, &name)) {
return Ok(EvalResult::Eval(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(EvalResult::Eval(Some(result)));
} else {
return Ok(EvalResult::InvalidArg);
}
}
else if self.map.basic.contains(&name) {
let func = self.map.basic.get(name).unwrap();
let final_result = func(&args, greedy, self)?;
return Ok(EvalResult::Eval(final_result));
}
else if self.closure_map.contains(&name) {
let final_result = self.closure_map.call(name, &args, greedy)?;
return Ok(EvalResult::Eval(final_result));
}
else {
return Ok(EvalResult::InvalidName);
}
}
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, ArgParser::new().args_to_vec(arg_values, ',', GreedyState::Never).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_body(level, &name, &rule.body)?;
Ok(Some(result))
}
fn add_rule(&mut self, frag: &MacroFragment, remainder: &mut String) -> Result<(), RadError> {
if let Some((name,args,body)) = self.defparser.parse_define(&frag.args) {
if self.state.strict && self.map.contains(&name) {
self.log_error("Can't override exsiting macro on strict mode")?;
return Err(RadError::StrictPanic);
}
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);
}
Ok(())
}
fn write_to(&mut self, content: &str, container: &mut Option<String>) -> Result<(), RadError> {
if content.len() == 0 { return Ok(()); }
if let Some(cont) = container.as_mut() {
cont.push_str(content);
return Ok(());
}
#[cfg(feature = "debug")]
self.debugger.write_to_processed(content)?;
if self.state.redirect {
self.state.temp_target.1.write(content.as_bytes())?;
} else {
match &mut self.write_option {
WriteOption::File(f) => f.write_all(content.as_bytes())?,
WriteOption::Terminal => print!("{}", content),
WriteOption::Discard => () }
}
Ok(())
}
fn lex_branch_comment_exit(&mut self, frag: &mut MacroFragment, remainder: &mut String) {
remainder.push_str(&frag.whole_string);
remainder.push_str(&self.state.newline);
frag.clear();
}
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) -> Result<(), RadError> {
#[cfg(feature = "debug")]
self.debugger.user_input_before_macro(&frag, &mut self.logger)?;
frag.whole_string.push(ch);
if self.state.paused && frag.name != "pause" {
lexor.reset();
remainder.push_str(&frag.whole_string);
frag.clear();
}
Ok(())
}
fn lex_branch_empty_name(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor) {
frag.whole_string.push(ch);
self.logger.freeze_number();
if self.state.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.state.paused {
self.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.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.lex_branch_end_frag_define(lexor, frag, remainder, level)?;
} else { self.lex_branch_end_invoke(lexor,frag,remainder, level, caller)?;
}
Ok(())
}
fn lex_branch_end_frag_define(&mut self,lexor: &mut Lexor, frag: &mut MacroFragment, remainder: &mut String, level: usize) -> Result<(), RadError> {
self.add_rule(frag, remainder)?;
lexor.escape_next_newline();
#[cfg(feature = "debug")]
self.check_debug_macro(frag, level)?;
frag.clear();
Ok(())
}
fn lex_branch_end_invoke(&mut self,lexor: &mut Lexor, frag: &mut MacroFragment, remainder: &mut String, level: usize, caller: &str) -> Result<(), RadError> {
if frag.name.len() == 0 {
self.log_error("Name is empty")?;
remainder.push_str(&frag.whole_string);
frag.clear();
return Ok(());
}
#[cfg(feature = "debug")]
{
self.debugger.print_log(&frag.name,&frag.args, frag, &mut self.logger)?;
self.debugger.break_point(frag, &mut self.logger)?;
if frag.name.len() == 0 {
lexor.escape_next_newline();
return Ok(());
}
}
let evaluation_result = self.evaluate(level, caller, &frag.name, &frag.args, frag.greedy || self.state.always_greedy);
match evaluation_result {
Err(error) => {
self.lex_branch_end_frag_eval_result_error(error)?;
}
Ok(eval_variant) => {
self.lex_branch_end_frag_eval_result_ok(eval_variant,frag,remainder,lexor,level)?;
}
}
frag.clear();
Ok(())
}
fn lex_branch_end_frag_eval_result_error(&mut self, error : RadError) -> Result<(), RadError> {
if let RadError::Panic = error{
();
} else {
self.log_error(&format!("{}", error))?;
}
if self.state.nopanic {
Ok(())
} else {
Err(RadError::Panic)
}
}
fn lex_branch_end_frag_eval_result_ok(&mut self, variant : EvalResult, frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor, level: usize) -> Result<(), RadError> {
match variant {
EvalResult::Eval(content) => {
#[cfg(feature = "debug")]
if !self.is_local(level + 1, &frag.name) {
self.check_debug_macro(frag, level)?;
}
if let None = content {
lexor.escape_next_newline();
} else {
let mut content = content.unwrap();
if frag.trimmed {
content = Utils::trim(&content);
}
if frag.yield_literal {
content = format!("\\*{}*\\", content);
}
#[cfg(feature = "hook")]
if let Some(mac_name) = self.hook_map.add_macro_count(&frag.name) {
let mut hook_frag = MacroFragment::new();
hook_frag.name = mac_name;
let mut hook_mainder = String::new();
let mut hook_lexor = Lexor::new(self.get_macro_char(), self.get_comment_char(), &self.state.comment_type);
self.lex_branch_end_invoke(
&mut hook_lexor,
&mut hook_frag,
&mut hook_mainder,
level,
&frag.name
)?;
if hook_mainder.len() != 0 {
content.push_str(&hook_mainder);
}
}
if frag.pipe {
self.state.pipe_value = content;
lexor.escape_next_newline();
} else {
remainder.push_str(&content);
}
}
}
EvalResult::InvalidArg => {
if self.state.strict {
return Err(RadError::StrictPanic);
} else {
remainder.push_str(&frag.whole_string);
}
}
EvalResult::InvalidName => {
if self.state.strict {
return Err(RadError::InvalidMacroName(format!("Failed to invoke a macro : \"{}\"", frag.name)));
}
else if self.state.purge {
lexor.escape_next_newline();
} else {
self.log_error(&format!("Failed to invoke a macro : \"{}\"", frag.name))?;
remainder.push_str(&frag.whole_string);
}
}
} 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();
}
pub(crate) fn get_comment_char(&self) -> char {
comment_start(self.state.comment_char)
}
pub(crate) fn get_macro_char(&self) -> char {
macro_start(self.state.macro_char)
}
pub(crate) fn get_map(&mut self) -> &mut MacroMap {
&mut self.map
}
pub(crate) fn get_auth_state(&self, auth_type : &AuthType) -> AuthState {
*self.state.auth_flags.get_state(auth_type)
}
pub(crate) fn set_temp_file(&mut self, path: &Path) {
self.state.temp_target = (path.to_owned(),OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.unwrap());
}
pub(crate) fn set_sandbox(&mut self) {
self.state.sandbox = true;
}
pub(crate) fn get_temp_path(&self) -> &Path {
self.state.temp_target.0.as_ref()
}
pub(crate) fn get_temp_file(&self) -> &File {
&self.state.temp_target.1
}
fn backup(&self) -> SandboxBackup {
SandboxBackup {
current_input: self.state.current_input.clone(),
local_macro_map: self.map.local.clone(),
logger_lines: self.logger.backup_lines(),
}
}
fn recover(&mut self, backup: SandboxBackup) -> Result<(), RadError> {
self.logger.set_file(&backup.current_input);
self.state.current_input = backup.current_input;
self.map.local= backup.local_macro_map;
self.logger.recover_lines(backup.logger_lines);
self.set_file_env(&self.state.current_input)?;
Ok(())
}
pub(crate) fn track_assertion(&mut self, success: bool) -> Result<(), RadError> {
self.logger.alog(success)?;
Ok(())
}
pub(crate) fn log_error(&mut self, log : &str) -> Result<(), RadError> {
self.logger.elog(log)?;
Ok(())
}
pub(crate) fn log_warning(&mut self, log : &str) -> Result<(), RadError> {
self.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.state.current_input = file.to_owned();
self.logger.set_file(file);
self.set_file_env(file)?;
Ok(())
}
}
fn set_file_env(&self, file: &str) -> Result<(), RadError> {
let path = Path::new(file);
std::env::set_var("RAD_FILE", file);
std::env::set_var("RAD_FILE_DIR", std::fs::canonicalize(path)?.parent().unwrap());
Ok(())
}
fn set_input(&mut self, input: &str) -> Result<(), RadError> {
self.state.current_input = input.to_owned();
self.logger.set_file(input);
Ok(())
}
#[cfg(feature = "debug")]
pub(crate) fn is_debug(&self) -> bool {
self.debugger.debug
}
#[cfg(feature = "debug")]
pub(crate) fn get_debug_switch(&self) -> &DebugSwitch {
&self.debugger.debug_switch
}
#[cfg(feature = "debug")]
pub(crate) fn set_prompt_log(&mut self, prompt: &str) {
self.debugger.set_prompt_log(prompt);
}
}
#[derive(Debug)]
enum ParseResult {
FoundMacro(String),
Printable(String),
NoPrint,
EOI,
}
struct SandboxBackup {
current_input: String,
local_macro_map: HashMap<String,String>,
logger_lines: LoggerLines,
}
enum EvalResult {
Eval(Option<String>),
InvalidName,
InvalidArg,
}