tlisp 0.0.2

Lisp interpreter in Rust
Documentation
use crate::lexer::*;
use crate::object::Object;

use std::error::Error;
use std::fmt;
use std::rc::Rc;

#[derive(Debug)]
pub struct ParseError {
  err: String,
}

impl fmt::Display for ParseError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Parse error: {}", self.err)
  }
}

impl Error for ParseError {}

fn token_to_object(t: Token) -> Result<Object, ParseError> {
  let object = match t {
    Token::Integer(n) => Object::Integer(n),
    Token::Float(f) => Object::Float(f),
    Token::String(s) => Object::String(s),
    Token::Symbol(word) => match word.as_str() {
      "define" | "defun" | "lambda" | "let" | "do" => Object::Keyword(word),
      "+" | "-" | "*" | "/" | "<" | ">" | "=" | "==" | "%" | "or" | "and" => Object::Operator(word),
      "cond" => Object::Cond,
      _ => Object::Symbol(word),
    },
    _ => {
      return Err(ParseError {
        err: format!("Unexpected token: {:?}", t),
      })
    }
  };

  return Ok(object);
}

fn parse_list(tokens: &mut Vec<Token>) -> Result<Object, ParseError> {
  let mut list: Vec<Object> = Vec::new();

  while !tokens.is_empty() {
    let token = tokens.pop();
    if token == None {
      return Err(ParseError {
        err: format!("Insufficient tokens"),
      });
    }

    let token = token.unwrap();

    match token {
      Token::LParen => {
        let sub_list = parse_list(tokens)?;
        list.push(sub_list);
      }
      Token::RParen => {
        return Ok(Object::List(list));
      }
      Token::Quote => {
        let quoted_token = tokens.pop();
        if quoted_token == None {
          return Err(ParseError {
            err: format!("Insufficient tokens"),
          });
        }

        let mut quoted_token = quoted_token.unwrap();

        let mut quote_count = 1;

        while let Token::Quote = quoted_token {
          quote_count += 1;
          quoted_token = tokens.pop().unwrap();
        }

        let object = match quoted_token {
          Token::LParen => {
            let mut paren_count = 1;
            let mut quoted_tokens = vec![];

            while paren_count > 0 {
              let token = tokens.pop();
              if token == None {
                return Err(ParseError {
                  err: format!("Insufficient tokens"),
                });
              }

              let token = token.unwrap();
              quoted_tokens.insert(0, token.clone());

              match token {
                Token::LParen => paren_count += 1,
                Token::RParen => paren_count -= 1,
                _ => (),
              }
            }

            parse_list(&mut quoted_tokens)
          }
          Token::RParen => {
            return Err(ParseError {
              err: format!("Unexpected RParen after quote"),
            });
          }
          token => token_to_object(token.clone()),
        };

        let mut quoted_object: Object = object?;
        for _ in 0..quote_count {
          quoted_object = Object::Quote(Rc::new(quoted_object));
        }

        list.push(quoted_object);
      }
      token => {
        let object = token_to_object(token.clone())?;

        list.push(object);
      }
    }
  }

  match list.len() {
    0 => Ok(Object::List(Vec::new())),
    1 => Ok(list[0].clone()),
    _ => Ok(Object::List(list)),
  }
}

pub fn parse(program: &str) -> Result<Object, ParseError> {
  let token_result = tokenize(program);

  let mut tokens = token_result
    .unwrap()
    .into_iter()
    .rev()
    .collect::<Vec<Token>>();
  let parsed_list = parse_list(&mut tokens)?;
  Ok(parsed_list)
}

#[cfg(test)]
mod lexer_tests {
  use super::*;

  #[test]
  fn test_add() {
    let list = parse("(+ 1 2)").unwrap();
    assert_eq!(
      list,
      Object::List(vec![
        Object::Operator("+".to_string()),
        Object::Integer(1),
        Object::Integer(2),
      ])
    );
  }

  #[test]
  fn test_symbol() {
    let list = parse("#t").unwrap();
    assert_eq!(list, Object::Symbol("#t".to_string()))
  }

  #[test]
  fn test_quotation() {
    let list = parse("'(1 2 3)").unwrap();
    assert_eq!(
      list,
      Object::Quote(Rc::new(Object::List(vec![
        Object::Integer(1),
        Object::Integer(2),
        Object::Integer(3),
      ])))
    )
  }

  #[test]
  fn test_multi_quotation() {
    let list = parse("''a").unwrap();

    assert_eq!(
      list,
      Object::Quote(Rc::new(Object::Quote(Rc::new(Object::Symbol(
        "a".to_string()
      )))))
    )
  }
}