use std::{
collections::HashMap,
sync::{atomic::AtomicUsize, Arc},
};
use hemtt_tokens::{Symbol, Token};
use crate::{defines::Defines, ifstate::IfStates, Error};
const BUILTIN: [&str; 37] = [
"__LINE__",
"__FILE__",
"__DATE_ARR__",
"__DATE_STR__",
"__DATE_STR_ISO8601__",
"__TIME__",
"__TIME_UTC__",
"__TIMESTAMP_UTC__",
"__COUNTER__",
"__COUNTER_RESET__",
"__RAND_INT8__",
"__RAND_INT16__",
"__RAND_INT32__",
"__RAND_INT64__",
"__RAND_UINT8__",
"__RAND_UINT16__",
"__RAND_UINT32__",
"__RAND_UINT64__",
"__ARMA__",
"__ARMA3__",
"__A3_DEBUG__",
"__HEMTT__",
"__HEMTT_DEBUG__",
"__HEMTT_VERSION__",
"__HEMTT_VERSION_MAJ__",
"__HEMTT_VERSION_MIN__",
"__HEMTT_VERSION_REV__",
"__HEMTT_VERSION_BUILD__",
"__HEMTT_PROJECT_NAME__",
"__HEMTT_PROJECT_VERSION__",
"__HEMTT_PROJECT_VERSION_MAJ__",
"__HEMTT_PROJECT_VERSION_MIN__",
"__HEMTT_PROJECT_VERSION_REV__",
"__HEMTT_PROJECT_VERSION_BUILD__",
"__HEMTT_PROJECT_MAINPREFIX__",
"__HEMTT_PROJECT_PREFIX__",
"__HEMTT_PROJECT_AUTHOR__",
];
#[derive(Clone, Debug)]
pub struct Context<'a> {
ifstates: IfStates,
definitions: Defines,
entry: String,
current_file: String,
counter: Arc<AtomicUsize>,
trace: Vec<Token>,
parent: Option<&'a Self>,
}
impl<'a> Context<'a> {
#[must_use]
pub fn new(entry: String) -> Self {
Self {
ifstates: IfStates::new(),
definitions: HashMap::new(),
current_file: entry.clone(),
entry,
counter: Arc::new(AtomicUsize::new(0)),
trace: Vec::new(),
parent: None,
}
}
#[must_use]
pub fn stack(&'a self, source: Token) -> Context<'a> {
Self {
ifstates: self.ifstates.clone(),
definitions: HashMap::new(),
current_file: self.current_file.clone(),
entry: self.entry.clone(),
counter: self.counter.clone(),
trace: {
let mut trace = self.trace.clone();
trace.push(source);
trace
},
parent: Some(self),
}
}
pub fn push(&mut self, source: Token) {
self.trace.push(source);
}
pub fn pop(&mut self) -> Option<Token> {
self.trace.pop()
}
#[must_use]
pub fn trace(&self) -> Vec<Token> {
self.trace.clone()
}
#[must_use]
pub const fn ifstates(&self) -> &IfStates {
&self.ifstates
}
pub fn ifstates_mut(&mut self) -> &mut IfStates {
&mut self.ifstates
}
#[must_use]
pub const fn definitions(&self) -> &Defines {
&self.definitions
}
pub fn definitions_mut(&mut self) -> &mut Defines {
&mut self.definitions
}
#[must_use]
pub const fn entry(&self) -> &String {
&self.entry
}
#[must_use]
pub const fn current_file(&self) -> &String {
&self.current_file
}
pub fn set_current_file(&mut self, file: String) {
self.current_file = file;
}
pub fn define(
&mut self,
ident: String,
source: Token,
definition: Definition,
) -> Result<(), Error> {
if BUILTIN.contains(&ident.as_str()) {
return Err(Error::ChangeBuiltin {
token: Box::new(source),
trace: self.trace(),
});
}
self.definitions.insert(ident, (source, definition));
Ok(())
}
pub fn undefine(
&mut self,
ident: &str,
source: &Token,
) -> Result<Option<(Token, Definition)>, Error> {
if BUILTIN.contains(&ident) {
return Err(Error::ChangeBuiltin {
token: Box::new(source.clone()),
trace: self.trace(),
});
}
Ok(self.definitions.remove(ident))
}
#[must_use]
pub fn has(&self, ident: &str) -> bool {
self.definitions.contains_key(ident)
}
#[must_use]
pub fn get(&self, ident: &str, token: &Token) -> Option<(Token, Definition)> {
match ident {
"__LINE__" => Some((
Token::builtin(Some(Box::new(token.clone()))),
Definition::Value(vec![Token::new(
Symbol::Word(token.source().start().1 .0.to_string()),
token.source().clone(),
Some(Box::new(token.clone())),
)]),
)),
"__FILE__" => Some((
Token::builtin(Some(Box::new(token.clone()))),
Definition::Value(vec![Token::new(
Symbol::Word(token.source().path().to_string().replace('\\', "/")),
token.source().clone(),
Some(Box::new(token.clone())),
)]),
)),
"__COUNTER__" => Some((
Token::builtin(Some(Box::new(token.clone()))),
Definition::Value(vec![Token::new(
Symbol::Word(
self.counter
.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
.to_string(),
),
token.source().clone(),
Some(Box::new(token.clone())),
)]),
)),
"__COUNTER_RESET__" => {
self.counter.store(0, std::sync::atomic::Ordering::SeqCst);
Some((
Token::builtin(Some(Box::new(token.clone()))),
Definition::Value(vec![Token::new(
Symbol::Void,
token.source().clone(),
Some(Box::new(token.clone())),
)]),
))
}
"__ARMA__" | "__ARMA3__" | "__HEMTT__" => Some((
Token::builtin(Some(Box::new(token.clone()))),
Definition::Value(vec![Token::new(
Symbol::Digit(1),
token.source().clone(),
Some(Box::new(token.clone())),
)]),
)),
_ => {
let mut context = self;
loop {
if let Some((source, definition)) = context.definitions.get(ident) {
return Some((source.clone(), definition.clone()));
}
if let Some(parent) = &context.parent {
context = parent;
} else {
break;
}
}
None
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Definition {
Function(FunctionDefinition),
Value(Vec<Token>),
Unit(Vec<Token>),
}
impl Definition {
#[must_use]
pub const fn is_function(&self) -> bool {
matches!(self, Self::Function(_))
}
#[must_use]
pub const fn is_value(&self) -> bool {
matches!(self, Self::Value(_))
}
#[must_use]
pub const fn is_unit(&self) -> bool {
matches!(self, Self::Unit(_))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FunctionDefinition {
parameters: Vec<Token>,
body: Vec<Token>,
}
impl FunctionDefinition {
#[must_use]
pub fn new(parameters: Vec<Token>, body: Vec<Token>) -> Self {
Self { parameters, body }
}
#[must_use]
pub fn parameters(&self) -> &[Token] {
&self.parameters
}
#[must_use]
pub fn body(&self) -> &[Token] {
&self.body
}
}