lep 0.5.0

Mini language for interactive consoles, featuring small code footprint.
Documentation
// Copyright (c) 2019 Timo Savola.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

use std::process::exit;
use std::time::{SystemTime, UNIX_EPOCH};

use rustyline::error::ReadlineError;
use rustyline::Editor;

use lep::{builtin, eval_stmt, obj, stringify, Domain, Fun, FunMut, Obj, Res, State};

struct Sequence {
    n: i64,
}

impl FunMut for Sequence {
    fn invoke(&mut self, args: &Obj) -> Res {
        if args.is::<()>() {
            self.n += 1;
            Ok(obj::int(self.n))
        } else {
            Err("sequence: too many arguments".to_string())
        }
    }
}

struct Time;

impl Fun for Time {
    fn invoke(&self, args: &Obj) -> Res {
        if args.is::<()>() {
            match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(n) => Ok(obj::int(n.as_secs() as i64)),
                Err(e) => Err(format!("time: {}", e)),
            }
        } else {
            Err("time: too many arguments".to_string())
        }
    }
}

fn main() {
    let mut sequence = Sequence { n: -1 };
    let time = Time {};

    let mut domain = Domain::new();
    builtin::register(&mut domain);
    domain.register_fun_mut("sequence", &mut sequence);
    domain.register_fun("time", &time);

    let mut state = State::new();

    let mut rl = Editor::<()>::new();
    let mut prefix = "".to_string();

    loop {
        match rl.readline(&format!("{}>> ", prefix)) {
            Ok(line) => {
                rl.add_history_entry(&line);

                match eval_stmt(&mut domain, state.clone(), &line) {
                    Ok(res) => {
                        if res.result.value.is::<()>() {
                            if res.result.name == "_" {
                                prefix = "".to_string();
                            } else if !res.result.name.is_empty() {
                                println!("{} = ()", res.result.name);
                            }
                        } else {
                            let repr = stringify(&res.result.value).unwrap_or("?".to_string());
                            if res.result.name == "_" {
                                prefix = repr + " ";
                            } else if !res.result.name.is_empty() {
                                println!("{} = {}", res.result.name, repr);
                            }
                        }

                        state = res;
                    }

                    Err(msg) => {
                        println!("error: {}", msg);
                    }
                }
            }

            Err(ReadlineError::Eof) => {
                break;
            }

            Err(x) => {
                println!("read error: {}", x);
                exit(1);
            }
        }
    }
}