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::NumeralData;

fn fi_num(s: &str) -> Option<f64> {
    match s {
        "nolla" => Some(0.0),
        "yksi" => Some(1.0),
        "kaksi" => Some(2.0),
        "kolme" => Some(3.0),
        "neljä" => Some(4.0),
        "viisi" => Some(5.0),
        "kuusi" => Some(6.0),
        "seitsemän" => Some(7.0),
        "kahdeksan" => Some(8.0),
        "yhdeksän" => Some(9.0),
        "kymmenen" => Some(10.0),
        "yksitoista" => Some(11.0),
        "kaksitoista" => Some(12.0),
        "kolmetoista" => Some(13.0),
        "neljätoista" => Some(14.0),
        "viisitoista" => Some(15.0),
        "kuusitoista" => Some(16.0),
        "seitsemäntoista" => Some(17.0),
        "kahdeksantoista" => Some(18.0),
        "yhdeksäntoista" => Some(19.0),
        "kaksikymmentä" => Some(20.0),
        "kolmekymmentä" => Some(30.0),
        "neljäkymmentä" => Some(40.0),
        "viisikymmentä" => Some(50.0),
        "kuusikymmentä" => Some(60.0),
        "seitsemänkymmentä" => Some(70.0),
        "kahdeksankymmentä" => Some(80.0),
        "yhdeksänkymmentä" => Some(90.0),
        _ => None,
    }
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "integer (numeric)".to_string(),
            pattern: vec![regex(r"(\d{1,18})")],
            production: Box::new(|nodes| {
                let t = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                Some(TokenData::Numeral(NumeralData::new(t.parse().ok()?)))
            }),
        },
        Rule {
            name: "number (0..90)".to_string(),
            pattern: vec![regex(
                r"(nolla|yksi|kaksi|kolme|neljä|viisi|kuusi|seitsemän|kahdeksan|yhdeksän|kymmenen|yksitoista|kaksitoista|kolmetoista|neljätoista|viisitoista|kuusitoista|seitsemäntoista|kahdeksantoista|yhdeksäntoista|kaksikymmentä|kolmekymmentä|neljäkymmentä|viisikymmentä|kuusikymmentä|seitsemänkymmentä|kahdeksankymmentä|yhdeksänkymmentä)",
            )],
            production: Box::new(|nodes| {
                let s = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?.to_lowercase(),
                    _ => return None,
                };
                Some(TokenData::Numeral(NumeralData::new(fi_num(&s)?)))
            }),
        },
        Rule {
            name: "composite tens".to_string(),
            pattern: vec![regex(
                r"(kaksikymmentä|kolmekymmentä|neljäkymmentä|viisikymmentä|kuusikymmentä|seitsemänkymmentä|kahdeksankymmentä|yhdeksänkymmentä)(yksi|kaksi|kolme|neljä|viisi|kuusi|seitsemän|kahdeksan|yhdeksän)",
            )],
            production: Box::new(|nodes| {
                let m = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m,
                    _ => return None,
                };
                let t = m.group(1)?.to_lowercase();
                let u = m.group(2)?.to_lowercase();
                Some(TokenData::Numeral(NumeralData::new(
                    fi_num(&t)? + fi_num(&u)?,
                )))
            }),
        },
    ]
}