nova-interpreter 0.1.1

An interpreter library for the nova language
Documentation
use std::{cmp, fmt::Debug};

use state::State;

use crate::{primitives::Builtins, utils::interner::Interner};

use super::{parser::ruleset::RuleSet, utils::PrimitiveFn};

pub mod state;

pub struct Engine {
    pub state: State,
    pub program: Program,
}

pub struct Program {
    pub primitives: Vec<(Box<[usize]>, PrimitiveFn)>,
    program: RuleSet,
}

#[derive(Debug)]
pub enum VeraEvent {
    Continuing,
    Error(VeraError),
}

#[derive(Debug)]
pub enum VeraError {
    MaxStepsExceeded,
}

type StepCallback = Box<dyn Fn(&mut Engine, usize, VeraEvent)>;

impl Engine {
    pub fn new(interner: Interner<String>) -> Self {
        let regs = vec![];
        let regs = regs.into_boxed_slice();
        Engine {
            state: State {
                registers: regs,
                interner,
            },
            program: Program {
                primitives: vec![],
                program: RuleSet::empty(),
            },
        }
    }

    /// Run a callback to get builtins to add to the engine
    pub fn with_builtins(&mut self, f: &dyn Fn(&mut Builtins, &mut Interner<String>)) {
        let mut builtins = Builtins {
            builtins: &mut self.program.primitives,
        };
        f(&mut builtins, &mut self.state.interner);
    }

    /// Parse code from a string and add it to the current program
    pub fn load_notecard(&mut self, code: &str) {
        let notecard = crate::parser::parse(code, &mut self.state.interner);
        self.program.program += notecard.unwrap();
    }

    /// Run the program for up to `max_steps` steps, calling a given callback
    /// on each step
    pub fn run(&mut self, max_steps: usize, step_callback: StepCallback) -> usize {
        let mut steps = 0;

        self.setup();

        while self.run_step() {
            steps += 1;

            if steps >= max_steps {
                step_callback(self, steps, VeraEvent::Error(VeraError::MaxStepsExceeded));
                break;
            } else {
                step_callback(self, steps, VeraEvent::Continuing);
            }
        }

        steps
    }

    /// Initialize the engine, creating the registers list
    /// and setting up the initial facts in the knowledge base
    pub fn setup(&mut self) {
        let registers = vec![0; self.state.interner.count()].into_boxed_slice();

        self.state.registers = registers;

        for fact in &self.program.program.facts {
            for (register, multiplicity) in &fact.multiplicities {
                self.state.produce(*register, *multiplicity);
            }
        }
    }

    /// Run the engine for a single step
    /// returning false if no rules or builtins matched
    pub fn run_step(&mut self) -> bool {
        'prims: for (condition, primitive) in self.program.primitives.iter() {
            for register in condition.iter() {
                if !self.state.present(*register) {
                    continue 'prims;
                }
            }

            primitive(&mut self.state, 0);
            return true;
        }

        'rules: for rule in &self.program.program.rules {
            let mut minimum = usize::MAX;

            for register in rule.conditions.iter() {
                let value = self.state.value(register.0);
                if value == 0 {
                    continue 'rules;
                }

                minimum = cmp::min(value, minimum);
            }

            for register in rule.conditions.iter() {
                self.state.consume(register.0, minimum);
            }

            for (register, multiplicity) in rule.multiplicities.iter() {
                self.state.produce(*register, minimum * *multiplicity);
            }

            return true;
        }

        false
    }

    pub fn print_state(&mut self) {
        self.state.print_state();
    }
}