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
//! Tokay repeat-eval-print-loop

use std::cell::RefCell;
use std::fs::File;
use std::io::{self, BufReader, Seek};

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

use crate::compiler::Compiler;
use crate::reader::Reader;
use crate::value::RefValue;
use crate::vm::Runtime;

// Helper enum to allow for different input types
pub enum Stream {
    String(String),
    File(File),
    Stdin,
}

impl Stream {
    pub fn get_reader(&mut self) -> Reader {
        match self {
            Stream::String(string) => Reader::new(Box::new(io::Cursor::new(string.clone()))),
            Stream::File(file) => {
                let mut file = file.try_clone().expect("File cannot be cloned?");
                file.seek(std::io::SeekFrom::Start(0))
                    .expect("Unable to seek to file's starting position");
                Reader::new(Box::new(BufReader::new(file)))
            }
            Stream::Stdin => Reader::new(Box::new(BufReader::new(io::stdin()))),
        }
    }
}

// A first simple REPL for Tokay
pub fn repl(streams: Vec<(&str, RefCell<Stream>)>, debug: bool) {
    let mut globals: Vec<RefValue> = Vec::new();

    let mut compiler = Compiler::new();
    compiler.interactive = true;
    compiler.debug = debug;

    // todo: Implement a completer?
    let mut readline = Editor::<()>::new();

    // todo: Implement a history in $HOME for production?
    if cfg!(debug_assertions) {
        readline.load_history(".tokayhist").ok();
    }

    loop {
        let code = match readline.readline(">>> ") {
            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,

            Err(err) => {
                println!("Error {:?}", err);
                break;
            }

            Ok(code) => code,
        };

        // Stop when program is empty.
        if code.trim().is_empty() {
            continue;
        }

        //println!("code = {:?}", code);

        readline.add_history_entry(code.as_str());

        match code.as_str() {
            "#debug" => {
                compiler.debug = true;
                println!("<<< Debug switched on")
            }
            "#nodebug" => {
                compiler.debug = false;
                println!("<<< Debug switched off")
            }
            _ => {
                if let Ok(program) = compiler.compile(Reader::new(Box::new(io::Cursor::new(code))))
                {
                    for (name, stream) in &streams {
                        let mut reader = stream.borrow_mut().get_reader();
                        let mut runtime = Runtime::new(&program, &mut reader);
                        runtime.debug = compiler.debug;
                        runtime.load_stack(globals);

                        let ret = program.run(&mut runtime);

                        if streams.len() > 1 {
                            print!("{}: ", name);
                        }

                        match ret {
                            Ok(None) => {
                                if streams.len() > 1 {
                                    print!("\n")
                                }
                            }
                            Ok(Some(value)) => println!("{}", value.borrow()),
                            Err(error) => println!("{}", error),
                        }

                        globals = runtime.save_stack();
                    }
                }
            }
        }
    }

    if cfg!(debug_assertions) {
        readline.save_history(".tokayhist").unwrap();
    }
}