typr-core 0.5.1

Core type checking and transpilation logic for TypR - a typed superset of R
Documentation
use crate::components::error_message::help_data::HelpData;
use crate::components::language::Lang;
use crate::components::r#type::Type;
use crate::processes::parsing::operation_priority::TokenKind;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_until;
use nom::character::complete::char;
use nom::character::complete::multispace0;
use nom::combinator::recognize;
use nom::sequence::terminated;
use nom::IResult;
use nom::Parser;
use nom_locate::LocatedSpan;
use serde::{Deserialize, Serialize};

type Span<'a> = LocatedSpan<&'a str, String>;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Op {
    And(HelpData),
    And2(HelpData),
    Eq(HelpData),
    Eq2(HelpData),
    NotEq(HelpData),
    Add(HelpData),
    Add2(HelpData),
    Pipe(HelpData),
    Dot(HelpData),
    Pipe2(HelpData),
    Dot2(HelpData),
    Or(HelpData),
    Or2(HelpData),
    Minus(HelpData),
    Minus2(HelpData),
    Mul(HelpData),
    Mul2(HelpData),
    In(HelpData),
    At(HelpData),
    At2(HelpData),
    Div(HelpData),
    Div2(HelpData),
    LesserThan(HelpData),
    GreaterThan(HelpData),
    LesserOrEqual(HelpData),
    GreaterOrEqual(HelpData),
    Modulo(HelpData),
    Modulo2(HelpData),
    Dollar(HelpData),
    Dollar2(HelpData),
    Custom(String, HelpData),
    Empty(HelpData),
}

impl Op {
    pub fn to_type(&self) -> Option<Type> {
        match self {
            Op::In(h) => Some(Type::In(h.clone())),
            _ => None,
        }
    }

    pub fn get_token_type(&self) -> TokenKind {
        TokenKind::Operator
    }

    pub fn get_binding_power(&self) -> i32 {
        match self {
            Op::Dot(_)
            | Op::Dot2(_)
            | Op::Pipe(_)
            | Op::Pipe2(_)
            | Op::Dollar(_)
            | Op::Dollar2(_)
            | Op::In(_) => 4,
            Op::Mul(_)
            | Op::Mul2(_)
            | Op::Div(_)
            | Op::Div2(_)
            | Op::Modulo(_)
            | Op::Modulo2(_)
            | Op::At(_)
            | Op::At2(_) => 3,
            Op::Add(_) | Op::Add2(_) | Op::Minus(_) | Op::Minus2(_) => 2,
            _ => 1,
        }
    }

    pub fn combine(self, left: Lang, right: Lang) -> Lang {
        Lang::Operator {
            operator: self,
            rhs: Box::new(left.clone()),
            lhs: Box::new(right),
            help_data: left.get_help_data(),
        }
    }

    pub fn get_help_data(&self) -> HelpData {
        match self {
            Op::Empty(h) => h.clone(),
            Op::Custom(_, h) => h.clone(),
            Op::Dollar(h) => h.clone(),
            Op::Dollar2(h) => h.clone(),
            Op::Modulo2(h) => h.clone(),
            Op::Modulo(h) => h.clone(),
            Op::LesserOrEqual(h) => h.clone(),
            Op::GreaterOrEqual(h) => h.clone(),
            Op::Add(h) => h.clone(),
            Op::Add2(h) => h.clone(),
            Op::And(h) => h.clone(),
            Op::And2(h) => h.clone(),
            Op::Or(h) => h.clone(),
            Op::Or2(h) => h.clone(),
            Op::Eq(h) => h.clone(),
            Op::Eq2(h) => h.clone(),
            Op::NotEq(h) => h.clone(),
            Op::Pipe(h) => h.clone(),
            Op::Pipe2(h) => h.clone(),
            Op::Dot(h) => h.clone(),
            Op::Dot2(h) => h.clone(),
            Op::Minus(h) => h.clone(),
            Op::Minus2(h) => h.clone(),
            Op::Mul(h) => h.clone(),
            Op::Mul2(h) => h.clone(),
            Op::In(h) => h.clone(),
            Op::At(h) => h.clone(),
            Op::At2(h) => h.clone(),
            Op::Div(h) => h.clone(),
            Op::Div2(h) => h.clone(),
            Op::LesserThan(h) => h.clone(),
            Op::GreaterThan(h) => h.clone(),
        }
    }
}

fn bool_op(s: Span) -> IResult<Span, Span> {
    terminated(
        alt((
            tag("<="),
            tag(">="),
            tag("=="),
            tag("!="),
            tag("<"),
            tag(">"),
            tag("and"),
            tag("&&"),
            tag("&"),
            tag("or"),
            tag("||"),
            tag("|"),
            tag("="),
        )),
        multispace0,
    )
    .parse(s)
}

