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 printer::print;
use reader::read;
use regex::Regex;
use rustyline::{config::Configurer, error::ReadlineError};
pub mod printer;
pub mod reader;
pub mod types;
use types::*;
@ -15,19 +18,19 @@ fn main() -> Result<()> {
match entry {
Ok(line) => {
rl.add_history_entry(line.as_str())?;
eval(line.as_str().into(), env.clone())?;
},
eval(line.as_str(), env.clone())?;
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break
},
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break
},
break;
}
Err(err) => {
println!("Error: {:?}", err);
break
break;
}
}
}
@ -46,20 +49,22 @@ fn read_eval_print(str: &str) -> Result<()> {
Ok(())
}
fn eval(expr: Expression, env: Environment) -> Result<()> {
let regex = Regex::new(r#"^\(([[:ascii:]] .*)\)$"#).unwrap();
let exp = regex.captures_iter(&expr.expr);
for expr in exp {
// 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);
};
}
}
println!("{}", expr.expr);
fn eval(expr: &str, env: Environment) -> Result<()> {
let ast = read(expr);
let expr = print(&ast);
// let regex = Regex::new(r"^\(([[:ascii:]] .*)\)$").unwrap();
// let exp = regex.captures_iter(&expr.expr);
// for expr in exp {
// // 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);
// };
// }
// }
println!("{}", expr);
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 crate::types::Symbol;
use crate::Keyword;
use crate::Value;
use color_eyre::{eyre::eyre, Result};
use lazy_static::lazy_static;
@ -14,68 +15,84 @@ struct Reader {
impl Reader {
fn next(&mut self) -> Option<&String> {
let token = self.position;
self.position += 1;
self.tokens.get(token)
self.tokens.get(self.position - 1)
}
fn peek(&self) -> Option<&String> {
let index = self.position;
self.tokens.get(index)
self.tokens.get(self.position)
}
fn read_form(&mut self) -> Value {
let Some(first) = self.peek() else {
let Some(token) = self.peek() else {
return Value::Nil;
};
let mut value = Value::Nil;
if first.as_str() == "(" {
value = self.read_list();
} else {
if let Some(atom) = self.read_atom() {
value = atom
let val;
match &token[..] {
"(" => {
val = self.read_list(")");
}
};
value
"[" => {
val = self.read_list("]");
}
_ => {
val = self.read_atom().unwrap_or(Value::Nil);
}
}
val
}
fn read_atom(&mut self) -> Option<Value> {
let Some(token) = self.next() else {
return None;
};
if token != "(" {
if token != "(" && token != ")" {
Some(Value::from(token))
} else {
None
}
}
fn read_list(&mut self) -> Value {
fn read_list(&mut self, end: &str) -> Value {
let mut values = vec![];
let length = self.tokens.len();
let position = self.position;
while self.tokens.len() >= position {
if self.peek().unwrap().as_str() == ")" {
self.next();
loop {
let Some(token) = self.peek() else {
return Value::Nil;
};
if token == end {
break;
}
values.push(self.read_form());
}
self.next();
Value::from(values)
}
}
pub fn read(str: &str) -> Value {
let mut reader = tokenize(str);
let value = reader.read_form();
value
let mut values = vec![];
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 {
lazy_static! {
static ref RE: Regex = Regex::new(
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"###
)
.unwrap();
static ref RE: Regex =
Regex::new(r#"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"#)
.unwrap();
}
let mut res = vec![];
@ -85,6 +102,7 @@ fn tokenize(str: &str) -> Reader {
}
res.push(String::from(&cap[1]));
}
// dbg!(&res);
Reader {
tokens: res,
position: 0,
@ -110,15 +128,119 @@ mod test {
String::from("world"),
String::from(")"),
];
// let string = get_test_string();
let reader = tokenize("(hello world)");
// print!("{:?}", reader);
assert_eq!(test_vec, reader.tokens)
}
#[test]
fn test_read() {
let test_values = Value::List(vec![Value::Symbol(Symbol(String::from("hello")))]);
// let values = read(&get_test_string());
// let values = read("(hello world)");
// assert_eq!(test_values, values)
let test_values = Value::List(vec![
Value::List(vec![
Value::Symbol(Symbol(String::from("slide"))),
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]
Nil,
Function,
True,
Symbol(Symbol),
Keyword(Keyword),
}
@ -49,8 +50,14 @@ impl From<&str> for Value {
Value::Number(parse_result)
} else if let Some(s) = s.strip_prefix(":") {
Value::Keyword(Keyword(s.to_string()))
} else if s == "t".to_owned() {
Value::True
} 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() {
Value::Nil
} else {