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

View File

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

View File

@ -1,6 +1,7 @@
// Specyfy components in "types"
use crate::types::*;
// By specifying enum variants it's possible to omit namespace
use crate::types::KeyVar;
use crate::types::MalType::*;
use regex::Regex;
@ -8,7 +9,6 @@ use regex::Regex;
pub struct Reader {
tokens: Vec<String>,
ptr: usize,
depth: usize,
}
// 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
impl Reader {
fn new(tokens: Vec<String>) -> Reader {
Reader {
tokens,
ptr: 0,
depth: 0,
}
Reader { tokens, ptr: 0 }
}
fn avail(&self) -> bool {
self.ptr < self.tokens.len()
}
/// Returns the token at the current position
@ -47,8 +47,6 @@ impl Reader {
/// NOTE: `read_list` calls `read_form` -> enable recursion
/// (lists can contains other lists)
fn read_list(&mut self, terminator: &str) -> MalRet {
self.depth += 1;
self.next()?;
let mut vector = Vec::new();
@ -60,14 +58,12 @@ impl Reader {
vector.push(self.read_form()?)
}
self.next()?;
let ret = match terminator {
match terminator {
")" => Ok(List(vector)),
"]" => Ok(Vector(vector)),
"}" => make_map(vector),
_ => Err(format!("Unknown collection terminator: {}", terminator)),
};
self.depth -= 1;
ret
}
}
/// Read atomic token and return appropriate scalar ()
@ -84,14 +80,14 @@ impl Reader {
Ok(Int(token.parse::<isize>().unwrap()))
} else if token.starts_with('\"') {
if token.ends_with('\"') {
Ok(Str(unescape_str(&token)))
Ok(Str(map_key(KeyVar::Str, &unescape_str(&token))))
} else {
Err("Unterminated string, expected \"".to_string())
}
} else if token.starts_with(':') {
Ok(Keyword(token))
Ok(Key(map_key(KeyVar::Key, &token)))
} else {
Ok(Symbol(token))
Ok(Sym(map_key(KeyVar::Sym, &token)))
}
}
}
@ -119,16 +115,20 @@ impl Reader {
/// Create anew Reader with the tokens
/// Call read_from with the reader instance
/// 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);
match tokens.len() {
0 => Ok(Nil),
0 => Ok(vec![Nil]),
_ => {
let mut reader = Reader::new(tokens);
match reader.read_form() {
Err(err) => Err((err, reader.depth)),
Ok(any) => Ok(any),
let mut res = Vec::new();
while reader.avail() {
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)]
/// 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) {
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)]
/// Evaluate the generated ast
fn EVAL(ast: MalType) -> MalType {
println!("{:#?}", ast);
fn EVAL(ast: Vec<MalType>) -> Vec<MalType> {
ast
}
#[allow(non_snake_case)]
/// Print out the result of the evaluation
fn PRINT(input: MalType) -> String {
pr_str(&input, true)
fn PRINT(input: Vec<MalType>) -> Vec<String> {
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 out = EVAL(ast);
Ok(PRINT(out))

View File

@ -2,15 +2,35 @@
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
#[derive(Debug, Clone)]
pub enum MalType {
List(Vec<MalType>),
Vector(Vec<MalType>),
Map(HashMap<String, MalType>),
Symbol(String),
Keyword(String),
Str(String),
Map(HashMap<KeyType, MalType>),
Sym(KeyType),
Key(KeyType),
Str(KeyType),
Int(isize),
Bool(bool),
Nil,
@ -30,7 +50,7 @@ pub type MalErr = String;
pub type MalArgs = Vec<MalType>;
pub type MalRet = Result<MalType, MalErr>;
use MalType::{Map, Symbol};
use MalType::{Key, Map, Str, Sym};
pub fn make_map(list: MalArgs) -> MalRet {
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) {
match &list[i] {
Symbol(k) => {
Sym(k) | Key(k) | Str(k) => {
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])),
}