Oneline multi expression

Multiple expressions can be evaluated on a single line

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-06-07 14:41:12 +02:00
parent 9d35d24cd4
commit 54d196582f
5 changed files with 75 additions and 53 deletions

View File

@ -10,34 +10,33 @@ use step1_read_print::rep;
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
loop { loop {
print!("user> ");
// Flush the prompt to appear before command
let _ = io::stdout().flush();
let mut input = String::new(); let mut input = String::new();
loop { loop {
print!("user> ");
// Flush the prompt to appear before command
let _ = io::stdout().flush();
// Read line to compose program inpug // Read line to compose program inpug
let mut line = String::new(); let mut line = String::new();
io::stdin().read_line(&mut line)?; io::stdin().read_line(&mut line)?;
// Append line to input if line == "\n" {
input.push_str(&line); break;
// If there is nothing to evaluate skip rep
if input == "\n" {
continue;
} }
input.push_str(&line);
// Perform rep on whole available input // Perform rep on whole available input
match rep(&input) { match rep(&input) {
Ok(output) => println!("{}", output), Ok(output) => {
Err((err, depth)) => { for el in output {
println!("{}", el);
}
}
Err(err) => {
if line == "\n" { if line == "\n" {
println!("ERROR: {}", err); println!("ERROR: {}", err);
} else { } else {
print!("user> {}", " ".repeat(depth));
// Flush the prompt to appear before command
let _ = io::stdout().flush();
continue; continue;
} }
} }

View File

@ -5,14 +5,14 @@ use crate::types::MalType::*;
pub fn pr_str(ast: &MalType, print_readably: bool) -> String { pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
match ast { match ast {
Nil => "nil".to_string(), Nil => "nil".to_string(),
Symbol(sym) | Keyword(sym) => sym.to_string(), Sym(sym) | Key(sym) => sym.val.to_string(),
Int(val) => val.to_string(), Int(val) => val.to_string(),
Bool(val) => val.to_string(), Bool(val) => val.to_string(),
Str(str) => { Str(str) => {
if print_readably { if print_readably {
escape_str(str) escape_str(&str.val)
} else { } else {
str.to_string() str.val.to_string()
} }
} }
List(el) => format!( List(el) => format!(
@ -33,7 +33,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
Map(el) => format!( Map(el) => format!(
"{{{}}}", "{{{}}}",
el.iter() el.iter()
.map(|sub| vec![sub.0.to_string(), pr_str(sub.1, print_readably)].join(" ")) .map(|sub| vec![sub.0.val.to_string(), pr_str(sub.1, print_readably)].join(" "))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" ") .join(" ")
), ),

View File

@ -1,6 +1,7 @@
// Specyfy components in "types" // Specyfy components in "types"
use crate::types::*; use crate::types::*;
// By specifying enum variants it's possible to omit namespace // By specifying enum variants it's possible to omit namespace
use crate::types::KeyVar;
use crate::types::MalType::*; use crate::types::MalType::*;
use regex::Regex; use regex::Regex;
@ -8,7 +9,6 @@ use regex::Regex;
pub struct Reader { pub struct Reader {
tokens: Vec<String>, tokens: Vec<String>,
ptr: usize, ptr: usize,
depth: usize,
} }
// TODO: instead of panic on missing ")" try implementing a multi line parsing // TODO: instead of panic on missing ")" try implementing a multi line parsing
@ -16,11 +16,11 @@ pub struct Reader {
// (append to the "last" list) while traversing // (append to the "last" list) while traversing
impl Reader { impl Reader {
fn new(tokens: Vec<String>) -> Reader { fn new(tokens: Vec<String>) -> Reader {
Reader { Reader { tokens, ptr: 0 }
tokens, }
ptr: 0,
depth: 0, fn avail(&self) -> bool {
} self.ptr < self.tokens.len()
} }
/// Returns the token at the current position /// Returns the token at the current position
@ -47,8 +47,6 @@ impl Reader {
/// NOTE: `read_list` calls `read_form` -> enable recursion /// NOTE: `read_list` calls `read_form` -> enable recursion
/// (lists can contains other lists) /// (lists can contains other lists)
fn read_list(&mut self, terminator: &str) -> MalRet { fn read_list(&mut self, terminator: &str) -> MalRet {
self.depth += 1;
self.next()?; self.next()?;
let mut vector = Vec::new(); let mut vector = Vec::new();
@ -60,14 +58,12 @@ impl Reader {
vector.push(self.read_form()?) vector.push(self.read_form()?)
} }
self.next()?; self.next()?;
let ret = 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(format!("Unknown collection terminator: {}", terminator)),
}; }
self.depth -= 1;
ret
} }
/// Read atomic token and return appropriate scalar () /// Read atomic token and return appropriate scalar ()
@ -84,14 +80,14 @@ impl Reader {
Ok(Int(token.parse::<isize>().unwrap())) Ok(Int(token.parse::<isize>().unwrap()))
} else if token.starts_with('\"') { } else if token.starts_with('\"') {
if token.ends_with('\"') { if token.ends_with('\"') {
Ok(Str(unescape_str(&token))) Ok(Str(map_key(KeyVar::Str, &unescape_str(&token))))
} else { } else {
Err("Unterminated string, expected \"".to_string()) Err("Unterminated string, expected \"".to_string())
} }
} else if token.starts_with(':') { } else if token.starts_with(':') {
Ok(Keyword(token)) Ok(Key(map_key(KeyVar::Key, &token)))
} else { } else {
Ok(Symbol(token)) Ok(Sym(map_key(KeyVar::Sym, &token)))
} }
} }
} }
@ -119,16 +115,20 @@ impl Reader {
/// 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 /// TODO: catch errors
pub fn read_str(input: &str) -> Result<MalType, (MalErr, usize)> { pub fn read_str(input: &str) -> Result<Vec<MalType>, MalErr> {
let tokens = tokenize(input); let tokens = tokenize(input);
match tokens.len() { match tokens.len() {
0 => Ok(Nil), 0 => Ok(vec![Nil]),
_ => { _ => {
let mut reader = Reader::new(tokens); let mut reader = Reader::new(tokens);
match reader.read_form() { let mut res = Vec::new();
Err(err) => Err((err, reader.depth)), while reader.avail() {
Ok(any) => Ok(any), match reader.read_form() {
Err(err) => return Err(err),
Ok(any) => res.push(any),
};
} }
Ok(res)
} }
} }
} }

View File

@ -10,27 +10,30 @@ use crate::types::{MalErr, 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<MalType, (MalErr, usize)> { fn READ(input: &str) -> Result<Vec<MalType>, MalErr> {
match read_str(input) { match read_str(input) {
Ok(ast) => Ok(ast), Ok(ast) => Ok(ast),
Err((err, depth)) => Err((format!("Unexpected error during READ: {}", err), depth)), Err(err) => Err(format!("Unexpected error during READ: {}", err)),
} }
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
/// Evaluate the generated ast /// Evaluate the generated ast
fn EVAL(ast: MalType) -> MalType { fn EVAL(ast: Vec<MalType>) -> Vec<MalType> {
println!("{:#?}", ast);
ast 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: MalType) -> String { fn PRINT(input: Vec<MalType>) -> Vec<String> {
pr_str(&input, true) let mut ret = Vec::new();
for expr in input {
ret.push(pr_str(&expr, true))
}
ret
} }
pub fn rep(input: &str) -> Result<String, (MalErr, usize)> { pub fn rep(input: &str) -> Result<Vec<String>, MalErr> {
let ast = READ(input)?; let ast = READ(input)?;
let out = EVAL(ast); let out = EVAL(ast);
Ok(PRINT(out)) Ok(PRINT(out))

View File

@ -2,15 +2,35 @@
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum KeyVar {
Key,
Sym,
Str,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct KeyType {
pub val: String,
var: KeyVar,
}
pub fn map_key(var: KeyVar, val: &str) -> KeyType {
KeyType {
val: val.to_string(),
var,
}
}
// All Mal types should inherit from this // All Mal types should inherit from this
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum MalType { pub enum MalType {
List(Vec<MalType>), List(Vec<MalType>),
Vector(Vec<MalType>), Vector(Vec<MalType>),
Map(HashMap<String, MalType>), Map(HashMap<KeyType, MalType>),
Symbol(String), Sym(KeyType),
Keyword(String), Key(KeyType),
Str(String), Str(KeyType),
Int(isize), Int(isize),
Bool(bool), Bool(bool),
Nil, Nil,
@ -30,7 +50,7 @@ pub type MalErr = String;
pub type MalArgs = Vec<MalType>; pub type MalArgs = Vec<MalType>;
pub type MalRet = Result<MalType, MalErr>; pub type MalRet = Result<MalType, MalErr>;
use MalType::{Map, Symbol}; use MalType::{Key, Map, Str, Sym};
pub fn make_map(list: MalArgs) -> MalRet { pub fn make_map(list: MalArgs) -> MalRet {
if list.len() % 2 != 0 { if list.len() % 2 != 0 {
@ -41,9 +61,9 @@ pub fn make_map(list: MalArgs) -> MalRet {
for i in (0..list.len()).step_by(2) { for i in (0..list.len()).step_by(2) {
match &list[i] { match &list[i] {
Symbol(k) => { Sym(k) | Key(k) | Str(k) => {
let v = list[i + 1].clone(); let v = list[i + 1].clone();
map.insert(k.to_string(), v); map.insert(k.clone(), v);
} }
_ => return Err(format!("Map key not valid: {:?}", list[i])), _ => return Err(format!("Map key not valid: {:?}", list[i])),
} }