scryer-prolog 0.8.51

A modern Prolog implementation written mostly in Rust.
use l1::ast::{Addr, Atom, CompiledFact, CompiledQuery,
              FactInstruction, Heap, HeapCellValue, QueryInstruction,
              Registers};

use std::collections::HashMap;
use std::vec::Vec;

#[derive(Clone, Copy)]
enum MachineMode {
    Read,
    Write
}

pub struct Machine {
    h : usize,
    s : usize,
    p : usize,
    code : CompiledFact,
    code_dir : HashMap<(Atom, usize), usize>,
    fail : bool,
    heap : Heap,
    mode : MachineMode,
    registers : Registers
}

impl Machine {
    pub fn new() -> Machine {
        Machine { h : 0,
                  s : 0,
                  p : 0,
                  code : Vec::new(),
                  code_dir : HashMap::new(),
                  fail : false,
                  heap : Vec::with_capacity(256),
                  mode : MachineMode::Write,
                  registers : vec![HeapCellValue::Ref(0); 32] }
    }

    pub fn add_fact(&mut self, mut fact: CompiledFact, name: Atom, arity: usize)
    {
        let index = self.code.len();

        self.code.append(&mut fact);
        self.code_dir.insert((name, arity), index);
    }

    pub fn failed(&self) -> bool {
        self.fail
    }

    fn lookup(&self, a: Addr) -> &HeapCellValue {
        match a {
            Addr::HeapCell(hc) => &self.heap[hc],
            Addr::RegNum(reg)  => &self.registers[reg]
        }
    }

    fn deref(&self, a: Addr) -> Addr {
        let mut a = a;

        loop {
            if let &HeapCellValue::Ref(value) = self.lookup(a) {
                if let Addr::HeapCell(av) = a {
                    if value != av {
                        a = Addr::HeapCell(value);
                        continue;
                    }
                } else {
                    a = Addr::HeapCell(value);
                    continue;
                }
            }

            return a;
        };
    }

    fn is_unbound(hc: &HeapCellValue, index: usize) -> bool {
        match hc {
            &HeapCellValue::Ref(r) => r == index,
            _ => false
        }
    }

