mirror of
https://github.com/MorizzG/rlox.git
synced 2026-02-04 11:54:48 +00:00
improved REPL a bit
now uses rustyline for input
This commit is contained in:
parent
ca6e092b38
commit
fb88595b6c
13 changed files with 560 additions and 100 deletions
12
src/main.rs
12
src/main.rs
|
|
@ -1,5 +1,9 @@
|
|||
use clap::{ArgAction, Parser};
|
||||
|
||||
mod repl;
|
||||
|
||||
use repl::{run_repl, LoxResult};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct CliArgs {
|
||||
#[arg()]
|
||||
|
|
@ -82,6 +86,12 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
rlox2_interpreter::run_repl(&mut runtime);
|
||||
run_repl(
|
||||
move |source| match rlox2_interpreter::run(source, &mut runtime) {
|
||||
Ok(()) => LoxResult::Ok(()),
|
||||
Err(rlox2_interpreter::LoxError::Exit { exit_code }) => LoxResult::Exit(exit_code),
|
||||
Err(e) => LoxResult::Err(e),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
199
src/repl.rs
Normal file
199
src/repl.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use std::error::Error;
|
||||
|
||||
use colored::Colorize;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::highlight::Highlighter;
|
||||
use rustyline::hint::Hinter;
|
||||
use rustyline::history::FileHistory;
|
||||
use rustyline::validate::{ValidationContext, ValidationResult, Validator};
|
||||
use rustyline::{Editor, Helper};
|
||||
|
||||
pub enum LoxResult<E: Error> {
|
||||
Ok(()),
|
||||
Err(E),
|
||||
Exit(i32),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ReplHelper {}
|
||||
|
||||
impl Validator for ReplHelper {
|
||||
fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result<ValidationResult> {
|
||||
let input = ctx.input();
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Brackets {
|
||||
LeftParen,
|
||||
LeftBrace,
|
||||
}
|
||||
|
||||
use Brackets::*;
|
||||
|
||||
let mut brackets = Vec::new();
|
||||
|
||||
let chars: Vec<char> = input.chars().collect();
|
||||
|
||||
let mut pos = 0;
|
||||
|
||||
while pos < chars.len() {
|
||||
match chars[pos] {
|
||||
'(' => brackets.push(LeftParen),
|
||||
')' => {
|
||||
if brackets.is_empty() || brackets.last().unwrap() != &LeftParen {
|
||||
// no matching opening paren on the stack -> parser error
|
||||
return Ok(ValidationResult::Valid(None));
|
||||
}
|
||||
brackets.pop();
|
||||
}
|
||||
'{' => brackets.push(LeftBrace),
|
||||
'}' => {
|
||||
if brackets.is_empty() || brackets.last().unwrap() != &LeftBrace {
|
||||
// no matching opening brace on the stack -> parser error
|
||||
return Ok(ValidationResult::Valid(None));
|
||||
}
|
||||
brackets.pop();
|
||||
}
|
||||
'/' => {
|
||||
// line comment
|
||||
match chars[pos + 1] {
|
||||
'/' => {
|
||||
pos += 2;
|
||||
while pos < chars.len() && chars[pos] != '\n' {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
'*' => {
|
||||
pos += 2;
|
||||
|
||||
let mut comment_depth: u32 = 1;
|
||||
|
||||
let match_two = |pos, c1, c2| chars[pos] == c1 && chars[pos + 1] == c2;
|
||||
|
||||
while pos < chars.len() - 1 {
|
||||
if match_two(pos, '/', '*') {
|
||||
comment_depth += 1;
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if match_two(pos, '*', '/') {
|
||||
comment_depth -= 1;
|
||||
|
||||
pos += 2;
|
||||
|
||||
if comment_depth == 0 {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if !brackets.is_empty() {
|
||||
return Ok(ValidationResult::Incomplete);
|
||||
}
|
||||
|
||||
// all braces/parens/brackets closed => Ok
|
||||
Ok(ValidationResult::Valid(None))
|
||||
}
|
||||
|
||||
fn validate_while_typing(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter for ReplHelper {}
|
||||
|
||||
impl Hinter for ReplHelper {
|
||||
type Hint = String;
|
||||
}
|
||||
|
||||
impl Completer for ReplHelper {
|
||||
type Candidate = String;
|
||||
}
|
||||
|
||||
impl Helper for ReplHelper {}
|
||||
|
||||
pub fn run_repl<F: FnMut(&str) -> LoxResult<E>, E: Error>(mut run: F) {
|
||||
// let mut readline = DefaultEditor::new().unwrap();
|
||||
let mut readline: Editor<ReplHelper, FileHistory> = Editor::new().unwrap();
|
||||
readline.set_helper(Some(ReplHelper::default()));
|
||||
let _ = readline.load_history("/tmp/rlox_history.txt");
|
||||
|
||||
let mut input_buf = String::new();
|
||||
|
||||
'outer: loop {
|
||||
input_buf.clear();
|
||||
|
||||
match readline.readline("> ") {
|
||||
Ok(line) => {
|
||||
readline.add_history_entry(&line).unwrap();
|
||||
input_buf += &line;
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => continue 'outer,
|
||||
Err(ReadlineError::Eof) => break 'outer,
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
continue 'outer;
|
||||
}
|
||||
};
|
||||
|
||||
/* 'inner: loop {
|
||||
let num_open_braces =
|
||||
(input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
|
||||
let num_open_parens =
|
||||
(input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
|
||||
let num_open_brackets =
|
||||
(input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
|
||||
|
||||
// all braces/parens/brackets closed => break
|
||||
if num_open_braces == 0 && num_open_parens == 0 && num_open_brackets == 0 {
|
||||
break 'inner;
|
||||
}
|
||||
|
||||
// any braces/parens/brackets more closing than opening => break (will be parse error)
|
||||
if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 {
|
||||
break 'inner;
|
||||
}
|
||||
|
||||
match readline.readline("< ") {
|
||||
Ok(line) => {
|
||||
input_buf += &line;
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => continue 'outer,
|
||||
Err(ReadlineError::Eof) => break 'outer,
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
continue 'outer;
|
||||
}
|
||||
};
|
||||
} */
|
||||
|
||||
if input_buf.is_empty() || input_buf == "exit\n" || input_buf == "quit\n" {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let input_buf = input_buf.trim();
|
||||
|
||||
match run(input_buf) {
|
||||
LoxResult::Ok(()) => {}
|
||||
LoxResult::Err(err) => println!("{}", err.to_string().red().bold()),
|
||||
LoxResult::Exit(exit_code) => std::process::exit(exit_code),
|
||||
}
|
||||
}
|
||||
|
||||
readline.save_history("/tmp/rlox_history.txt").unwrap();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue