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;

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "number words (da corpus subset)".to_string(),
            pattern: vec![regex(
                r"(nul|én|en|ét|et|to|fjorten|seksten|sytten|atten|Atten)",
            )],
            production: Box::new(|nodes| {
                let s = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let v = match s.to_lowercase().as_str() {
                    "nul" => 0.0,
                    "én" | "en" | "ét" | "et" => 1.0,
                    "to" => 2.0,
                    "fjorten" => 14.0,
                    "seksten" => 16.0,
                    "sytten" => 17.0,
                    "atten" => 18.0,
                    _ => return None,
                };
                Some(TokenData::Numeral(NumeralData::new(v)))
            }),
        },
        Rule {
            name: "et par".to_string(),
            pattern: vec![regex("et par")],
            production: Box::new(|_| Some(TokenData::Numeral(NumeralData::new(2.0)))),
        },
        Rule {
            name: "fem tusinde".to_string(),
            pattern: vec![regex("(fem tusinde|fem Tusind|5 tusind)")],
            production: Box::new(|_| Some(TokenData::Numeral(NumeralData::new(5000.0)))),
        },
        Rule {
            name: "thousands separator".to_string(),
            pattern: vec![regex(r"(\d{1,3}(\.\d\d\d){1,5})")],
            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.replace('.', "").parse().ok()?,
                )))
            }),
        },
        Rule {
            name: "decimal number".to_string(),
            pattern: vec![regex(r"(\d*,\d+)")],
            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.replace(',', ".").parse().ok()?,
                )))
            }),
        },
        Rule {
            name: "kmg suffix".to_string(),
            pattern: vec![regex(r"(\d*\,?\d+|\,\d+)([kmgKMG])")],
            production: Box::new(|nodes| {
                let m = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m,
                    _ => return None,
                };
                let n = m.group(1)?;
                let x: f64 = if n.starts_with(',') {
                    format!("0{}", n).replace(',', ".").parse().ok()?
                } else {
                    n.replace(',', ".").parse().ok()?
                };
                let s = m.group(2)?.to_lowercase();
                let v = match s.as_str() {
                    "k" => x * 1e3,
                    "m" => x * 1e6,
                    "g" => x * 1e9,
                    _ => return None,
                };
                Some(TokenData::Numeral(NumeralData::new(v)))
            }),
        },
        Rule {
            name: "negative".to_string(),
            pattern: vec![regex(
                r"(-\s*|minus\s+|negativ\s+)(\d{1,3}(\.\d\d\d){1,5}|\d*\,?\d+|\,\d+)([kmgKMG])?",
            )],
            production: Box::new(|nodes| {
                let m = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m,
                    _ => return None,
                };
                let n = m.group(2)?;
                let mut x: f64 = if n.starts_with(',') {
                    format!("0{}", n)
                        .replace('.', "")
                        .replace(',', ".")
                        .parse()
                        .ok()?
                } else {
                    n.replace('.', "").replace(',', ".").parse().ok()?
                };
                if let Some(suf) = m.group(4) {
                    x = match suf.to_lowercase().as_str() {
                        "k" => x * 1e3,
                        "m" => x * 1e6,
                        "g" => x * 1e9,
                        _ => x,
                    };
                }
                Some(TokenData::Numeral(NumeralData::new(-x)))
            }),
        },
    ]
}