duckling 0.4.0

A Rust port of Facebook's Duckling library for parsing natural language into structured data
Documentation
use crate::pattern::regex;
use crate::types::{Rule, TokenData};

use super::OrdinalData;

fn ordinals_map(s: &str) -> Option<i64> {
    match s {
        "اول" | "أول" | "حاد" | "حادي" | "واحد" => Some(1),
        "ثان" | "ثاني" => Some(2),
        "ثالث" => Some(3),
        "رابع" => Some(4),
        "خامس" => Some(5),
        "سادس" => Some(6),
        "سابع" => Some(7),
        "ثامن" => Some(8),
        "تاسع" => Some(9),
        "عاشر" => Some(10),
        _ => None,
    }
}

fn cardinals_map(s: &str) -> Option<i64> {
    match s {
        "عشر" => Some(20),
        "ثلاث" => Some(30),
        "اربع" => Some(40),
        "خمس" => Some(50),
        "ست" => Some(60),
        "سبع" => Some(70),
        "ثمان" => Some(80),
        "تسع" => Some(90),
        _ => None,
    }
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "ordinals (composite, e.g., eighty-seven)".to_string(),
            pattern: vec![regex("ال(واحد|حادي?|ثاني?|ثالث|رابع|خامس|سادس|سابع|ثامن|تاسع|عاشر) و ?ال(عشر|ثلاث|اربع|خمس|ست|سبع|ثمان|تسع)(ون|ين)")],
            production: Box::new(|nodes| {
                let unit = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let tens = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(2)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(
                    ordinals_map(unit)?.checked_add(cardinals_map(tens)?)?,
                )))
            }),
        },
        Rule {
            name: "ordinals (first..tenth)".to_string(),
            pattern: vec![regex("(?:ال)?([أا]ول|ثاني?|ثالث|رابع|خامس|سادس|سابع|ثامن|تاسع|عاشر)[ةهى]?")],
            production: Box::new(|nodes| {
                let value = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => ordinals_map(m.group(1)?)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(value)))
            }),
        },
        Rule {
            name: "ordinals (eleventh)".to_string(),
            pattern: vec![regex("ال([اأإ]حد[يى]?|حاد(ي[ةه]?)?) ?عشر[ةه]?")],
            production: Box::new(|_| Some(TokenData::Ordinal(OrdinalData::new(11)))),
        },
        Rule {
            name: "ordinals (twelveth)".to_string(),
            pattern: vec![regex("ال([اأإ]ثن[يى]?|ثان(ي[ةه]?)?) ?عشر[ةه]?")],
            production: Box::new(|_| Some(TokenData::Ordinal(OrdinalData::new(12)))),
        },
        Rule {
            name: "ordinals (thirtieth..nineteenth)".to_string(),
            pattern: vec![regex("ال(ثالث|رابع|خامس|سادس|سابع|ثامن|تاسع)[ةه]? ?عشرة?")],
            production: Box::new(|nodes| {
                let unit = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(10_i64.checked_add(ordinals_map(unit)?)?)))
            }),
        },
        Rule {
            name: "ordinals (twenty, thirty..ninety)".to_string(),
            pattern: vec![regex("ال(عشر|ثلاث|اربع|خمس|ست|سبع|ثمان|تسع)(ون|ين)")],
            production: Box::new(|nodes| {
                let tens = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(cardinals_map(tens)?)))
            }),
        },
    ]
}