duckling 0.4.0

A Rust port of Facebook's Duckling library for parsing natural language into structured data
Documentation
use std::collections::HashMap;

use crate::pattern::regex;
use crate::types::{Rule, TokenData};

use super::OrdinalData;

fn one_to_nineteen_map() -> HashMap<&'static str, i64> {
    HashMap::from([
        ("ഒന്നാം", 1),
        ("രണ്ടാം", 2),
        ("മൂന്നാം", 3),
        ("നാലാം", 4),
        ("അഞ്ചാം", 5),
        ("ആറാം", 6),
        ("ഏഴാം", 7),
        ("എട്ടാം", 8),
        ("ഒമ്പതാം", 9),
        ("പത്താം", 10),
        ("പതിനൊന്നാം", 11),
        ("പന്ത്രണ്ടാം", 12),
        ("പതിമൂന്നാം", 13),
        ("പതിനാലാം", 14),
        ("പതിനഞ്ചാം", 15),
        ("പതിനാറാം", 16),
        ("പതിനേഴാം", 17),
        ("പതിനെട്ടാം", 18),
        ("പത്തൊൻപതാം", 19),
    ])
}

fn tens_map() -> HashMap<&'static str, i64> {
    HashMap::from([
        ("ഇരുപതാം", 20),
        ("മുപ്പത്തഞ്ചാം", 30),
        ("നാല്പതാം", 40),
        ("അമ്പതാം", 50),
        ("അറുപതാം", 60),
        ("എഴുപത്താം", 70),
        ("എൺപത്താം", 80),
        ("തൊണ്ണൂറാം", 90),
    ])
}

fn tens_ordinal_map() -> HashMap<&'static str, i64> {
    HashMap::from([
        ("ഇരുപത്തി", 20),
        ("മുപ്പത്തി", 30),
        ("നാല്പത്തി", 40),
        ("അമ്പത്തി", 50),
        ("അറുപത്തി", 60),
        ("എഴുപത്തി", 70),
        ("എൺപത്തി", 80),
        ("തൊണ്ണൂറ്റി", 90),
    ])
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "ordinal (digits)".to_string(),
            pattern: vec![regex("0*(\\d+)\\.")],
            production: Box::new(|nodes| {
                let value: i64 = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?.parse().ok()?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(value)))
            }),
        },
        Rule {
            name: "integer (1..19)".to_string(),
            pattern: vec![regex("(ഒന്നാം|രണ്ടാം|മൂന്നാം|നാലാം|അഞ്ചാം|ആറാം|ഏഴാം|എട്ടാം|ഒമ്പതാം|പത്താം|പതിനൊന്നാം|പന്ത്രണ്ടാം|പതിമൂന്നാം|പതിനാലാം|പതിനഞ്ചാം|പതിനാറാം|പതിനേഴാം|പതിനെട്ടാം|പത്തൊൻപതാം)")],
            production: Box::new(|nodes| {
                let text = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let map = one_to_nineteen_map();
                Some(TokenData::Ordinal(OrdinalData::new(*map.get(text)?)))
            }),
        },
        Rule {
            name: "integer (20..90)".to_string(),
            pattern: vec![regex("(ഇരുപതാം|മുപ്പത്തഞ്ചാം|നാല്പതാം|അമ്പതാം|അറുപതാം|എഴുപത്താം|എൺപത്താം|തൊണ്ണൂറാം)")],
            production: Box::new(|nodes| {
                let text = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let map = tens_map();
                Some(TokenData::Ordinal(OrdinalData::new(*map.get(text)?)))
            }),
        },
        Rule {
            name: "integer ([2-9][1-9])".to_string(),
            pattern: vec![regex("(ഇരുപത്തി|മുപ്പത്തി|നാല്പത്തി|അമ്പത്തി|അറുപത്തി|എഴുപത്തി|എൺപത്തി|തൊണ്ണൂറ്റി)(ആദ്യം|രണ്ടാം|മൂന്നാം|നാലാം|അഞ്ചാം|ആറാം|ഏഴാം|എട്ടാം|ഒമ്പത)")],
            production: Box::new(|nodes| {
                let m1 = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let m2 = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(2)?,
                    _ => return None,
                };
                let tens = tens_ordinal_map();
                let units = one_to_nineteen_map();
                Some(TokenData::Ordinal(OrdinalData::new(
                    tens.get(m1)?.checked_add(*units.get(m2)?)?,
                )))
            }),
        },
    ]
}