fn get_op(ls: LocatedSpan<&str, String>) -> Op {
    match ls.clone().into_fragment() {
        "+" => Op::Add(ls.into()),
        "++" => Op::Add2(ls.into()),
        "-" => Op::Minus(ls.into()),
        "--" => Op::Minus2(ls.into()),
        "*" => Op::Mul(ls.into()),
        "**" => Op::Mul2(ls.into()),
        "/" => Op::Div(ls.into()),
        "//" => Op::Div2(ls.into()),
        "@@" => Op::At2(ls.into()),
        "@" => Op::At(ls.into()),
        "%%" => Op::Modulo2(ls.into()),
        "%" => Op::Modulo(ls.into()),
        "|>" => Op::Pipe(ls.into()),
        "|>>" => Op::Pipe2(ls.into()),
        "::" => Op::Dollar(ls.into()),
        "=" => Op::Eq2(ls.into()),
        "." => Op::Dot(ls.into()),
        ".." => Op::Dot2(ls.into()),
        "$" => Op::Dollar(ls.into()),
        "$$" => Op::Dollar2(ls.into()),
        "==" => Op::Eq(ls.into()),
        "!=" => Op::NotEq(ls.into()),
        "<=" => Op::LesserOrEqual(ls.into()),
        ">=" => Op::GreaterOrEqual(ls.into()),
        "<" => Op::LesserThan(ls.into()),
        ">" => Op::GreaterThan(ls.into()),
        "in " => Op::In(ls.into()),
        "and" => Op::And2(ls.into()),
        "&&" => Op::And2(ls.into()),
        "&" => Op::And(ls.into()),
        "or" => Op::Or2(ls.into()),
        "||" => Op::Or2(ls.into()),
        "|" => Op::Or(ls.into()),
        n => Op::Custom(n.to_string(), ls.into()),
    }
}

pub fn custom_op(s: Span) -> IResult<Span, Span> {
    recognize((char('%'), take_until("%"), char('%'))).parse(s)
}

fn pipe_op(s: Span) -> IResult<Span, Span> {
    alt((
        tag("|>>"),
        tag("|>"),
        tag("::"),
        tag(".."),
        tag("."),
        tag("$$"),
        tag("$"),
    ))
    .parse(s)
}

pub fn op(s: Span) -> IResult<Span, Op> {
    let res = terminated(
        alt((
            custom_op,
            pipe_op,
            bool_op,
            tag("in "),
            tag("++"),
            tag("+"),
            tag("--"),
            tag("-"),
            tag("@@"),
            tag("@"),
            tag("**"),
            tag("*"),
            tag("//"),
            tag("/"),
            tag("%%"),
            tag("%"),
        )),
        multispace0,
    )
    .parse(s);
    match res {
        Ok((s, ls)) => Ok((s, get_op(ls))),
        Err(r) => Err(r),
    }
}

pub fn get_string(op: &Op) -> String {
    match op {
        Op::In(_) => "in".to_string(),
        Op::And(_) => "&".to_string(),
        Op::And2(_) => "&&".to_string(),
        Op::Or(_) => "|".to_string(),
        Op::Or2(_) => "||".to_string(),
        Op::Add(_) => "+".to_string(),
        Op::Add2(_) => "++".to_string(),
        Op::Minus(_) => "-".to_string(),
        Op::Minus2(_) => "--".to_string(),
        Op::Mul(_) => "*".to_string(),
        Op::Mul2(_) => "**".to_string(),
        Op::Div(_) => "/".to_string(),
        Op::Div2(_) => "//".to_string(),
        Op::At(_) => "@".to_string(),
        Op::At2(_) => "@@".to_string(),
        Op::Pipe(_) => "|>".to_string(),
        Op::Pipe2(_) => "|>>".to_string(),
        Op::Dot(_) => ".".to_string(),
        Op::Dot2(_) => "..".to_string(),
        Op::LesserThan(_) => "<".to_string(),
        Op::GreaterThan(_) => ">".to_string(),
        Op::LesserOrEqual(_) => "<=".to_string(),
        Op::GreaterOrEqual(_) => ">=".to_string(),
        Op::Modulo2(_) => "%%".to_string(),
        Op::Modulo(_) => "%".to_string(),
        Op::Dollar2(_) => "$$".to_string(),
        Op::Dollar(_) => "$".to_string(),
        Op::Eq(_) => "==".to_string(),
        Op::Eq2(_) => "=".to_string(),
        Op::NotEq(_) => "!=".to_string(),
        n => {
            dbg!(n);
            todo!()
        }
    }
}

use std::fmt;
impl fmt::Display for Op {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let res = get_string(self);
        write!(f, "{}", res)
    }
}