Interp - an Interpreted Programming Language Pt.10 - REPL and Comments

1 minute read

Hey there! It’s Pt. 10 and in this one we create a command line interface. Also, comments and multilines.

RustyLine

Add rustyline (found here) to your dependencies.

Cargo.toml:

[dependencies]

...

rustyline = "10.0.0"

VM

Make the functions expr_to_value and value_repr public since we are going to use them from main.

src/vm/mod.rs:

pub fn expr_to_value(vm: &mut VM, expr: &Expr) -> value::Value {
    ...

pub fn value_repr(val: value::Value) -> String {
    ...

Main

Multilines and comments

Add support for multilines and comments.

src/main.rs:


...

use std::fs;
use structopt::StructOpt;

fn main() {
    let opt = opt::Opt::from_args();

    let mut vm = vm::init_vm();

    match opt.source_file {
        Some(source_file) => {
// We read the whole file before parsing
match fs::read_to_string(&source_file) {
    Ok(text) => {
        let mut line_buffer = String::new();
        for (line_nr, mut line_str) in text.lines().enumerate() {
            if let Some(i) = line_str.find("#") {
                line_str = &line_str[0..i];
            }
            // Multi-line "lines"
            if line_str.trim_end().ends_with("\\") {
                line_buffer.push_str(&line_str[0..line_str.rfind("\\").unwrap()]);
            } else  {
                line_buffer.push_str(&line_str);
                let parsed_line = parser::parse_line(
                    &line_buffer,
                    &source_file,
                    line_nr
                );
                if let Ok(parsed_line) = parsed_line {
                    vm::exec_line(&mut vm, &parsed_line);
                }
                line_buffer.clear();
            }
        }
    }
    Err(err) => {
        err::throw(err::Err::FileError { file: source_file, message: err.to_string() });
    }
}

REPL

Implement REPL with rustyline.

None => {
    let mut rl = rustyline::Editor::<()>::new().unwrap();
    for line_nr in 0.. {
        let line_str = rl.readline("> ");
        match &line_str {
            Ok(line_str) => {
                rl.add_history_entry(line_str);
                let parsed_line = parser::parse_line(
                    line_str,
                    "<stdin>",
                    line_nr
                );
                if let Ok(parsed_line) = parsed_line {
                    // If a line contains only an Expr and we are not inside a scope
                    // We will simply print the repr
                    if vm.scope == 0 {
                         if let parser::line::LineType::Expr(expr) = &parsed_line.type_ {
                                    let value = vm::expr_to_value(&mut vm, expr);
                            println!("{}", vm::value_repr(value));
                        } else {
                            vm::exec_line(&mut vm, &parsed_line);
                        }
                    } else {
                        vm::exec_line(&mut vm, &parsed_line);
                    }
                }
            }
            Err(_) => break,
        }
    }
}

...

Source Code

Source code for this part can be found here.

Next Up

Click for Pt. 11 or click here for all parts.

Updated: