Basic read and print are working perfectly

This commit is contained in:
Chris Cochrun 2024-11-05 17:29:34 -06:00
parent 25a451e23e
commit f7f90cd78f
4 changed files with 238 additions and 52 deletions

View file

@ -1,7 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use printer::print;
use reader::read;
use regex::Regex; use regex::Regex;
use rustyline::{config::Configurer, error::ReadlineError}; use rustyline::{config::Configurer, error::ReadlineError};
pub mod printer;
pub mod reader; pub mod reader;
pub mod types; pub mod types;
use types::*; use types::*;
@ -15,19 +18,19 @@ fn main() -> Result<()> {
match entry { match entry {
Ok(line) => { Ok(line) => {
rl.add_history_entry(line.as_str())?; rl.add_history_entry(line.as_str())?;
eval(line.as_str().into(), env.clone())?; eval(line.as_str(), env.clone())?;
}, }
Err(ReadlineError::Interrupted) => { Err(ReadlineError::Interrupted) => {
println!("CTRL-C"); println!("CTRL-C");
break break;
}, }
Err(ReadlineError::Eof) => { Err(ReadlineError::Eof) => {
println!("CTRL-D"); println!("CTRL-D");
break break;
}, }
Err(err) => { Err(err) => {
println!("Error: {:?}", err); println!("Error: {:?}", err);
break break;
} }
} }
} }
@ -46,20 +49,22 @@ fn read_eval_print(str: &str) -> Result<()> {
Ok(()) Ok(())
} }
fn eval(expr: Expression, env: Environment) -> Result<()> { fn eval(expr: &str, env: Environment) -> Result<()> {
let regex = Regex::new(r#"^\(([[:ascii:]] .*)\)$"#).unwrap(); let ast = read(expr);
let exp = regex.captures_iter(&expr.expr); let expr = print(&ast);
for expr in exp { // let regex = Regex::new(r"^\(([[:ascii:]] .*)\)$").unwrap();
// let regex = Regex::new(r#"^(\w)$"#).unwrap(); // let exp = regex.captures_iter(&expr.expr);
// let mut atom = regex.captures_iter(atom?.get(0).unwrap().as_str()); // for expr in exp {
// for atom in atom { // // let regex = Regex::new(r#"^(\w)$"#).unwrap();
// // let mut atom = regex.captures_iter(atom?.get(0).unwrap().as_str());
// // for atom in atom {
// // }
// for atom in expr.iter() {
// if let Some(atom) = atom {
// println!("{:?}", atom);
// };
// } // }
for atom in expr.iter() { // }
if let Some(atom) = atom { println!("{}", expr);
println!("{:?}", atom);
};
}
}
println!("{}", expr.expr);
Ok(()) Ok(())
} }

52
src/printer.rs Normal file
View file

@ -0,0 +1,52 @@
use crate::Value;
pub fn print(value: &Value) -> String {
match value {
Value::List(v) => {
let strings: Vec<String> = v.iter().map(print).collect();
let mut string = strings.join(" ");
if v.len() < 2 {
return string;
}
let end = string.len() + 1;
string.insert_str(0, "(");
string.insert_str(end, ")");
// dbg!(&string);
string
}
Value::String(s) => {
let mut s = s.clone();
let length = s.len() + 1;
s.insert(0, '"');
s.insert(length, '"');
s.to_string()
}
Value::Number(n) => n.to_string(),
Value::Nil => "nil".to_string(),
Value::Function => "fn".to_string(),
Value::True => "t".to_string(),
Value::Symbol(s) => s.0.clone(),
Value::Keyword(k) => {
let mut string = k.0.clone();
string.insert_str(0, ":");
string
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::reader::read;
use pretty_assertions::assert_eq;
use std::fs::read_to_string;
#[test]
fn test_print() {
let string = read_to_string("./test_presentation.lisp").unwrap();
let values = read(&string);
let string = r#"((slide :background (image :source "~/pics/frodo.jpg" :fit crop) (text "This is frodo" :font-size 50)) (slide (video :source "~/vids/test/chosensmol.mp4" :fit fill)) (song :author "Jordan Feliz" :ccli 97987 :font "Quicksand" :font-size 80 :title "The River" :background (image :source "./coolbg.jpg") (text "I'm going down to the river") (text "Down to the river") (text "Down to the river to pray ay ay!")) (song :author "Jordan Feliz" :ccli 97987 :font "Quicksand" :font-size 80 :title "The River" :background (video :source "./coolerbg.mkv" :fit cover) :verse-order (v1 c1 v2 c1) (v1 "I'm going down to the river") (c1 "Down to the river") (v2 "Down to the river to pray ay ay!")) (load "./10000-reasons.lisp"))"#.to_string();
let print = print(&values);
assert_eq!(print, string)
}
}

View file

@ -1,6 +1,7 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::types::Symbol; use crate::types::Symbol;
use crate::Keyword;
use crate::Value; use crate::Value;
use color_eyre::{eyre::eyre, Result}; use color_eyre::{eyre::eyre, Result};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -14,67 +15,83 @@ struct Reader {
impl Reader { impl Reader {
fn next(&mut self) -> Option<&String> { fn next(&mut self) -> Option<&String> {
let token = self.position;
self.position += 1; self.position += 1;
self.tokens.get(token) self.tokens.get(self.position - 1)
} }
fn peek(&self) -> Option<&String> { fn peek(&self) -> Option<&String> {
let index = self.position; self.tokens.get(self.position)
self.tokens.get(index)
} }
fn read_form(&mut self) -> Value { fn read_form(&mut self) -> Value {
let Some(first) = self.peek() else { let Some(token) = self.peek() else {
return Value::Nil; return Value::Nil;
}; };
let mut value = Value::Nil; let val;
if first.as_str() == "(" { match &token[..] {
value = self.read_list(); "(" => {
} else { val = self.read_list(")");
if let Some(atom) = self.read_atom() {
value = atom
} }
}; "[" => {
value val = self.read_list("]");
}
_ => {
val = self.read_atom().unwrap_or(Value::Nil);
}
}
val
} }
fn read_atom(&mut self) -> Option<Value> { fn read_atom(&mut self) -> Option<Value> {
let Some(token) = self.next() else { let Some(token) = self.next() else {
return None; return None;
}; };
if token != "(" { if token != "(" && token != ")" {
Some(Value::from(token)) Some(Value::from(token))
} else { } else {
None None
} }
} }
fn read_list(&mut self) -> Value { fn read_list(&mut self, end: &str) -> Value {
let mut values = vec![]; let mut values = vec![];
let length = self.tokens.len(); self.next();
let position = self.position; loop {
while self.tokens.len() >= position { let Some(token) = self.peek() else {
if self.peek().unwrap().as_str() == ")" { return Value::Nil;
};
if token == end {
break; break;
} }
values.push(self.read_form()); values.push(self.read_form());
} }
self.next();
Value::from(values) Value::from(values)
} }
} }
pub fn read(str: &str) -> Value { pub fn read(str: &str) -> Value {
let mut reader = tokenize(str); let mut reader = tokenize(str);
let value = reader.read_form(); let mut values = vec![];
value loop {
let length = reader.tokens.len();
if reader.position <= length {
if reader.peek().is_some_and(|s| &s[..] == "(") {
values.push(reader.read_form());
} else {
reader.next();
}
} else {
break;
}
}
Value::from(values)
} }
fn tokenize(str: &str) -> Reader { fn tokenize(str: &str) -> Reader {
lazy_static! { lazy_static! {
static ref RE: Regex = Regex::new( static ref RE: Regex =
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"### Regex::new(r#"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"#)
)
.unwrap(); .unwrap();
} }
@ -85,6 +102,7 @@ fn tokenize(str: &str) -> Reader {
} }
res.push(String::from(&cap[1])); res.push(String::from(&cap[1]));
} }
// dbg!(&res);
Reader { Reader {
tokens: res, tokens: res,
position: 0, position: 0,
@ -110,15 +128,119 @@ mod test {
String::from("world"), String::from("world"),
String::from(")"), String::from(")"),
]; ];
// let string = get_test_string();
let reader = tokenize("(hello world)"); let reader = tokenize("(hello world)");
// print!("{:?}", reader);
assert_eq!(test_vec, reader.tokens) assert_eq!(test_vec, reader.tokens)
} }
#[test] #[test]
fn test_read() { fn test_read() {
let test_values = Value::List(vec![Value::Symbol(Symbol(String::from("hello")))]); let test_values = Value::List(vec![
// let values = read(&get_test_string()); Value::List(vec![
// let values = read("(hello world)"); Value::Symbol(Symbol(String::from("slide"))),
// assert_eq!(test_values, values) Value::Keyword(Keyword(String::from("background"))),
Value::List(vec![
Value::Symbol(Symbol(String::from("image"))),
Value::Keyword(Keyword(String::from("source"))),
Value::String(String::from("~/pics/frodo.jpg")),
Value::Keyword(Keyword(String::from("fit"))),
Value::Symbol(Symbol(String::from("crop"))),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("text"))),
Value::String(String::from("This is frodo")),
Value::Keyword(Keyword(String::from("font-size"))),
Value::Number(50),
]),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("slide"))),
Value::List(vec![
Value::Symbol(Symbol(String::from("video"))),
Value::Keyword(Keyword(String::from("source"))),
Value::String(String::from("~/vids/test/chosensmol.mp4")),
Value::Keyword(Keyword(String::from("fit"))),
Value::Symbol(Symbol(String::from("fill"))),
]),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("song"))),
Value::Keyword(Keyword(String::from("author"))),
Value::String(String::from("Jordan Feliz")),
Value::Keyword(Keyword(String::from("ccli"))),
Value::Number(97987),
Value::Keyword(Keyword(String::from("font"))),
Value::String(String::from("Quicksand")),
Value::Keyword(Keyword(String::from("font-size"))),
Value::Number(80),
Value::Keyword(Keyword(String::from("title"))),
Value::String(String::from("The River")),
Value::Keyword(Keyword(String::from("background"))),
Value::List(vec![
Value::Symbol(Symbol(String::from("image"))),
Value::Keyword(Keyword(String::from("source"))),
Value::String(String::from("./coolbg.jpg")),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("text"))),
Value::String(String::from("I'm going down to the river")),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("text"))),
Value::String(String::from("Down to the river")),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("text"))),
Value::String(String::from("Down to the river to pray ay ay!")),
]),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("song"))),
Value::Keyword(Keyword(String::from("author"))),
Value::String(String::from("Jordan Feliz")),
Value::Keyword(Keyword(String::from("ccli"))),
Value::Number(97987),
Value::Keyword(Keyword(String::from("font"))),
Value::String(String::from("Quicksand")),
Value::Keyword(Keyword(String::from("font-size"))),
Value::Number(80),
Value::Keyword(Keyword(String::from("title"))),
Value::String(String::from("The River")),
Value::Keyword(Keyword(String::from("background"))),
Value::List(vec![
Value::Symbol(Symbol(String::from("video"))),
Value::Keyword(Keyword(String::from("source"))),
Value::String(String::from("./coolerbg.mkv")),
Value::Keyword(Keyword(String::from("fit"))),
Value::Symbol(Symbol(String::from("cover"))),
]),
Value::Keyword(Keyword(String::from("verse-order"))),
Value::List(vec![
Value::Symbol(Symbol(String::from("v1"))),
Value::Symbol(Symbol(String::from("c1"))),
Value::Symbol(Symbol(String::from("v2"))),
Value::Symbol(Symbol(String::from("c1"))),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("v1"))),
Value::String(String::from("I'm going down to the river")),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("c1"))),
Value::String(String::from("Down to the river")),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("v2"))),
Value::String(String::from("Down to the river to pray ay ay!")),
]),
]),
Value::List(vec![
Value::Symbol(Symbol(String::from("load"))),
Value::String(String::from("./10000-reasons.lisp")),
]),
]);
let values = read(&get_test_string());
assert_eq!(test_values, values)
} }
} }

View file

@ -23,6 +23,7 @@ pub enum Value {
#[default] #[default]
Nil, Nil,
Function, Function,
True,
Symbol(Symbol), Symbol(Symbol),
Keyword(Keyword), Keyword(Keyword),
} }
@ -49,8 +50,14 @@ impl From<&str> for Value {
Value::Number(parse_result) Value::Number(parse_result)
} else if let Some(s) = s.strip_prefix(":") { } else if let Some(s) = s.strip_prefix(":") {
Value::Keyword(Keyword(s.to_string())) Value::Keyword(Keyword(s.to_string()))
} else if s == "t".to_owned() {
Value::True
} else if s.starts_with(r#"""#) { } else if s.starts_with(r#"""#) {
Value::String(s) Value::String(s.replace('"', ""))
} else if s == "fn".to_owned() {
Value::Function
} else if s == "defun".to_owned() {
Value::Function
} else if s == "nil".to_owned() { } else if s == "nil".to_owned() {
Value::Nil Value::Nil
} else { } else {