nodespeak 0.2.1

A JIT-ish compiler for number-crunching applications.
Documentation
extern crate pest;

use pest::iterators::Pair;
use pest::Parser;

use crate::trivial::structure::KnownData;
use crate::vague::structure::KnownData as VagueKnownData;

#[derive(Parser)]
#[grammar = "util/known_data.pest"]
struct KnownDataParser;

fn parse_literal(source: Pair<Rule>) -> KnownData {
    match source.as_rule() {
        Rule::neg_float => KnownData::Float(
            -parse_literal(source.into_inner().next().expect("Required by grammar."))
                .require_float(),
        ),
        Rule::neg_int => KnownData::Int(
            -parse_literal(source.into_inner().next().expect("Required by grammar.")).require_int(),
        ),
        Rule::float => KnownData::Float(
            source
                .as_str()
                .replace("_", "")
                .parse()
                .expect("Valid float required by grammar."),
        ),
        Rule::dec_int => KnownData::Int(
            source
                .as_str()
                .replace("_", "")
                .parse()
                .expect("Valid int required by grammar."),
        ),
        Rule::hex_int => KnownData::Int(
            i64::from_str_radix(&source.as_str().replace("_", "")[2..], 16)
                .expect("Grammar requires valid hexadecimal int."),
        ),
        Rule::oct_int => KnownData::Int(
            i64::from_str_radix(&source.as_str().replace("_", "")[2..], 8)
                .expect("Grammar requires valid octal int."),
        ),
        Rule::legacy_oct_int => KnownData::Int(
            i64::from_str_radix(&source.as_str().replace("_", "")[1..], 8)
                .expect("Grammar requires valid octal int."),
        ),
        Rule::bin_int => KnownData::Int(
            i64::from_str_radix(&source.as_str().replace("_", "")[2..], 2)
                .expect("Grammar requires valid binary int."),
        ),
        Rule::bool_true => KnownData::Bool(true),
        Rule::bool_false => KnownData::Bool(false),
        Rule::array_literal => {
            let children: Vec<_> = source
                .into_inner()
                .map(|child| parse_literal(child))
                .collect();
            if children.len() == 0 {
                panic!("TODO: Nice error, an array cannot have no children.");
            } else {
                KnownData::Array(children)
            }
        }
        _ => {
            eprintln!("{}", source);
            unreachable!("No other possible children in grammar.")
        }
    }
}

fn convert_native_to_vague(data: &KnownData) -> VagueKnownData {
    match data {
        KnownData::Bool(value) => VagueKnownData::Bool(*value),
        KnownData::Int(value) => VagueKnownData::Int(*value),
        KnownData::Float(value) => VagueKnownData::Float(*value),
        KnownData::Array(values) => {
            let new_values = values.iter().map(convert_native_to_vague).collect();
            VagueKnownData::Array(new_values)
        }
    }
}

pub fn parse_known_data(source: &str) -> Result<VagueKnownData, String> {
    let mut parse_result = match KnownDataParser::parse(Rule::root, source) {
        Result::Ok(result) => result,
        Result::Err(err) => {
            return Result::Err(format!("{}", err));
        }
    };
    let literal = parse_result
        .next()
        .expect("Grammar requires a result.")
        .into_inner()
        .next()
        .expect("Grammar requires a literal.");
    let native = parse_literal(literal);
    Result::Ok(convert_native_to_vague(&native))
}

pub fn parse_native_data(source: &str) -> Result<KnownData, String> {
    let mut parse_result = match KnownDataParser::parse(Rule::root, source) {
        Result::Ok(result) => result,
        Result::Err(err) => {
            return Result::Err(format!("{}", err));
        }
    };
    let literal = parse_result
        .next()
        .expect("Grammar requires a result.")
        .into_inner()
        .next()
        .expect("Grammar requires a literal.");
    Result::Ok(parse_literal(literal))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn int_literal() -> Result<(), String> {
        parse_known_data("40189")?;
        parse_known_data("94901")?;
        parse_known_data("-110298")?;
        parse_known_data("901489")?;
        parse_known_data("   909814  ")?;
        Result::Ok(())
    }

    #[test]
    fn float_literal() -> Result<(), String> {
        parse_known_data("4180.")?;
        parse_known_data(".9901824")?;
        parse_known_data("-1901824.490182e-42")?;
        parse_known_data("248e69")?;
        parse_known_data("   -0.94981  ")?;
        Result::Ok(())
    }
}