weresocool_parser 1.0.47

Parser for WereSoCool
Documentation
lalrpop_mod!(pub socool);
use crate::error_handling::handle_parse_error;
use crate::imports::{get_filepath_and_import_name, is_import};
use colored::*;
use num_rational::Rational64;
use path_clean::PathClean;
use scop::Defs;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use weresocool_ast::{NormalForm, Normalize, Op, Term};
use weresocool_error::{Error, ParseError};

#[derive(Clone, PartialEq, Debug)]
pub struct Init {
    pub f: Rational64,
    pub l: Rational64,
    pub g: Rational64,
    pub p: Rational64,
}

#[derive(Clone, PartialEq, Debug)]
pub struct ParsedComposition {
    pub init: Init,
    pub defs: Defs<Term>,
}

fn process_op_table(mut defs: &mut Defs<Term>) -> Result<Defs<Term>, Error> {
    let mut result: Defs<Term> = Defs::default();

    for (scope_name, scope) in defs.clone().iter_mut() {
        for (name, term) in scope {
            match term {
                Term::Nf(nf) => {
                    result.insert(scope_name, name, Term::Nf(nf.to_owned()));
                }
                Term::Op(op) => {
                    let mut nf = NormalForm::init();
                    op.apply_to_normal_form(&mut nf, &mut defs)?;

                    result.insert(scope_name, name, Term::Nf(nf));
                }
                Term::FunDef(fun) => {
                    result.insert(scope_name, name, Term::FunDef(fun.to_owned()));
                }
                Term::Lop(lop) => {
                    let mut nf = NormalForm::init();
                    lop.apply_to_normal_form(&mut nf, &mut defs.clone())?;
                    result.insert(scope_name, name, Term::Nf(nf));
                }
                Term::Gen(gen) => {
                    let mut nf = NormalForm::init();
                    gen.apply_to_normal_form(&mut nf, &mut defs.clone())?;

                    result.insert(scope_name, name, Term::Nf(nf));
                }
            };
        }
    }

    result.stems = defs.stems.to_owned();

    Ok(result)
}
pub fn read_file(filename: &str) -> Result<File, Error> {
    let f = File::open(filename);
    match f {
        Ok(f) => return Ok(f),
        _ => {
            println!(
                "{} {}\n",
                "\n        File not found:".red().bold(),
                filename.red().bold()
            );

            return Err(Error::with_msg(format!("File not found: {}", filename)));
        }
    };
}

pub fn filename_to_vec_string(filename: &str) -> Result<Vec<String>, Error> {
    let file = read_file(filename)?;
    let reader = BufReader::new(&file);
    Ok(reader
        .lines()
        .map(|line| line)
        .collect::<Result<Vec<_>, _>>()?)
}

pub fn language_to_vec_string(language: &str) -> Vec<String> {
    language.split('\n').map(|l| l.to_string()).collect()
}

pub fn parse_file(
    vec_string: Vec<String>,
    prev_defs: Option<Defs<Term>>,
    working_path: Option<PathBuf>,
) -> Result<ParsedComposition, Error> {
    let mut defs: Defs<Term> = if let Some(defs) = prev_defs {
        defs
    } else {
        Default::default()
    };

    let (imports_needed, composition) = handle_whitespace_and_imports(vec_string)?;
    for import in imports_needed {
        let (mut filepath, import_name) = get_filepath_and_import_name(import);
        if let Some(mut wd) = working_path.clone() {
            wd.push(filepath);
            filepath = wd.clean().display().to_string();
        }
        // dbg!(&filepath);
        let vec_string = filename_to_vec_string(&filepath.to_string())?;
        let parsed_composition = parse_file(vec_string, Some(defs.clone()), working_path.clone())?;

        for (scope_name, scope) in parsed_composition.defs.iter() {
            for (n, term) in scope {
                let mut name = import_name.clone();
                name.push('.');
                name.push_str(n);
                defs.insert(scope_name, name, term.clone());
            }
        }
    }

    let init = socool::SoCoolParser::new().parse(&mut defs, &composition);

    match init {
        Ok(init) => {
            let defs = process_op_table(&mut defs)?;
            Ok(ParsedComposition { init, defs })
        }
        Err(error) => {
            println!("\n");
            let location = Arc::new(Mutex::new(Vec::new()));
            error.map_location(|l| location.lock().unwrap().push(l));
            let (line, column) = handle_parse_error(location, &composition);

            Err(ParseError {
                message: "Unexpected Token".to_string(),
                line,
                column,
            }
            .into_error())
        }
    }
}

fn handle_whitespace_and_imports(lines: Vec<String>) -> Result<(Vec<String>, String), Error> {
    let mut composition = String::new();
    let mut imports_needed: Vec<String> = vec![];
    for line in lines {
        let l = line;
        let copy_l = l.trim_start();
        if copy_l.starts_with("--") {
            composition.push_str("\n");
        } else if is_import(copy_l.to_string()) {
            imports_needed.push(copy_l.to_owned());
            composition.push_str("\n");
        } else {
            composition.push_str("\n");
            composition.push_str(&l);
        }
    }

    Ok((imports_needed, composition))
}

pub fn handle_fit_length_recursively(terms: Vec<Term>) -> Vec<Term> {
    let mut result = vec![];
    let mut i = 0;

    for term in terms.iter() {
        if i == 0 {
            result.push(term.to_owned());
        } else {
            match term {
                Term::Op(op) => match op {
                    Op::WithLengthRatioOf { with_length_of, .. } => {
                        let op1 = Term::Op(Op::Compose {
                            operations: result[0..i].into_iter().cloned().collect(),
                        });
                        result = vec![Term::Op(Op::Compose {
                            operations: vec![
                                op1.to_owned(),
                                Term::Op(Op::WithLengthRatioOf {
                                    with_length_of: with_length_of.clone(),
                                    main: Some(Box::new(op1)),
                                }),
                            ],
                        })];
                        i = 0;
                    }
                    _ => result.push(term.to_owned()),
                },
                _ => result.push(term.to_owned()),
            }
        }
        i += 1;
    }

    result
}

mod tests {
    #[test]
    fn filename_and_language_to_vec_string() {
        use super::*;
        let filename = "./working.socool";
        let mut language = "".to_string();
        let f = File::open(filename).expect("couldn't open ./working.socool");
        let file = BufReader::new(&f);
        file.lines().for_each(|line| {
            let l = line.expect("Could not parse line");
            language.push_str(&l);
            language.push_str("\n");
        });

        let from_filename = filename_to_vec_string(filename).unwrap();
        let from_language = language_to_vec_string(language.as_str());

        for (a, b) in from_filename.iter().zip(&from_language) {
            assert_eq!(a, b);
        }
    }
}