mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-13 01:35:31 +01:00
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:
25
src/main.rs
25
src/main.rs
@ -9,35 +9,34 @@ mod step1_read_print;
|
||||
use step1_read_print::rep;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
loop {
|
||||
print!("user> ");
|
||||
// Flush the prompt to appear before command
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
let mut input = String::new();
|
||||
loop {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(" ")
|
||||
),
|
||||
|
||||
@ -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);
|
||||
let mut res = Vec::new();
|
||||
while reader.avail() {
|
||||
match reader.read_form() {
|
||||
Err(err) => Err((err, reader.depth)),
|
||||
Ok(any) => Ok(any),
|
||||
Err(err) => return Err(err),
|
||||
Ok(any) => res.push(any),
|
||||
};
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
34
src/types.rs
34
src/types.rs
@ -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])),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user