summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: aae51225714be78e2e6c196d762afbd553fbda2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use difx::def_rules::Rule;
use difx::match_rules::{self, Bindings};
use difx::simplify;
use difx::sexpr::{Expr, Parser};
use rustyline::DefaultEditor;
use rustyline::error::ReadlineError;

const MAX_REWRITE_ITERS: usize = 100;

fn parse_expr(input: &str) -> Option<Expr> {
    std::panic::catch_unwind(|| {
        let mut parser = Parser::new(input);
        parser.parse_one()
    })
    .ok()
}

fn rewrite_node(rules: &[Rule], expr: &Expr) -> Option<(usize, Expr)> {
    for (idx, rule) in rules.iter().enumerate() {
        let mut bindings = Bindings::default();
        if match_rules::matches(&rule.lhs, expr, &mut bindings) {
            let rewritten = match_rules::substitute(&rule.rhs, &bindings);
            return Some((idx, rewritten));
        }
    }

    None
}

fn rewrite_top_down_once(rules: &[Rule], expr: &Expr) -> Option<(usize, Expr)> {
    if let Some(hit) = rewrite_node(rules, expr) {
        return Some(hit);
    }

    match expr {
        Expr::Atom(_) => None,
        Expr::Application(items) => {
            for (idx, item) in items.iter().enumerate() {
                if let Some((rule_idx, rewritten_child)) = rewrite_top_down_once(rules, item) {
                    let mut rewritten_items = items.clone();
                    rewritten_items[idx] = rewritten_child;
                    return Some((rule_idx, Expr::Application(rewritten_items)));
                }
            }

            None
        }
        Expr::OrderedList(items) => {
            for (idx, item) in items.iter().enumerate() {
                if let Some((rule_idx, rewritten_child)) = rewrite_top_down_once(rules, item) {
                    let mut rewritten_items = items.clone();
                    rewritten_items[idx] = rewritten_child;
                    return Some((rule_idx, Expr::OrderedList(rewritten_items)));
                }
            }

            None
        }
    }
}

fn main() {
    let input = include_str!("../base.rules");
    let rules = difx::def_rules::parse_def_rules(input.to_string());

    println!("Loaded {} rule(s) from base.rules", rules.len());

    let mut rl = DefaultEditor::new().expect("failed to initialize line editor");

    loop {
        let line = match rl.readline("> ") {
            Ok(line) => line,
            Err(ReadlineError::Interrupted) => {
                println!("^C");
                continue;
            }
            Err(ReadlineError::Eof) => {
                println!();
                break;
            }
            Err(err) => {
                eprintln!("Readline error: {err}");
                break;
            }
        };

        let line = line.trim();
        if line.is_empty() {
            continue;
        }

        if let Err(err) = rl.add_history_entry(line) {
            eprintln!("history error: {err}");
        }

        let Some(mut expr) = parse_expr(line) else {
            println!("Parse error");
            continue;
        };

        let mut applied = 0usize;
        while applied < MAX_REWRITE_ITERS {
            let Some((idx, rewritten_expr)) = rewrite_top_down_once(&rules, &expr) else {
                break;
            };
            let next_expr = simplify::simplify(rewritten_expr);

            let rule = &rules[idx];
            println!("{}.", applied + 1);
            println!("```lisp\n{} => {}\n```", rule.lhs, rule.rhs);
            println!("> {}", next_expr);

            expr = next_expr;
            applied += 1;
        }

        if applied == 0 {
            println!("NO MATCH");
        } else {
            println!("final: {}", expr);
        }

        let stopped = applied == MAX_REWRITE_ITERS;
        if applied == MAX_REWRITE_ITERS {
            let stop_msg = format!("Stopped after {} iterations (safety limit).", MAX_REWRITE_ITERS);
            println!("{}", stop_msg);
        }
    }
}