mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Cleaning repl
Cleaning repl, removing multi-statement repl, further implementation as separated code from the rest Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
@ -29,10 +29,8 @@ fn main() -> io::Result<()> {
|
|||||||
// Perform rep on whole available input
|
// Perform rep on whole available input
|
||||||
match rep(&input) {
|
match rep(&input) {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
for el in output {
|
num += 1;
|
||||||
num += 1;
|
println!("[{}]> {}", num, output);
|
||||||
println!("[{}]> {}", num, el);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if line == "\n" {
|
if line == "\n" {
|
||||||
|
|||||||
100
src/reader.rs
100
src/reader.rs
@ -19,26 +19,23 @@ impl Reader {
|
|||||||
Reader { tokens, ptr: 0 }
|
Reader { tokens, ptr: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn avail(&self) -> bool {
|
// May be improved
|
||||||
self.ptr < self.tokens.len()
|
fn get_token(&self, i: usize) -> Result<String, MalErr> {
|
||||||
|
match self.tokens.get(i) {
|
||||||
|
Some(token) => Ok(token.to_string()),
|
||||||
|
None => Err("Unexpected EOF".to_string()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the token at the current position
|
/// Returns the token at the current position
|
||||||
fn peek(&self) -> Result<String, MalErr> {
|
fn peek(&self) -> Result<String, MalErr> {
|
||||||
match self.tokens.get(self.ptr) {
|
self.get_token(self.ptr)
|
||||||
Some(token) => Ok(token.to_string()),
|
|
||||||
None => Err("Unexpected EOF, Missing parenthesis?".to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the token at current position and increment current position
|
/// Returns the token at current position and increment current position
|
||||||
// TODO: PLEASE USE THE PEEK FUNCTION
|
|
||||||
fn next(&mut self) -> Result<String, MalErr> {
|
fn next(&mut self) -> Result<String, MalErr> {
|
||||||
self.ptr += 1;
|
self.ptr += 1;
|
||||||
match self.tokens.get(self.ptr - 1) {
|
self.get_token(self.ptr - 1)
|
||||||
Some(token) => Ok(token.to_string()),
|
|
||||||
None => Err("Unexpected EOF, Missing parenthesis?".to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repeatedly calls `read_form` of the reader object until it finds a ")" token
|
/// Repeatedly calls `read_form` of the reader object until it finds a ")" token
|
||||||
@ -50,44 +47,36 @@ impl Reader {
|
|||||||
self.next()?;
|
self.next()?;
|
||||||
|
|
||||||
let mut vector = Vec::new();
|
let mut vector = Vec::new();
|
||||||
loop {
|
|
||||||
let token = self.peek()?;
|
while self.peek()? != terminator {
|
||||||
if token == terminator {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
vector.push(self.read_form()?)
|
vector.push(self.read_form()?)
|
||||||
}
|
}
|
||||||
self.next()?;
|
self.next()?;
|
||||||
|
|
||||||
match terminator {
|
match terminator {
|
||||||
")" => Ok(List(vector)),
|
")" => Ok(List(vector)),
|
||||||
"]" => Ok(Vector(vector)),
|
"]" => Ok(Vector(vector)),
|
||||||
"}" => make_map(vector),
|
"}" => make_map(vector),
|
||||||
_ => Err(format!("Unknown collection terminator: {}", terminator)),
|
_ => Err("Unknown collection terminator".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read atomic token and return appropriate scalar ()
|
/// Read atomic token and return appropriate scalar ()
|
||||||
fn read_atom(&mut self) -> MalRet {
|
fn read_atom(&mut self) -> MalRet {
|
||||||
let token = self.next()?;
|
match &self.next()?[..] {
|
||||||
let re_digits = Regex::new(r"^-?[0-9]+$").unwrap();
|
")" | "]" | "}" => Err("Missing open parenthesis".to_string()),
|
||||||
match token.as_str() {
|
|
||||||
")" | "]" | "}" => Err(format!("Lone parenthesis {}", token)),
|
|
||||||
"false" => Ok(Bool(false)),
|
"false" => Ok(Bool(false)),
|
||||||
"true" => Ok(Bool(true)),
|
"true" => Ok(Bool(true)),
|
||||||
"nil" => Ok(Nil),
|
"nil" => Ok(Nil),
|
||||||
_ => {
|
tk => {
|
||||||
if re_digits.is_match(&token) {
|
if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) {
|
||||||
Ok(Int(token.parse::<isize>().unwrap()))
|
Ok(Int(tk.parse::<isize>().unwrap()))
|
||||||
} else if token.starts_with('\"') {
|
} else if tk.starts_with('\"') {
|
||||||
if token.ends_with('\"') {
|
Ok(Str(map_key(KeyVar::Str, &unescape_str(tk))))
|
||||||
Ok(Str(map_key(KeyVar::Str, &unescape_str(&token))))
|
} else if tk.starts_with(':') {
|
||||||
} else {
|
Ok(Key(map_key(KeyVar::Key, tk)))
|
||||||
Err("Unterminated string, expected \"".to_string())
|
|
||||||
}
|
|
||||||
} else if token.starts_with(':') {
|
|
||||||
Ok(Key(map_key(KeyVar::Key, &token)))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Sym(map_key(KeyVar::Sym, &token)))
|
Ok(Sym(map_key(KeyVar::Sym, tk)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,9 +88,8 @@ impl Reader {
|
|||||||
/// "(" -> call `read_list`
|
/// "(" -> call `read_list`
|
||||||
/// otherwise -> call `read_atom`
|
/// otherwise -> call `read_atom`
|
||||||
fn read_form(&mut self) -> MalRet {
|
fn read_form(&mut self) -> MalRet {
|
||||||
let token = self.peek()?;
|
|
||||||
// String slice containing the whole string
|
// String slice containing the whole string
|
||||||
match &token[..] {
|
match &self.peek()?[..] {
|
||||||
// Consume "(" and parse list
|
// Consume "(" and parse list
|
||||||
"(" => self.read_list(")"),
|
"(" => self.read_list(")"),
|
||||||
"[" => self.read_list("]"),
|
"[" => self.read_list("]"),
|
||||||
@ -114,43 +102,17 @@ impl Reader {
|
|||||||
/// Call `tokenize` on a string
|
/// Call `tokenize` on a string
|
||||||
/// Create anew Reader with the tokens
|
/// Create anew Reader with the tokens
|
||||||
/// Call read_from with the reader instance
|
/// Call read_from with the reader instance
|
||||||
/// TODO: catch errors
|
pub fn read_str(input: &str) -> MalRet {
|
||||||
pub fn read_str(input: &str) -> Result<Vec<MalType>, MalErr> {
|
Reader::new(tokenize(input)).read_form()
|
||||||
let tokens = tokenize(input);
|
|
||||||
match tokens.len() {
|
|
||||||
0 => Ok(vec![Nil]),
|
|
||||||
_ => {
|
|
||||||
let mut reader = Reader::new(tokens);
|
|
||||||
let mut res = Vec::new();
|
|
||||||
while reader.avail() {
|
|
||||||
match reader.read_form() {
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
Ok(any) => res.push(any),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a string and return a list of tokens in it (following regex in README)
|
/// Read a string and return a list of tokens in it (following regex in README)
|
||||||
// Add error handling for strings that are not terminated
|
// Add error handling for strings that are not terminated
|
||||||
fn tokenize(input: &str) -> Vec<String> {
|
fn tokenize(input: &str) -> Vec<String> {
|
||||||
let mut tokens = Vec::new();
|
Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*\n|[^\s\[\]{}('"`,;)]*)"###)
|
||||||
|
.unwrap()
|
||||||
let re = Regex::new(
|
.captures_iter(input)
|
||||||
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*\n|[^\s\[\]{}('"`,;)]*)"###,
|
.map(|e| e[1].to_string())
|
||||||
)
|
.filter(|e| !e.is_empty() || !e.starts_with(';'))
|
||||||
.unwrap();
|
.collect::<Vec<String>>()
|
||||||
for match_str in re.captures_iter(input) {
|
|
||||||
if !match_str[1].is_empty() {
|
|
||||||
// Drop comments
|
|
||||||
if match_str[1].starts_with(';') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tokens.push(match_str[1].to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,11 @@
|
|||||||
|
|
||||||
use crate::printer::pr_str;
|
use crate::printer::pr_str;
|
||||||
use crate::reader::read_str;
|
use crate::reader::read_str;
|
||||||
use crate::types::{MalErr, MalType};
|
use crate::types::{MalRet, MalType};
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
/// Read input and generate an ast
|
/// Read input and generate an ast
|
||||||
fn READ(input: &str) -> Result<Vec<MalType>, MalErr> {
|
fn READ(input: &str) -> MalRet {
|
||||||
match read_str(input) {
|
match read_str(input) {
|
||||||
Ok(ast) => Ok(ast),
|
Ok(ast) => Ok(ast),
|
||||||
Err(err) => Err(format!("@ READ: {}", err)),
|
Err(err) => Err(format!("@ READ: {}", err)),
|
||||||
@ -19,22 +19,18 @@ fn READ(input: &str) -> Result<Vec<MalType>, MalErr> {
|
|||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
/// Evaluate the generated ast
|
/// Evaluate the generated ast
|
||||||
fn EVAL(ast: Vec<MalType>) -> Vec<MalType> {
|
fn EVAL(ast: MalType) -> MalRet {
|
||||||
ast
|
Ok(ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
/// Print out the result of the evaluation
|
/// Print out the result of the evaluation
|
||||||
fn PRINT(input: Vec<MalType>) -> Vec<String> {
|
fn PRINT(output: MalType) -> String {
|
||||||
let mut ret = Vec::new();
|
pr_str(&output, true)
|
||||||
for expr in input {
|
|
||||||
ret.push(pr_str(&expr, true))
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rep(input: &str) -> Result<Vec<String>, MalErr> {
|
pub fn rep(input: &str) -> Result<String, String> {
|
||||||
let ast = READ(input)?;
|
let ast = READ(input)?;
|
||||||
let out = EVAL(ast);
|
let out = EVAL(ast)?;
|
||||||
Ok(PRINT(out))
|
Ok(PRINT(out))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user