view src/generic_term_parser_experiment.rs @ 4:000d0a69ed00 draft

Implement prototype for generic two-side operator parser
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 23 Nov 2016 20:53:58 +0100
parents
children
line wrap: on
line source


type TwoHandBuilder = fn(Box<Ast>, Box<Ast>) -> Ast;
type OperatorMap = HashMap<char, TwoHandBuilder>;

fn term_op_map() -> OperatorMap {
    let mut map: OperatorMap = HashMap::new();
    map.insert('*', Ast::Multiplication);
    map.insert('/', Ast::Division);
    map
}

fn expr_op_map() -> OperatorMap {
    let mut map: OperatorMap = HashMap::new();
    map.insert('+', Ast::Addition);
    map.insert('-', Ast::Subtraction);
    map
}

fn parse_twohand_expr<'a>(opmap: OperatorMap, st: State<'a>) -> ParseResult {
    if st.len() == 0 {
        err("Empty", st)
    } else {
        let ops: Vec<char> = opmap.keys().map(|c| *c).collect();
        // Predicate: Is this an operator we're looking for
        let _wanted_operator = |c: char| ops.iter().find(|d| c == **d).is_some();

        // Test if addition or subtraction
        let mut paren_lvl = 0;
        let mut ix = 0;
        let mut op_pos: i32 = -1;

        // Look for operator at top level
        for c in st.iter() {
            if c == '(' {
                paren_lvl += 1;
            } else if c == ')' {
                paren_lvl -= 1;
            } else if paren_lvl == 0 && (_wanted_operator(c)) {
                // yep!
                op_pos = ix;
                break;
            }
            ix += 1;
        }

        if op_pos > -1 {
            let left = pexpr(st.restrict(0, op_pos as usize));
            let right = pexpr(st.restrict(op_pos as usize + 1, st.len()));

            if left.ok() && right.ok() {
                match (left, right) {
                    (ParseResult::Ok(a1), ParseResult::Ok(a2)) => {
                        if let Some(builder) = opmap.get(&st.at(op_pos as usize)) {
                            ParseResult::Ok(builder(Box::new(a1), Box::new(a2)))
                        } else {
                            err("No matching Ast builder found", st)
                        }
                    }
                    _ => panic!("Expected OK results"),
                }
            } else if !left.ok() {
                err("Left part of term is bad", st)
            } else {
                err("Right or both parts of term is|are bad", st)
            }
        } else {
            // XXX This needs to be generic, too
            sym(st)
        }
    }
}