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:
29
src/main.rs
29
src/main.rs
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(" ")
|
||||||
),
|
),
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
34
src/types.rs
34
src/types.rs
@ -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])),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user