    //TODO: try to compress this function.
    fn bind(&mut self, a: Addr, val: usize) {
        let mut a = a;

        loop {
            match a {
                Addr::RegNum(reg) => {
                    if let HeapCellValue::Ref(hc) = self.registers[reg] {
                        a = Addr::HeapCell(hc);
                    } else if Machine::is_unbound(&self.heap[val], val) {
                        self.heap[val] = self.registers[reg].clone();
                        break;
                    } else {
                        self.fail = true;
                        break;
                    }
                },
                Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[hc], hc) => {
                    self.heap[hc] = HeapCellValue::Ref(val);
                    break;
                },
                Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[val], val) => {
                    self.heap[val] = HeapCellValue::Ref(hc);
                    break;
                },
                _ => {
                    self.fail = true;
                    break;
                }
            };
        }
    }

    fn unify(&mut self, a1: Addr, a2: Addr) {
        let mut pdl = vec![a1, a2];

        self.fail = false;

        while !(pdl.is_empty() || self.fail) {
            let d1 = self.deref(pdl.pop().unwrap());
            let d2 = self.deref(pdl.pop().unwrap());

            if d1 != d2 {
                match (self.lookup(d1), self.lookup(d2)) {
                    (&HeapCellValue::Ref(hc), _) =>
                        self.bind(d2, hc),
                    (_, &HeapCellValue::Ref(hc)) =>
                        self.bind(d1, hc),
                    (&HeapCellValue::Str(a1), &HeapCellValue::Str(a2)) => {
                        let r1 = &self.heap[a1];
                        let r2 = &self.heap[a2];

                        if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
                            if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
                                if n1 == n2 && *f1 == *f2 {
                                    for i in 1 .. n1 {
                                        pdl.push(Addr::HeapCell(a1 + i));
                                        pdl.push(Addr::HeapCell(a2 + i));
                                    }

                                    continue;
                                }
                            }
                        }

                        self.fail = true;
                    },
                    _ => self.fail = true,
                };
            }
        }
    }

    pub fn execute_fact(&mut self) {
        loop {
            if let &FactInstruction::Proceed = &self.code[self.p] {
                break;
            } else if self.fail {
                break;
            }

            let fact_instr = self.code[self.p].clone();
            self.execute_fact_instr(fact_instr);
        }
    }

    pub fn execute_query(&mut self, query: &CompiledQuery) {
        for instr in query {
            self.execute_query_instr(instr);

            if self.fail {
                break;
            }
        }
    }

    fn execute_query_instr<'a, 'b: 'a>(&'a mut self, instr: &'b QueryInstruction) {
        match instr {
            &QueryInstruction::Call(ref name, arity) => {
                // why is Option<&T> not Deref?!?!?
                // is it because if the value is None, there's nothing to
                // dereference?
                let compiled_fact_index =
                    self.code_dir.get(&(name.clone(), arity))
                        .map(|index| *index);

                match compiled_fact_index {
                    Some(compiled_fact_index) => {
                        self.p = compiled_fact_index;
                        self.execute_fact();
                    },
                    None => self.fail = true,
                };
            }
            &QueryInstruction::PutStructure(_, ref name, arity, reg) => {
                self.heap.push(HeapCellValue::Str(self.h + 1));
                self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));

                self.registers[reg] = self.heap[self.h].clone();

                self.h += 2;
            },
            &QueryInstruction::PutValue(arg, norm) =>
                self.registers[arg] = self.registers[norm].clone(),
            &QueryInstruction::PutVariable(arg, norm) => {
                self.heap.push(HeapCellValue::Ref(self.h));

                self.registers[norm] = self.heap[self.h].clone();
                self.registers[arg]  = self.heap[self.h].clone();

                self.h += 1;
            },
            &QueryInstruction::SetVariable(reg) => {
                self.heap.push(HeapCellValue::Ref(self.h));
                self.registers[reg] = self.heap[self.h].clone();

                self.h += 1;
            },
            &QueryInstruction::SetValue(reg) => {
                self.heap.push(self.registers[reg].clone());
                self.h += 1;
            },
        }
    }

    fn execute_fact_instr(&mut self, instr: FactInstruction) {
        match instr {
            FactInstruction::Proceed => return,
            FactInstruction::GetStructure(_, name, arity, reg) => {
                let addr = self.deref(Addr::RegNum(reg));

                match self.lookup(addr) {
                    &HeapCellValue::Str(a) => {
                        let result = &self.heap[a];

                        if let &HeapCellValue::NamedStr(named_arity, ref named_str) = result {
                            if arity == named_arity && *name == *named_str {
                                self.s = a + 1;
                                self.mode = MachineMode::Read;
                            } else {
                                self.fail = true;
                            }
                        }
                    },
                    &HeapCellValue::Ref(r) => {
                        self.heap.push(HeapCellValue::Str(self.h + 1));
                        self.heap.push(HeapCellValue::NamedStr(arity, name));

                        let h = self.h;

                        self.bind(Addr::HeapCell(r), h);

                        self.h += 2;
                        self.mode = MachineMode::Write;
                    },
                    _ => {
                        self.fail = true;
                    }
                };
            },
            FactInstruction::GetVariable(arg, norm) =>
                self.registers[norm] = self.registers[arg].clone(),
            FactInstruction::GetValue(arg, norm) =>
                self.unify(Addr::RegNum(norm), Addr::RegNum(arg)),
            FactInstruction::UnifyVariable(reg) => {
                match self.mode {
                    MachineMode::Read  => self.registers[reg] = self.heap[self.s].clone(),
                    MachineMode::Write => {
                        self.heap.push(HeapCellValue::Ref(self.h));
                        self.registers[reg] = self.heap[self.h].clone();
                        self.h += 1;
                    }
                };

                self.s += 1;
            },
            FactInstruction::UnifyValue(reg) => {
                let s = self.s;

                match self.mode {
                    MachineMode::Read  => self.unify(Addr::RegNum(reg), Addr::HeapCell(s)),
                    MachineMode::Write => {
                        self.heap.push(self.registers[reg].clone());
                        self.h += 1;
                    }
                };

                self.s += 1;
            }
        }

        self.p += 1;
    }

    pub fn reset_machine_state(&mut self) {
        self.h = 0;
        self.s = 0;
        self.p = 0;

        self.fail = false;
        self.heap = Vec::with_capacity(256);
        self.mode = MachineMode::Write;
        self.registers = vec![HeapCellValue::Ref(0); 32];
    }
}