use crate::{
error::{self, arg_error},
parser::rules::{LiteralType, OtherStuff},
};
use hexponent::FloatLiteral;
use std::{
env::consts::OS,
fmt::{self, Debug, Display},
io::{self, Write},
process::{exit, Command},
};
#[derive(PartialEq, Debug, Clone)]
#[allow(clippy::module_name_repetitions)]
pub enum TokenType {
RightParen,
LeftParen,
GreaterThanSymbol,
LessThanSymbol,
Dot,
Bang,
RightBracket,
LeftBracket,
RightBrace,
LeftBrace,
CodeBlockBegin,
CodeBlockEnd,
Plus,
Minus,
Divide,
Multiply,
Equal,
NotEqual,
GreaterEqual,
LessEqual,
GreaterThan,
LessThan,
And,
Or,
Not,
Identifier { name: String },
FunctionIdentifier { name: char },
String { literal: String },
Number { literal: f64 },
Create,
With,
Set,
AddWith,
SubtractWith,
DivideWith,
MultiplyWith,
List,
Car,
Cdr,
Return { value: Option<Box<OtherStuff>> },
Colon,
Break,
Continue,
Loop,
Potato,
If,
Else,
Hempty,
Boolean { value: bool },
Input,
New,
Function,
FunctionArgument { name: String },
StrToNum,
StrToBool,
StrToHempty,
RunCommand,
Open,
Close,
Write,
Read,
ReadLine,
Exit,
Error,
EOF,
Program,
Delete,
SplitOn,
WriteLine,
CreateFile,
DeleteFile,
Type,
}
impl TokenType {
#[allow(clippy::too_many_lines)]
pub fn r#do(&self, args: &[LiteralType], line: i32) -> LiteralType {
if crate::KEYWORDS.is_keyword(self) {
match self {
Self::Not => {
if args.len() != 1 {
error::error(line, "Expected 1 argument for not operator");
}
match &args[0] {
LiteralType::Boolean(b) => LiteralType::Boolean(!b),
_ => error::error(line, "Expected boolean for not operator"),
}
}
Self::Plus | Self::Minus | Self::Divide | Self::Multiply => {
match &args[0] {
LiteralType::Number(number) => {
// check if minus and only one argument
let mut total: f64 = if self == &Self::Minus && args.len() == 1 {
-number
} else {
*number
};
for thing in args.iter().skip(1) {
if let LiteralType::Number(number) = thing {
{
// convert the self to an operator
match self {
Self::Plus => {
total += number;
}
Self::Minus => {
total -= number;
}
Self::Divide => {
total /= number;
}
Self::Multiply => {
total *= number;
}
_ => {}
};
}
}
}
LiteralType::Number(total)
}
LiteralType::String(string) => {
let mut new_string = string.to_string();
for (index, thing) in args.iter().skip(1).enumerate() {
match self {
Self::Plus => {
match thing {
LiteralType::String(ref string) => {
new_string.push_str(string);
}
LiteralType::Number(number) => {
new_string.push_str(&number.to_string());
}
LiteralType::Boolean(boolean) => {
new_string.push_str(&boolean.to_string());
}
LiteralType::Hempty => {
new_string.push_str("HEMPTY");
}
};
}
Self::Multiply => {
if index > 0 {
error::error(
line,
"Multiply can only be used with the car argument",
);
}
match thing {
LiteralType::Number(number) => {
let mut new_new_string = String::new();
for _ in 0..*number as i32 - 1 {
new_new_string.push_str(&new_string);
}
new_string = new_new_string;
}
_ => {
error::error(
line,
"strings can only be multiplied by numbers",
);
}
}
}
Self::Divide | Self::Minus => {
error::error(
line,
"Only numbers can be divided or subtracted found",
);
}
_ => {}
};
}
LiteralType::String(new_string)
}
_ => error::error(line, "Invalid literal arguments"),
}
}
Self::Error
| Self::Input
| Self::StrToBool
| Self::StrToHempty
| Self::StrToNum
| Self::RunCommand => {
arg_error(1, args.len() as u32, self, false, line);
match &args[0] {
LiteralType::String(ref string) => match self {
Self::Error => exit(1),
Self::Input => {
let mut input = String::new();
print!("{string}");
// flush stdout
io::stdout().flush().unwrap_or_else(|_| {
error::error(line, "Error flushing stdout");
});
io::stdin().read_line(&mut input).unwrap_or_else(|_| {
error::error(line, "Failed to read input");
});
LiteralType::String(input.trim().to_string())
}
Self::StrToBool => {
if string == "true" {
LiteralType::Boolean(true)
} else if string == "false" {
LiteralType::Boolean(false)
} else {
error::error(line, "Expected true or false");
}
}
Self::StrToHempty => {
if string == "HEMPTY" {
LiteralType::Hempty
} else {
error::error(line, "Expected HEMPTY");
}
}
Self::StrToNum => {
let string = match string {
strings if string.starts_with("0x") => {
strings.clone().trim().to_owned()
}
strings => format!("0x{}", strings.trim()),
};
let number: FloatLiteral = string.parse().map_or_else(
|_| {
error::error(
line,
format!(
"Error parsing string {} to number",
string.trim()
),
)
},
|value: FloatLiteral| value,
);
LiteralType::Number(number.convert::<f64>().inner())
}
Self::RunCommand => {
let cmd = if OS == "windows" {
let mut cmd = Command::new("powershell");
cmd.args(["-c", string.trim()]).output()
} else {
let mut cmd = Command::new("sh");
cmd.args(["-c", string.trim()]).output()
};
let cmd: String = match cmd {
Ok(value) => {
if value.status.success() {
String::from_utf8_lossy(&value.stdout).into()
} else {
String::from_utf8_lossy(&value.stderr).into()
}
}
Err(_) => error::error(
line,
format!("Error running command {}", string.trim()),
),
};
LiteralType::String(cmd)
}
_ => {
error::error(line, "command not found");
}
},
_ => error::error(line, "Expected string for input operator"),
}
}
Self::NotEqual | Self::Equal => {
if args.len() != 2 {
error::error(line, format!("Expected 2 arguments for {self:?} operator"));
}
let type_ = &args[0];
let type_1 = &args[1];
if type_.type_eq(type_1) {
} else {
error::error(
line,
format!(
"{} and {} are not the same type which is required for {} operator",
type_, type_1, self
),
);
}
if self == &Self::Equal {
LiteralType::Boolean(type_ == type_1)
} else {
LiteralType::Boolean(!(type_ == type_1))
}
}
Self::Or | Self::And => {
if args.len() != 2 {
error::error(line, format!("Expected 2 arguments for {self:?} operator"));
}
let bool_1 = match &args[0] {
LiteralType::Boolean(boolean) => boolean,
_ => error::error(line, format!("Expected boolean for {self:?} operator")),
};
let bool_2 = match &args[1] {
LiteralType::Boolean(boolean) => boolean,
_ => error::error(line, format!("Expected boolean for {self:?} operator")),
};
if bool_1 == bool_2 {
if bool_1 == &true {
LiteralType::Boolean(true)
} else {
LiteralType::Boolean(false)
}
} else {
LiteralType::Boolean(false)
}
}
Self::GreaterThan | Self::LessThan | Self::GreaterEqual | Self::LessEqual => {
if args.len() != 2 {
error::error(line, format!("Expected 2 arguments for {self:?} operator"));
}
let type_ = match &args[0] {
LiteralType::Number(number) => number,
_ => error::error(line, format!("Expected number for {self:?} operator")),
};
let type_1 = match &args[1] {
LiteralType::Number(number) => number,
_ => error::error(line, format!("Expected number for {self:?} operator")),
};
if self == &Self::GreaterThan {
LiteralType::Boolean(type_ > type_1)
} else if self == &Self::LessThan {
LiteralType::Boolean(type_ < type_1)
} else if self == &Self::GreaterEqual {
LiteralType::Boolean(type_ >= type_1)
} else {
LiteralType::Boolean(type_ <= type_1)
}
}
Self::Exit => {
if args.len() == 1 {
match &args[0] {
LiteralType::Number(number) => exit(*number as i32),
_ => {
error::error(line, format!("Expected number for {self:?} operator"))
}
}
} else {
error::error(line, format!("Expected 1 argument for {self:?} operator"));
}
}
Self::SplitOn => {
if args.len() < 2 {
error::error(
line,
format!("Expected al least 2 arguments for {self:?} operator"),
);
}
let og_string = match &args[0] {
LiteralType::String(string) => string,
_ => error::error(line, format!("Expected string for {self:?} operator")),
};
let split_on = match &args[1] {
LiteralType::String(string) => string,
_ => error::error(line, format!("Expected string for {self:?} operator")),
};
// check if there is a third argument (number)
args.get(2).map_or_else(
|| {
og_string.split_once(split_on).map_or_else(
|| LiteralType::String(og_string.to_string()),
|v| LiteralType::String(v.0.to_string()),
)
},
|number| -> LiteralType {
if let LiteralType::Number(number) = number {
let number = *number as usize;
// return the string until the nth time split_on is found
let string: Vec<&str> =
og_string.split_inclusive(split_on).collect::<Vec<&str>>();
if number > string.len() {
error::error(
line,
format!("{number} is greater than the number of splits"),
);
}
// loop through the splits and add them to the string if they are less than the number
let mut ret_string = String::new();
string.iter().take(number).for_each(|i: &&str| {
ret_string.push_str(i);
});
let ret_string = ret_string
.rsplit_once(split_on)
.map_or(og_string.to_string(), |string| string.0.to_string());
LiteralType::String(ret_string)
} else {
error::error(line, format!("Expected number for {self:?} operator"))
}
},
)
}
keyword if crate::KEYWORDS.is_keyword(keyword) => {
error::error(line, format!("Keyword not found {self}"));
}
_ => {
error::error(line, format!("Keyword not found {self}"));
}
}
} else {
error::error(line, "Unknown keyword");
}
}
}
impl Display for TokenType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TokenType {self:?}",)
}
}
#[derive(PartialEq, Clone)]
pub struct Token {
pub token_type: TokenType,
pub lexeme: String,
pub line: i32,
}
impl Token {
pub fn new(token_type: TokenType, lexeme: &str, line: i32) -> Self {
Self {
token_type,
lexeme: lexeme.to_string(),
line,
}
}
}
impl Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.token_type {
TokenType::String { literal } => {
write!(f, "String: [lexeme {:?}, value {literal:?}]", self.lexeme)
}
TokenType::Number { literal } => {
write!(f, "Number: [lexeme {:?}, value {literal:?}]", self.lexeme)
}
_ => write!(f, "{:?}: [lexeme {:?}]", self.token_type, self.lexeme),
}
}
}
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self} at {}", self.line)
}
}