use crate::deterred_map::DFunctionMacroType;
use crate::deterred_map::DeterredMacroMap;
use crate::error::RadError;
use crate::function_map::FunctionMacroMap;
use crate::function_map::FunctionMacroType;
use crate::runtime_map::{RuntimeMacro, RuntimeMacroMap};
#[cfg(feature = "signature")]
use crate::sigmap::MacroSignature;
use crate::trim;
use crate::utils::Utils;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::File;
use std::path::{Path, PathBuf};
pub type RadResult<T> = Result<T, RadError>;
pub enum WriteOption<'a> {
File(File),
Variable(&'a mut String),
Return,
Terminal,
Discard,
}
#[derive(Clone)]
pub struct LocalMacro {
pub level: usize,
pub name: String,
pub body: String,
}
impl LocalMacro {
pub fn new(level: usize, name: String, body: String) -> Self {
Self { level, name, body }
}
}
pub(crate) struct MacroMap {
pub deterred: DeterredMacroMap,
pub function: FunctionMacroMap,
pub runtime: RuntimeMacroMap,
pub local: HashMap<String, LocalMacro>,
}
impl MacroMap {
pub fn empty() -> Self {
Self {
deterred: DeterredMacroMap::empty(),
function: FunctionMacroMap::empty(),
runtime: RuntimeMacroMap::new(),
local: HashMap::new(),
}
}
pub fn new() -> Self {
Self {
deterred: DeterredMacroMap::new(),
function: FunctionMacroMap::new(),
runtime: RuntimeMacroMap::new(),
local: HashMap::new(),
}
}
pub fn clear_runtime_macros(&mut self, volatile: bool) {
self.runtime.clear_runtime_macros(volatile);
}
pub fn add_local_macro(&mut self, level: usize, name: &str, value: &str) {
self.local.insert(
Utils::local_name(level, name),
LocalMacro::new(level, name.to_owned(), value.to_owned()),
);
}
pub fn remove_local_macro(&mut self, level: usize, name: &str) {
self.local.remove(&Utils::local_name(level, name));
}
pub fn clear_local(&mut self) {
self.local.clear();
}
pub fn clear_lower_locals(&mut self, current_level: usize) {
self.local.retain(|_, mac| mac.level <= current_level);
}
pub fn is_deterred_macro(&self, name: &str) -> bool {
self.deterred.contains(name)
}
pub fn contains_macro(
&self,
macro_name: &str,
macro_type: MacroType,
hygiene_type: Hygiene,
) -> bool {
match macro_type {
MacroType::Deterred => self.deterred.contains(macro_name),
MacroType::Function => self.function.contains(macro_name),
MacroType::Runtime => self.runtime.contains(macro_name, hygiene_type),
MacroType::Any => {
self.function.contains(macro_name)
|| self.runtime.contains(macro_name, hygiene_type)
|| self.deterred.contains(macro_name)
}
}
}
pub fn register_runtime(
&mut self,
name: &str,
args: &str,
body: &str,
hygiene_type: Hygiene,
) -> RadResult<()> {
let mac = RuntimeMacro::new(&trim!(name), &trim!(args), body);
self.runtime.new_macro(name, mac, hygiene_type);
Ok(())
}
pub fn undefine(&mut self, macro_name: &str, macro_type: MacroType, hygiene_type: Hygiene) {
match macro_type {
MacroType::Deterred => {
self.deterred.undefine(macro_name);
}
MacroType::Function => {
self.function.undefine(macro_name);
}
MacroType::Runtime => {
self.runtime.undefine(macro_name, hygiene_type);
}
MacroType::Any => {
self.function.undefine(macro_name);
self.runtime.undefine(macro_name, hygiene_type);
self.deterred.undefine(macro_name);
}
}
}
pub fn rename(
&mut self,
macro_name: &str,
target_name: &str,
macro_type: MacroType,
hygiene_type: Hygiene,
) {
match macro_type {
MacroType::Deterred => {
self.deterred.rename(macro_name, target_name);
}
MacroType::Function => {
self.function.rename(macro_name, target_name);
}
MacroType::Runtime => {
self.runtime.rename(macro_name, target_name, hygiene_type);
}
MacroType::Any => {
if !self.runtime.rename(macro_name, target_name, hygiene_type)
&& !self.deterred.rename(macro_name, target_name)
{
self.function.rename(macro_name, target_name);
}
}
}
}
pub fn append(&mut self, name: &str, target: &str, hygiene_type: Hygiene) {
if self.runtime.contains(name, hygiene_type) {
self.runtime.append_macro(name, target, hygiene_type);
}
}
pub fn replace(&mut self, name: &str, target: &str, hygiene_type: Hygiene) -> bool {
if self.runtime.contains(name, hygiene_type) {
self.runtime.replace_macro(name, target, hygiene_type);
true
} else {
false
}
}
#[cfg(feature = "signature")]
pub fn get_signature(&self, macro_name: &str) -> Option<MacroSignature> {
if let Some(mac) = self.runtime.get(macro_name, Hygiene::None) {
Some(MacroSignature::from(mac))
} else if let Some(mac) = self.deterred.get_signature(macro_name) {
Some(MacroSignature::from(mac))
} else {
self.function
.get_signature(macro_name)
.map(MacroSignature::from)
}
}
#[cfg(feature = "signature")]
pub fn get_signatures(&self) -> Vec<MacroSignature> {
let key_iter = self
.deterred
.macros
.iter()
.map(|(_, sig)| MacroSignature::from(sig));
let funcm_iter = self
.function
.macros
.iter()
.map(|(_, sig)| MacroSignature::from(sig));
let runtime_iter = self
.runtime
.macros
.iter()
.map(|(_, mac)| MacroSignature::from(mac));
key_iter.chain(funcm_iter).chain(runtime_iter).collect()
}
#[cfg(feature = "signature")]
pub fn get_default_signatures(&self) -> Vec<MacroSignature> {
let key_iter = self
.deterred
.macros
.iter()
.map(|(_, sig)| MacroSignature::from(sig));
let funcm_iter = self
.function
.macros
.iter()
.map(|(_, sig)| MacroSignature::from(sig));
key_iter.chain(funcm_iter).collect()
}
#[cfg(feature = "signature")]
pub fn get_runtime_signatures(&self) -> Vec<MacroSignature> {
self.runtime
.macros
.iter()
.map(|(_, mac)| MacroSignature::from(mac))
.collect()
}
}
pub(crate) struct UnbalancedChecker {
paren: usize,
}
impl UnbalancedChecker {
pub fn new() -> Self {
Self { paren: 0 }
}
pub fn check(&mut self, ch: char) -> bool {
match ch {
'(' => self.paren += 1,
')' => {
if self.paren > 0 {
self.paren -= 1;
} else {
return false;
}
}
_ => {
return true;
}
}
true
}
}
#[derive(Serialize, Deserialize)]
pub struct RuleFile {
pub rules: HashMap<String, RuntimeMacro>,
}
impl RuleFile {
pub fn new(rules: Option<HashMap<String, RuntimeMacro>>) -> Self {
if let Some(content) = rules {
Self { rules: content }
} else {
Self {
rules: HashMap::new(),
}
}
}
pub fn melt(&mut self, path: &Path) -> RadResult<()> {
Utils::is_real_path(path)?;
let result = bincode::deserialize::<Self>(&std::fs::read(path)?);
if let Err(err) = result {
Err(RadError::BincodeError(format!(
"Failed to melt the file : {} \n {}",
path.display(),
err
)))
} else {
self.rules.extend(result.unwrap().rules.into_iter());
Ok(())
}
}
pub fn melt_literal(&mut self, literal: &[u8]) -> RadResult<()> {
let result = bincode::deserialize::<Self>(literal);
if let Ok(rule_file) = result {
self.rules.extend(rule_file.rules.into_iter());
Ok(())
} else {
Err(RadError::BincodeError(
"Failed to melt the literal value".to_string(),
))
}
}
pub(crate) fn freeze(&self, path: &std::path::Path) -> RadResult<()> {
let result = bincode::serialize(self);
if result.is_err() {
Err(RadError::BincodeError(format!(
"Failed to freeze to a file : {}",
path.display()
)))
} else if std::fs::write(path, result.unwrap()).is_err() {
Err(RadError::InvalidArgument(format!(
"Failed to create a file : {}",
path.display()
)))
} else {
Ok(())
}
}
}
#[derive(Debug)]
pub(crate) struct MacroFragment {
pub whole_string: String,
pub name: String,
pub args: String,
#[cfg(feature = "debug")]
pub processed_args: String,
pub pipe: bool,
pub greedy: bool,
pub yield_literal: bool,
pub trim_input: bool,
pub trimmed: bool,
}
impl MacroFragment {
pub fn new() -> Self {
MacroFragment {
whole_string: String::new(),
name: String::new(),
args: String::new(),
#[cfg(feature = "debug")]
processed_args: String::new(),
pipe: false,
greedy: false,
yield_literal: false,
trim_input: false,
trimmed: false,
}
}
pub(crate) fn clear(&mut self) {
self.whole_string.clear();
self.name.clear();
self.args.clear();
#[cfg(feature = "debug")]
self.processed_args.clear();
self.pipe = false;
self.greedy = false;
self.yield_literal = false;
self.trim_input = false;
self.trimmed = false;
}
pub(crate) fn is_empty(&self) -> bool {
self.whole_string.len() == 0
}
pub(crate) fn has_attribute(&self) -> bool {
self.pipe || self.greedy || self.yield_literal || self.trimmed || self.trim_input
}
}
#[derive(PartialEq, Debug)]
pub enum CommentType {
None,
Start,
Any,
}
impl std::str::FromStr for CommentType {
type Err = RadError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let comment_type = match s.to_lowercase().as_str() {
"none" => Self::None,
"start" => Self::Start,
"any" => Self::Any,
_ => {
return Err(RadError::InvalidCommandOption(format!(
"Comment type : \"{}\" is not available.",
s
)));
}
};
Ok(comment_type)
}
}
#[derive(Debug)]
pub enum DiffOption {
None,
All,
Change,
}
impl std::str::FromStr for DiffOption {
type Err = RadError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
let var = match text.to_lowercase().as_str() {
"none" => Self::None,
"all" => Self::All,
"change" => Self::Change,
_ => {
return Err(RadError::InvalidConversion(format!(
"Diffoption, \"{}\" is not a valid type",
text
)))
}
};
Ok(var)
}
}
#[derive(Debug, PartialEq)]
pub enum FlowControl {
None,
Escape,
Exit,
}
#[cfg(feature = "signature")]
pub enum SignatureType {
All,
Default,
Runtime,
}
#[cfg(feature = "signature")]
impl SignatureType {
pub fn from_str(text: &str) -> RadResult<Self> {
let variant = match text.to_lowercase().as_str() {
"all" => Self::All,
"default" => Self::Default,
"runtime" => Self::Runtime,
_ => {
return Err(RadError::InvalidConversion(format!(
"\"{}\" is not supported signature type",
text
)))
}
};
Ok(variant)
}
}
pub type StorageResult<T> = Result<T, Box<dyn std::error::Error>>;
pub trait RadStorage {
fn update(&mut self, args: &[String]) -> StorageResult<()>;
fn extract(&mut self, serialize: bool) -> StorageResult<Option<StorageOutput>>;
}
#[derive(Debug)]
pub enum StorageOutput {
Binary(Vec<u8>),
Text(String),
}
impl StorageOutput {
pub(crate) fn into_printable(self) -> String {
match self {
Self::Binary(bytes) => format!("{:?}", bytes),
Self::Text(text) => text,
}
}
}
#[derive(Debug)]
pub enum RelayTarget {
None,
File(FileTarget),
Macro(String),
#[cfg(not(feature = "wasm"))]
Temp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ProcessInput {
Stdin,
File(PathBuf),
}
impl std::fmt::Display for ProcessInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Stdin => write!(f, "Stdin"),
Self::File(file) => write!(f, "{}", file.display()),
}
}
}
#[derive(PartialEq, Clone, Copy)]
pub enum ErrorBehaviour {
Strict,
Lenient,
Purge,
Assert,
}
#[derive(Clone)]
pub struct ExtMacroBuilder {
pub(crate) macro_name: String,
pub(crate) macro_type: ExtMacroType,
pub(crate) args: Vec<String>,
pub(crate) macro_body: Option<ExtMacroBody>,
pub(crate) macro_desc: Option<String>,
}
impl ExtMacroBuilder {
pub fn new(macro_name: &str) -> Self {
Self {
macro_name: macro_name.to_string(),
macro_type: ExtMacroType::Function,
args: vec![],
macro_body: None,
macro_desc: None,
}
}
pub fn function(mut self, func: FunctionMacroType) -> Self {
self.macro_type = ExtMacroType::Function;
self.macro_body = Some(ExtMacroBody::Function(func));
self
}
pub fn deterred(mut self, func: DFunctionMacroType) -> Self {
self.macro_type = ExtMacroType::Deterred;
self.macro_body = Some(ExtMacroBody::Deterred(func));
self
}
pub fn args(mut self, args: &[impl AsRef<str>]) -> Self {
self.args = args.iter().map(|a| a.as_ref().to_string()).collect();
self
}
pub fn desc(mut self, description: &str) -> Self {
self.macro_desc.replace(description.to_string());
self
}
}
#[derive(Clone)]
pub(crate) enum ExtMacroType {
Function,
Deterred,
}
#[derive(Clone)]
pub(crate) enum ExtMacroBody {
Function(FunctionMacroType),
Deterred(DFunctionMacroType),
}
pub enum MacroType {
Function,
Deterred,
Runtime,
Any,
}
#[derive(Debug)]
pub struct FileTarget {
pub(crate) path: PathBuf,
pub(crate) file: Option<File>,
}
impl std::fmt::Display for FileTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl FileTarget {
pub fn empty() -> Self {
Self {
path: PathBuf::new(),
file: None,
}
}
pub fn set_path(&mut self, path: &Path) {
self.path = path.to_owned();
self.file = Some(
std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.unwrap(),
);
}
}
#[derive(PartialEq, Clone, Copy)]
pub enum Hygiene {
None,
Macro,
Input,
Aseptic,
}
pub(crate) struct RegexCache {
cache: HashMap<String, Regex>,
register: HashMap<String, Regex>,
}
impl RegexCache {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
register: HashMap::new(),
}
}
pub fn register(&mut self, name: &str, source: &str) -> RadResult<()> {
self.cache.insert(name.to_string(), Regex::new(source)?);
Ok(())
}
pub fn append(&mut self, src: &str) -> RadResult<&Regex> {
if self.cache.len() > 100 {
self.cache.clear();
}
self.cache.insert(src.to_string(), Regex::new(src)?);
Ok(self.get(src).unwrap())
}
pub fn get(&self, src: &str) -> Option<&Regex> {
if self.register.get(src).is_some() {
self.register.get(src)
} else {
self.cache.get(src)
}
}
}