finitio 0.1.0

Finitio data language
Documentation
use super::any::parse_any;
use super::builtin::parse_builtin;
use super::r#ref::parse_ref;
use super::r#struct::parse_struct;
use super::relation::parse_relation;
use super::seq::parse_seq;
use super::set::parse_set;
use super::tuple::parse_tuple;
use crate::fio::r#type::parse_subtypeable;

use super::{Type};
use crate::common::FilePosition;
use crate::fio::common::Span;
use nom::branch::alt;
use nom::character::complete::alphanumeric1;
use nom::combinator::{peek};
use nom::sequence::{preceded, separated_pair, terminated};
use nom::{bytes::complete::tag, combinator::map, IResult};
use serde::{Serialize, Deserialize};
use super::common::{ws, take_parenth_content};


#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct Constraint {
    pub param: String,
    pub expr: String,
    pub position: FilePosition,
}

#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct SubType {
    pub base: Box<Type>,
    pub constraints: Vec<Constraint>,
    pub position: FilePosition,
}

pub fn parse_anonymous_constraint(input: Span) -> IResult<Span, Constraint> {
    let constraint = take_parenth_content('(', ')');

    let (rest, parsed) = match constraint(input) {
        Ok(c) => c,
        Err(err) => {
            return Err(err)
        }
    };

    let mut param = preceded(ws, terminated(alphanumeric1, preceded(ws, tag("|"))));
    match param(parsed) {
        Ok((expr, param)) => {
            let c = Constraint {
                param: param.to_string(),
                expr: expr.to_string(),
                position: parsed.into(),
            };
            Ok((rest, c))
        }
        Err(err) => return Err(err)
    }
}

pub fn check_looks_like_sub(input: Span) -> IResult<Span, bool> {
    // First we ensure that it looks like a subtype
    let types = alt((
        map(parse_builtin, |_| {}),
        map(parse_ref, |_| {}),
        map(parse_seq, |_| {}),
        map(parse_set, |_| {}),
        map(parse_struct, |_| {}),
        map(parse_tuple, |_| {}),
        map(parse_relation, |_| {}),
        map(parse_any, |_| {}),
    ));
    let check = separated_pair(types, ws, tag("("));
    match peek(check)(input) {
        Ok(_s) => Ok((input, true)),
        Err(err) => Err(err),
    }
}

pub fn parse_sub(input: Span) -> IResult<Span, SubType> {
    // First we ensure that it looks like a subtype
    match peek(check_looks_like_sub)(input) {
        Err(err) => Err(err),
        Ok(_) => {
            map(
                separated_pair(parse_subtypeable, ws, parse_anonymous_constraint),
                |(ftype, constraint)| SubType {
                    base: Box::new(ftype),
                    constraints: vec![constraint],
                    position: input.into(),
                },
            )(input)
        }
    }
}

#[cfg(test)]
use super::{BuiltinType, SeqType};
#[cfg(test)]
use crate::fio::common::assert_parse;

#[test]
fn test_parse_anonymous_constraint() {
    assert_parse(
        parse_anonymous_constraint(Span::new("(s | some anonymous constraint)")),
        Constraint {
            param: "s".to_string(),
            expr: " some anonymous constraint".to_string(),
            position: FilePosition { line: 1, column: 2 },
        },
    );
}

#[test]
fn test_check_looks_like_sub() {
    let output = check_looks_like_sub(Span::new("Number(s | foo bar baz)"));
    let output = output.unwrap();
    assert_eq!(output.0.fragment(), &"Number(s | foo bar baz)");
    assert_eq!(output.1, true);
}

#[test]
fn test_parse_sub_type_builtin() {
    assert_parse(
        parse_sub(Span::new(".Number(s | some anonymous constraint)")),
        SubType {
            position: FilePosition { line: 1, column: 1 },
            base: Box::new(Type::BuiltinType(BuiltinType {
                name: "Number".to_string(),
                position: FilePosition { line: 1, column: 1 },
            })),
            constraints: vec![Constraint {
                param: "s".to_string(),
                expr: " some anonymous constraint".to_string(),
                position: FilePosition { line: 1, column: 9 },
            }],
        },
    );
}

#[test]
fn test_parse_sub_type_seq() {
    assert_parse(
        parse_sub(Span::new("[.Number](s | some anonymous constraint)")),
        SubType {
            position: FilePosition { line: 1, column: 1 },
            base: Box::new(Type::SeqType(SeqType {
                elm_type: Box::new(Type::BuiltinType(BuiltinType {
                    name: "Number".to_string(),
                    position: FilePosition { line: 1, column: 2 },
                })),
                position: FilePosition { line: 1, column: 1 },
            })),
            constraints: vec![Constraint {
                param: "s".to_string(),
                expr: " some anonymous constraint".to_string(),
                position: FilePosition {
                    line: 1,
                    column: 11,
                },
            }],
        },
    );
}

#[test]
fn test_parse_sub_type_spacing() {
    assert_parse(
        parse_sub(Span::new(
            "[ .Number ] (   s  |   \nsome anonymous constraint)",
        )),
        SubType {
            position: FilePosition { line: 1, column: 1 },
            base: Box::new(Type::SeqType(SeqType {
                elm_type: Box::new(Type::BuiltinType(BuiltinType {
                    name: "Number".to_string(),
                    position: FilePosition { line: 1, column: 3 },
                })),
                position: FilePosition { line: 1, column: 1 },
            })),
            constraints: vec![Constraint {
                param: "s".to_string(),
                expr: "   \nsome anonymous constraint".to_string(),
                position: FilePosition {
                    line: 1,
                    column: 14,
                },
            }],
        },
    );
}


#[test]
fn test_parse_sub_type_functions_in_expressions() {
    assert_parse(
        parse_sub(Span::new(
            ".String(s | len(s) > 8)",
        )),
        SubType {
            position: FilePosition { line: 1, column: 1 },
            base: Box::new(Type::BuiltinType(BuiltinType {
                name: "String".to_string(),
                position: FilePosition { line: 1, column: 1 },
            })),
            constraints: vec![Constraint {
                param: "s".to_string(),
                expr: " len(s) > 8".to_string(),
                position: FilePosition { line: 1, column: 9 },
            }],
        },
    );
}