cluna 1.1.0

Tool to convert Lua code into Clue code Made by MarkosTh09
Documentation
use crate::lexer::Lexer;

#[derive(Debug)]
pub struct HexDecimal {
    before_decimal: String,
    after_decimal: String,
}

impl HexDecimal {
    #[inline]
    fn new(before_decimal: String, after_decimal: String) -> Self {
        HexDecimal {
            before_decimal,
            after_decimal,
        }
    }

    fn into_decimal(self) -> f64 {
        let mut num = 0.0f64;

        for (i, c) in (0i32..).zip(self.before_decimal.chars().rev()) {
            let digit = c.to_digit(16).unwrap() as f64;
            num += digit * 16f64.powi(i);
        }

        for (i, c) in (0i32..).zip(self.after_decimal.chars().skip(1)) {
            let digit = c.to_digit(16).unwrap() as f64;
            num += digit * 16f64.powi(-(i + 1));
        }

        num
    }
}

#[derive(Debug)]
pub struct Decimal {
    before_decimal: String,
    after_decimal: String,
}

#[inline]
fn normalize_before_decimal(before_decimal: String) -> String {
    if before_decimal.is_empty() {
        "0".to_string()
    } else {
        before_decimal
    }
}

#[inline]
fn normalize_after_decimal(after_decimal: String) -> String {
    if after_decimal == "." {
        ".0".to_string()
    } else {
        after_decimal
    }
}

impl Decimal {
    #[inline]
    fn new(before_decimal: String, after_decimal: String) -> Self {
        Decimal {
            before_decimal: normalize_before_decimal(before_decimal),
            after_decimal: normalize_after_decimal(after_decimal),
        }
    }
}

#[derive(Debug)]
pub enum Number {
    Hex(String),
    HexDecimal(HexDecimal),
    HexScientific {
        mantissa: HexDecimal,
        exponent: String,
    },
    Decimal(Decimal),
    Scientific {
        mantissa: Decimal,
        exponent: String,
    },
}

impl Number {
    pub fn from_source(lexer: &mut Lexer) -> Result<Number, String> {
        let start = lexer.current;
        let mut digit_encountered = false;
        let mut decimal_encountered = false;
        let mut is_scientific = false;
        let mut is_hex = false;
        let mut sign_encountered = false;

        let mut before_decimal = String::with_capacity(4);
        let mut after_decimal = String::with_capacity(4);
        let mut exponent = String::new();

        while let Some(c) = lexer.advance() {
            match c {
                '0'..='9' => {
                    digit_encountered = true;
                    if is_scientific {
                        exponent.push(c);
                    } else if decimal_encountered {
                        after_decimal.push(c);
                    } else {
                        before_decimal.push(c);
                    }
                }
                '.' => {
                    if decimal_encountered {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }

                    decimal_encountered = true;
                    if is_scientific {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }

                    if is_hex
                        && !digit_encountered
                        && !lexer.peek().map_or(false, |c| c.is_ascii_hexdigit())
                    {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }

                    if !is_hex
                        && !digit_encountered
                        && !lexer.peek().map_or(false, |c| c.is_ascii_digit())
                    {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }
                    after_decimal.push(c);
                }
                'x' | 'X' => {
                    if !lexer.look_back().map_or(false, |c| c == '0')
                        || is_scientific
                        || is_hex
                        || !lexer
                            .peek()
                            .map_or(false, |c| c.is_ascii_hexdigit() || c == '.')
                    {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }
                    is_hex = true;
                    digit_encountered = false;
                }
                'e' | 'E' => {
                    if !is_scientific && is_hex {
                        continue;
                    }

                    if !digit_encountered
                        || !lexer
                            .peek()
                            .map_or(false, |c| c.is_ascii_digit() || c == '+' || c == '-')
                        || is_scientific
                    {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    } else {
                        exponent.push(lexer.advance().unwrap());
                    }
                    is_scientific = true;
                }
                'p' | 'P' => {
                    if !is_hex || !digit_encountered || is_scientific {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }

                    if lexer
                        .peek()
                        .map_or(false, |c| c == '+' || c == '-' || c.is_ascii_digit())
                    {
                        exponent.push(lexer.advance().unwrap());
                    } else {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }
                    is_scientific = true;
                }
                'a'..='f' | 'A'..='F' => {
                    if !is_hex || is_scientific {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }
                    if decimal_encountered {
                        after_decimal.push(c);
                    } else {
                        before_decimal.push(c);
                    }
                    digit_encountered = true;
                }
                '+' | '-' => {
                    if !is_scientific {
                        lexer.go_back();
                        break;
                    }

                    if sign_encountered {
                        return Err(format!(
                            "Error: Malformed number at {}:{}",
                            lexer.line, lexer.column
                        ));
                    }
                    sign_encountered = true;
                    exponent.push(c);
                }
                _ => {
                    lexer.go_back();
                    break;
                }
            }
        }

        if is_scientific
            && !(lexer.source[lexer.current - 1].is_ascii_digit()
                || lexer.peek().map_or(false, |c| c.is_ascii_digit()))
        {
            return Err(format!(
                "Error: Malformed number at {}:{}",
                lexer.line, lexer.column
            ));
        }

        if !is_hex && !digit_encountered {
            return Err(format!(
                "Error: Malformed number at {}:{}",
                lexer.line, lexer.column
            ));
        }

        let len = lexer.current - start;
        let lexeme: String = lexer.source[lexer.current - len..lexer.current]
            .iter()
            .collect();

        let number = if is_scientific {
            if is_hex {
                let mantissa = HexDecimal::new(before_decimal, after_decimal);
                Number::HexScientific { mantissa, exponent }
            } else {
                let mantissa = Decimal::new(before_decimal, after_decimal);
                Number::Scientific { mantissa, exponent }
            }
        } else if is_hex {
            if decimal_encountered {
                let hex_decimal = HexDecimal::new(before_decimal[1..].to_string(), after_decimal);
                Number::HexDecimal(hex_decimal)
            } else {
                Number::Hex(lexeme)
            }
        } else if decimal_encountered {
            let decimal = Decimal::new(before_decimal, after_decimal);
            Number::Decimal(decimal)
        } else {
            Number::Decimal(Decimal::new(before_decimal, after_decimal))
        };
        Ok(number)
    }

    pub fn into_clue_number(self) -> String {
        match self {
            Number::Hex(hex) => hex,
            Number::HexDecimal(hex) => {
                let number = hex.into_decimal().to_string();
                if number == "inf" {
                    "1 / 0".to_owned()
                } else if number == "-inf" {
                    "-1 / 0".to_owned()
                } else {
                    number
                }
            }
            Number::HexScientific { mantissa, exponent } => {
                let number = mantissa.into_decimal();
                let number = number * 2f64.powi(exponent.parse::<i32>().unwrap());
                let number = number.to_string();

                if number == "inf" {
                    "1 / 0".to_owned()
                } else if number == "-inf" {
                    "-1 / 0".to_owned()
                } else {
                    number
                }
            }
            Number::Decimal(Decimal {
                before_decimal,
                after_decimal,
            }) => {
                let mut s = String::with_capacity(4);

                s.push_str(&before_decimal);
                s.push_str(&after_decimal);
                s
            }
            Number::Scientific { mantissa, exponent } => {
                let mut s = String::with_capacity(4);

                s.push_str(&mantissa.before_decimal);
                s.push_str(&mantissa.after_decimal);
                s.push('e');
                s.push_str(&exponent);
                s
            }
        }
    }
}