This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
holy_lisp_archive/src/parser.rs
Apache 4d27cd7ae3
Add bools and instants, add a few functions
Light formatting as well

Signed-off-by: Apache <apache.software.foundation@gmail.com>
2024-06-29 02:32:50 -05:00

379 lines
8.1 KiB
Rust

use std::{collections::HashMap, fmt};
#[cfg(test)]
mod tests {
use crate::{Parser, PreLispValue, Tokenizer, TokenizerError};
fn quick_parse(source: &str) -> Result<Vec<PreLispValue>, TokenizerError> {
let mut tokenizer = Tokenizer::new(String::from(source));
let tokens = tokenizer.tokenize()?;
let mut parser = Parser::new(tokens);
Ok(parser.parse())
}
fn var(name: &str) -> PreLispValue {
PreLispValue::Var(String::from(name))
}
fn string(s: &str) -> PreLispValue {
PreLispValue::String(String::from(s))
}
fn list(v: Vec<PreLispValue>, quoted: bool) -> PreLispValue {
PreLispValue::List(v, quoted)
}
fn int(n: i32) -> PreLispValue {
PreLispValue::Integer(n)
}
fn float(n: f32) -> PreLispValue {
PreLispValue::Float(n)
}
#[test]
fn test_hello_world() -> Result<(), TokenizerError> {
assert_eq!(
vec![list(vec![var("print"), string("Hello, World!")], false)],
quick_parse(
"
(print \"Hello, World!\")
"
)?
);
Ok(())
}
#[test]
fn test_math() -> Result<(), TokenizerError> {
assert_eq!(
vec![list(
vec![
var("print"),
list(
vec![
var("add"),
list(
vec![
var("div"),
float(4.5),
list(vec![var("sub"), float(0.5), float(0.2)], false)
],
false
),
list(vec![var("mul"), int(21), float(0.05)], false)
],
false
)
],
false
)],
quick_parse(
"
(print
(add
(div
4.5
(sub 0.5 0.2)
)
(mul 21 0.05)
)
)
"
)?
);
Ok(())
}
}
use crate::Token;
#[derive(Debug, Clone, PartialEq)]
pub enum PreLispValue {
Nil,
True,
False,
String(String),
Var(String),
Integer(i32),
Float(f32),
List(Vec<PreLispValue>, bool), // Values, Quoted
LispFunction(String, Vec<String>, Vec<PreLispValue>),
}
impl fmt::Display for PreLispValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PreLispValue::Nil => write!(f, "nil"),
PreLispValue::True => write!(f, "true"),
PreLispValue::False => write!(f, "false"),
PreLispValue::String(str) => write!(f, "{}", str),
PreLispValue::LispFunction(name, _, _) => write!(f, "<function {}>", name),
PreLispValue::Integer(num) => write!(f, "{}", num),
PreLispValue::Float(num) => write!(f, "{}", num),
_ => todo!(),
}
}
}
impl From<&str> for PreLispValue {
fn from(value: &str) -> Self {
PreLispValue::String(value.to_string())
}
}
impl From<String> for PreLispValue {
fn from(value: String) -> Self {
PreLispValue::String(value)
}
}
impl From<i32> for PreLispValue {
fn from(value: i32) -> Self {
PreLispValue::Integer(value)
}
}
impl From<f32> for PreLispValue {
fn from(value: f32) -> Self {
PreLispValue::Float(value)
}
}
impl From<Vec<PreLispValue>> for PreLispValue {
fn from(value: Vec<PreLispValue>) -> Self {
PreLispValue::List(value, false)
}
}
// TODO: Handle failure
fn read_exp(open_paren_idx: usize, tokens: &Vec<Token>) -> Option<(Vec<Token>, usize)> /* Option<tokens, close_paren_idx> */
{
let mut tkns = Vec::new();
let mut depth = 0;
for i in open_paren_idx..tokens.len() {
match &tokens[i] {
Token::OpenParen => {
if depth != 0 {
tkns.push(Token::OpenParen);
}
depth += 1;
}
Token::CloseParen => {
depth -= 1;
if depth == 0 {
return Some((tkns, i));
} else {
tkns.push(Token::CloseParen)
}
}
token => {
tkns.push(token.clone());
}
}
}
None
}
fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
let mut values = Vec::new();
if tokens.len() < 1 {
return PreLispValue::List(values, false);
}
let mut quote_next_list = false;
let mut unquote_next_list = false;
let mut i = 0;
while i < tokens.len() {
let tkn = &tokens[i];
match tkn {
Token::Quote => {
quote_next_list = true;
}
Token::Unquote => {
unquote_next_list = true;
}
Token::OpenParen => {
let (tkns, close_paren_idx) = read_exp(i, &tokens).unwrap();
values.push(parse_exp(
tkns,
if unquote_next_list {
false
} else {
quote_next_list || quoted
},
));
i = close_paren_idx;
quote_next_list = false;
unquote_next_list = false;
}
Token::Identifier(name) => match name.as_str() {
"nil" => values.push(PreLispValue::Nil),
"true" => values.push(PreLispValue::True),
"false" => values.push(PreLispValue::False),
_ => values.push(PreLispValue::Var(name.to_owned())),
},
Token::Index(map, idx) => {
values.push(PreLispValue::List(
vec![
PreLispValue::Var(String::from("get")),
PreLispValue::Var(map.to_owned()),
PreLispValue::String(idx.to_owned()),
],
false,
));
}
Token::Integer(num) => {
values.push(PreLispValue::Integer(*num));
}
Token::Float(num) => {
values.push(PreLispValue::Float(*num));
}
Token::String(str) => {
values.push(PreLispValue::String(str.to_owned()));
}
Token::CloseParen => {
panic!("Unexpected closing parenthesis");
}
}
i += 1;
}
PreLispValue::List(values, quoted)
}
pub struct Parser {
tokens: Vec<Token>,
macros: HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
}
fn add_macro(
macros: &mut HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
name: impl Into<String>,
mac: fn(Vec<PreLispValue>) -> Vec<PreLispValue>,
) {
macros.insert(name.into(), mac);
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Parser {
let mut macros = HashMap::new();
add_macro(&mut macros, "set", |x| {
if x.len() != 3
|| (!matches!(x[1], PreLispValue::Var(_))
&& !matches!(x[1], PreLispValue::String(_)))
{
return x;
}
let mut list = x.clone();
if let PreLispValue::Var(name) = &list[1] {
list[1] = PreLispValue::String(name.to_owned())
}
list
});
add_macro(&mut macros, "fn", |x| {
if x.len() < 3 || x.len() > 4 {
return x;
}
let mut name: String = String::new();
let mut args: &Vec<PreLispValue> = &Vec::new();
let mut list: &Vec<PreLispValue> = &Vec::new();
if x.len() == 3 {
if !matches!(x[1], PreLispValue::List(_, _))
|| !matches!(x[2], PreLispValue::List(_, _))
{
return x;
}
name = String::from("Anonymous");
if let PreLispValue::List(l, _) = &x[1] {
args = l;
}
if let PreLispValue::List(l, _) = &x[2] {
list = l;
}
} else if x.len() == 4 {
if !matches!(x[1], PreLispValue::Var(_))
|| !matches!(x[2], PreLispValue::List(_, _))
|| !matches!(x[3], PreLispValue::List(_, _))
{
return x;
}
if let PreLispValue::Var(n) = &x[1] {
name = n.to_owned();
}
if let PreLispValue::List(l, _) = &x[2] {
args = l;
}
if let PreLispValue::List(l, _) = &x[3] {
list = l;
}
} else {
return x;
}
let mut arg_names = Vec::new();
for arg in args {
if let PreLispValue::Var(n) = arg {
arg_names.push(n.to_owned());
}
}
let func = PreLispValue::LispFunction(name.clone(), arg_names, list.to_owned());
if x.len() == 4 {
return vec![PreLispValue::Var(String::from("set")), name.into(), func];
}
return vec![PreLispValue::Var(String::from("echo")), func];
});
Parser { tokens, macros }
}
fn macro_check(&mut self, input: Vec<PreLispValue>, quoted: bool) -> Vec<PreLispValue> {
if quoted || input.len() < 1 {
return input;
}
let mut output = input.clone();
for i in 0..output.len() {
if let PreLispValue::List(elems, quoted) = &output[i] {
output[i] = PreLispValue::List(self.macro_check(elems.to_vec(), *quoted), *quoted);
}
}
if let PreLispValue::Var(name) = &output[0] {
if self.macros.contains_key(name) {
return self.macros.get(name).unwrap()(output);
}
}
output
}
pub fn parse(&mut self) -> Vec<PreLispValue> {
let mut values = Vec::new();
let mut i = 0;
while i < self.tokens.len() {
match &self.tokens[i] {
Token::OpenParen => {
let (tkns, close_paren_idx) = read_exp(i, &self.tokens).unwrap();
values.push(parse_exp(tkns, false));
i = close_paren_idx;
}
tkn => {
// TODO: Include more error data
panic!("Unexpected token {:?}", tkn);
}
}
i += 1;
}
self.macro_check(values, false)
}
}