mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Improved read
- no need to parse the input multiple times - read function is more clean Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
88
src/main.rs
88
src/main.rs
@ -1,64 +1,16 @@
|
||||
// io lib to read input and print output
|
||||
use std::env::args;
|
||||
use std::io::{self, Write};
|
||||
|
||||
mod env;
|
||||
mod eval;
|
||||
mod parse_tools;
|
||||
mod printer;
|
||||
mod reader;
|
||||
mod types;
|
||||
use env::{env_init, Env};
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use types::MalErr;
|
||||
|
||||
mod step4_if_fn_do;
|
||||
use step4_if_fn_do::rep;
|
||||
mod types;
|
||||
|
||||
fn load_file(filename: &str, env: &Env) -> io::Result<()> {
|
||||
let file = File::open(filename)?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut last: Result<Vec<String>, MalErr> = Ok(Vec::new());
|
||||
|
||||
let comment_line = Regex::new(r#"^[\s]*;.*"#).unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
for line in reader.lines() {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
// Read line to compose program inpu
|
||||
|
||||
if line == "" || comment_line.is_match(&line) {
|
||||
continue; // Don't even add it
|
||||
} else {
|
||||
input.push_str(format!("{}\n", &line).as_str());
|
||||
}
|
||||
|
||||
last = match rep(&input, env) {
|
||||
Err(error) if error.is_recoverable() => Err(error),
|
||||
tmp => {
|
||||
input = String::new();
|
||||
tmp.map_err(|error| {
|
||||
println!("; Error @ {}", error.message());
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => eprintln!("Error reading line: {}", err),
|
||||
}
|
||||
}
|
||||
match last {
|
||||
Err(error) => println!(
|
||||
"; ERROR parsing: '{}'\n; {}\n; the environment is in an unknown state",
|
||||
filename,
|
||||
error.message()
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
use env::env_init;
|
||||
use parse_tools::{load_file, interactive};
|
||||
|
||||
fn main() {
|
||||
let reply_env = env_init();
|
||||
@ -69,35 +21,5 @@ fn main() {
|
||||
let _ = load_file(filename, &reply_env);
|
||||
}
|
||||
|
||||
let mut num = 0;
|
||||
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
loop {
|
||||
print!("user> ");
|
||||
// Flush the prompt to appear before command
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
// Read line to compose program input
|
||||
let mut line = String::new();
|
||||
io::stdin().read_line(&mut line).unwrap();
|
||||
|
||||
input.push_str(&line);
|
||||
|
||||
if input != "\n" {
|
||||
// Perform rep on whole available input
|
||||
match rep(&input, &reply_env) {
|
||||
Ok(output) => output.iter().for_each(|el| println!("[{}]> {}", num, el)),
|
||||
Err(error) => {
|
||||
if error.is_recoverable() && line != "\n" {
|
||||
continue;
|
||||
}
|
||||
println!("; [{}]> Error @ {}", num, error.message());
|
||||
}
|
||||
}
|
||||
num += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
interactive(reply_env);
|
||||
}
|
||||
|
||||
87
src/parse_tools.rs
Normal file
87
src/parse_tools.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::env::Env;
|
||||
use crate::reader::Reader;
|
||||
use crate::step4_if_fn_do::rep;
|
||||
use crate::types::MalErr;
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
|
||||
pub fn load_file(filename: &str, env: &Env) -> io::Result<()> {
|
||||
let file = File::open(filename)?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut last: Result<Vec<String>, MalErr> = Ok(Vec::new());
|
||||
|
||||
let comment_line = Regex::new(r#"^[\s]*;.*"#).unwrap();
|
||||
|
||||
let parser = Reader::new();
|
||||
for line in reader.lines() {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
// Read line to compose program inpu
|
||||
|
||||
if line == "" || comment_line.is_match(&line) {
|
||||
continue; // Don't even add it
|
||||
} else {
|
||||
parser.push(&line);
|
||||
}
|
||||
|
||||
last = match rep(&parser, env) {
|
||||
Err(error) if error.is_recoverable() => {
|
||||
Err(error)
|
||||
}
|
||||
tmp => {
|
||||
parser.clear();
|
||||
tmp.map_err(|error| {
|
||||
println!("; Error @ {}", error.message());
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => eprintln!("Error reading line: {}", err),
|
||||
}
|
||||
}
|
||||
match last {
|
||||
Err(error) => println!(
|
||||
"; ERROR parsing: '{}'\n; {}\n; the environment is in an unknown state",
|
||||
filename,
|
||||
error.message()
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
pub fn interactive(env: Env) {
|
||||
let mut num = 0;
|
||||
let parser = Reader::new();
|
||||
loop {
|
||||
parser.clear();
|
||||
loop {
|
||||
print!("user> ");
|
||||
// Flush the prompt to appear before command
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
// Read line to compose program input
|
||||
let mut line = String::new();
|
||||
io::stdin().read_line(&mut line).unwrap();
|
||||
|
||||
parser.push(&line);
|
||||
|
||||
// Perform rep on whole available input
|
||||
match rep(&parser, &env) {
|
||||
Ok(output) => output.iter().for_each(|el| println!("[{}]> {}", num, el)),
|
||||
Err(error) => {
|
||||
if error.is_recoverable() {// && line != "\n" {
|
||||
continue;
|
||||
}
|
||||
println!("; [{}]> Error @ {}", num, error.message());
|
||||
}
|
||||
}
|
||||
num += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
// Specyfy components in "types"
|
||||
use crate::types::*;
|
||||
@ -9,7 +8,7 @@ use crate::types::MalType::*;
|
||||
use regex::Regex;
|
||||
|
||||
pub struct Reader {
|
||||
tokens: Vec<String>,
|
||||
tokens: RefCell<Vec<String>>,
|
||||
ptr: Cell<usize>,
|
||||
}
|
||||
|
||||
@ -19,13 +18,31 @@ type Tokens = Vec<String>;
|
||||
// Status on return should always be The last element of the last opened lists
|
||||
// (append to the "last" list) while traversing
|
||||
impl Reader {
|
||||
pub fn new(input: &str) -> Reader {
|
||||
Reader { tokens: tokenize(input), ptr: Cell::new(0) }
|
||||
pub fn new() -> Reader {
|
||||
Reader {
|
||||
tokens: RefCell::new(Vec::new()),
|
||||
ptr: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&self, input: &str) {
|
||||
self.ptr.set(0);
|
||||
// reset the state of the parser and push the additional strings
|
||||
self.tokens
|
||||
.borrow_mut()
|
||||
.append(&mut tokenize(input))
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.ptr.set(0);
|
||||
*self.tokens
|
||||
.borrow_mut() = Vec::new();
|
||||
}
|
||||
|
||||
// May be improved
|
||||
fn get_token(&self, i: usize) -> Result<String, MalErr> {
|
||||
self.tokens
|
||||
.borrow()
|
||||
.get(i)
|
||||
.ok_or(MalErr::recoverable("Unexpected EOF"))
|
||||
.cloned()
|
||||
@ -103,9 +120,9 @@ impl Reader {
|
||||
_ => self.read_atom(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn ended(&self) -> bool {
|
||||
self.tokens.len() == self.ptr.get()
|
||||
self.tokens.borrow().len() == self.ptr.get()
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +136,13 @@ pub fn read_str(reader: &Reader) -> MalRet {
|
||||
/// Read a string and return a list of tokens in it (following regex in README)
|
||||
// Add error handling for strings that are not terminated
|
||||
fn tokenize(input: &str) -> Tokens {
|
||||
let tokens = Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*\n|[^\s\[\]{}('"`,;)]*)"###)
|
||||
.unwrap()
|
||||
.captures_iter(input)
|
||||
.map(|e| e[1].to_string())
|
||||
.filter(|e| !(e.is_empty() || e.starts_with(';')))
|
||||
.collect::<Vec<String>>();
|
||||
let tokens = Regex::new(
|
||||
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"###,
|
||||
)
|
||||
.unwrap()
|
||||
.captures_iter(input)
|
||||
.map(|e| e[1].to_string())
|
||||
.filter(|e| !(e.is_empty() || e.starts_with(';')))
|
||||
.collect::<Vec<String>>();
|
||||
tokens
|
||||
}
|
||||
|
||||
@ -28,13 +28,14 @@ fn PRINT(output: MalType) -> String {
|
||||
pr_str(&output, true)
|
||||
}
|
||||
|
||||
pub fn rep(input: &str, env: &Env) -> Result<Vec<String>, MalErr> {
|
||||
let reader = Reader::new(input);
|
||||
pub fn rep(reader: &Reader, env: &Env) -> Result<Vec<String>, MalErr> {
|
||||
let mut ret_str = Vec::new();
|
||||
loop {
|
||||
let ast = READ(&reader)?;
|
||||
let out = EVAL(ast, env.clone())?;
|
||||
ret_str.push(PRINT(out));
|
||||
if reader.ended(){break Ok(ret_str);}
|
||||
if reader.ended() {
|
||||
break Ok(ret_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user