refactor: prep for switching to Node in evaluator

Involves the following

1. Node True/False to Boolean
2. Removing parser tests
3. Other stuff
4. This is a bad commit
This commit is contained in:
Roman Godmaire 2024-05-04 08:08:37 -04:00
parent 040a2518cc
commit 4808904571
5 changed files with 222 additions and 119 deletions

9
.gitignore vendored
View file

@ -1 +1,10 @@
/target
# Devenv
.devenv*
devenv.local.nix
# direnv
.direnv
# pre-commit
.pre-commit-config.yaml

156
devenv.lock Normal file
View file

@ -0,0 +1,156 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1714390914,
"owner": "cachix",
"repo": "devenv",
"rev": "34e6461fd76b5f51ad5f8214f5cf22c4cd7a196e",
"treeHash": "5c5dc481030d6e30f8aa506e6d0c0d0809490d65",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"treeHash": "bd263f021e345cb4a39d80c126ab650bebc3c10c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1713361204,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
"treeHash": "50354b35a3e0277d4a83a0a88fa0b0866b5f392f",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1714531828,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0638fe2715d998fa81d173aad264eb671ce2ebc1",
"treeHash": "f72314bde6c77cbdf35f68581c4758f9ff28461f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1714478972,
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "2849da033884f54822af194400f8dff435ada242",
"treeHash": "578180deb59a545b0032e9a66da4c0c043c5057d",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"treeHash": "cce81f2a0f0743b2eb61bc2eb6c7adbe2f2c6beb",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -5,7 +5,7 @@ use super::{eval_ast_node, Error, Expression};
use crate::parser::Node;
pub type RawEnvironment = HashMap<String, Rc<Expression>>;
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Environment {
current: RefCell<RawEnvironment>,
outer: Option<Rc<Environment>>,
@ -104,6 +104,18 @@ pub fn core_environment() -> Rc<Environment> {
"vector",
Expression::NativeFunc(|args| Ok(Rc::new(Expression::Vector(args)))),
),
(
"vector?",
Expression::NativeFunc(|args| {
arg_count!(1, args.len());
if let Expression::Vector(_vec) = args[0].borrow() {
return Ok(Expression::Boolean(true).into());
}
Ok(Expression::Boolean(false).into())
}),
),
(
"hashmap",
Expression::NativeFunc(|args| {
@ -127,23 +139,48 @@ pub fn core_environment() -> Rc<Environment> {
}),
),
(
"eq?",
"hashmap?",
Expression::NativeFunc(|args| {
arg_count!(2, args.len());
arg_count!(1, args.len());
if args[0] == args[1] {
if let Expression::HashMap(_map) = args[0].borrow() {
return Ok(Expression::Boolean(true).into());
}
Ok(Expression::Boolean(false).into())
}),
),
// Ordering
(
"eq?",
Expression::NativeFunc(|args| {
arg_count!(2, args.len());
let lhs: &Expression = args[0].borrow();
let rhs: &Expression = args[1].borrow();
match (lhs, rhs) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Ok(Expression::Boolean(lhs == rhs).into())
}
(Expression::Boolean(lhs), Expression::Boolean(rhs)) => {
Ok(Expression::Boolean(lhs == rhs).into())
}
(Expression::String(lhs), Expression::String(rhs)) => {
Ok(Expression::Boolean(lhs == rhs).into())
}
(Expression::Nil, Expression::Nil) => Ok(Expression::Boolean(true).into()),
_ => Err(Error::ExpectedNumber)?,
}
}),
),
(
"not",
Expression::NativeFunc(|args| {
arg_count!(1, args.len());
if *args[0] == Expression::Boolean(false) {
let expr: &Expression = args[0].borrow();
if let Expression::Boolean(false) = expr {
return Ok(Expression::Boolean(true).into());
}
@ -212,7 +249,7 @@ pub fn core_environment() -> Rc<Environment> {
(args[0].clone(), args[1].clone(), args[2].clone());
// If the value is anything other than false, then it is truthy
if *cond == Expression::Boolean(false) {
if let Expression::Boolean(false) = cond.borrow() {
return Ok(alternative);
};

View file

@ -10,7 +10,7 @@ mod macros;
pub use env::{core_environment, Environment};
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum Expression {
// Values
Int(i64),
@ -163,9 +163,12 @@ fn eval_ast_node(env: Rc<Environment>, ast_node: Node) -> Result<Rc<Expression>>
Node::Keyword(val) => Expression::Keyword(val).into(),
Node::String(s) => Expression::String(s).into(),
Node::True => Expression::Boolean(true).into(),
Node::False => Expression::Boolean(false).into(),
Node::Boolean(true) => Expression::Boolean(true).into(),
Node::Boolean(false) => Expression::Boolean(false).into(),
Node::Nil => Expression::Nil.into(),
Node::Vector(vec) => todo!(),
Node::Map(map) => todo!(),
};
Ok(expr)

View file

@ -1,10 +1,10 @@
use std::{iter::Peekable, vec::IntoIter};
use std::{collections::HashMap, iter::Peekable, vec::IntoIter};
use anyhow::{bail, Result};
use crate::lexer::Token;
#[derive(Debug, PartialEq, PartialOrd, Clone)]
#[derive(Debug, Clone)]
pub enum Node {
List(Vec<Node>),
@ -12,9 +12,11 @@ pub enum Node {
Keyword(String),
Int(i64),
String(String),
True,
False,
Boolean(bool),
Nil,
Vector(Vec<Node>),
Map(HashMap<String, Node>),
}
pub fn parse(tokens: Vec<Token>) -> Result<Vec<Node>> {
@ -68,8 +70,8 @@ fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>
Token::Ident(val) => Node::Symbol(val),
Token::String(val) => Node::String(val),
Token::Int(int) => Node::Int(int),
Token::True => Node::True,
Token::False => Node::False,
Token::True => Node::Boolean(true),
Token::False => Node::Boolean(false),
Token::Nil => Node::Nil,
};
@ -120,107 +122,3 @@ fn read_quote(tokens: &mut Peekable<IntoIter<Token>>, quote_type: &str) -> Resul
follower_node,
]))
}
#[cfg(test)]
mod test {
use crate::lexer;
use super::*;
use rstest::rstest;
#[rstest]
#[case("10", vec![
Node::Int(10)])]
#[case(":owo", vec![
Node::Keyword("owo".into())])]
#[case("\"uwu\"", vec![
Node::String("uwu".into())])]
#[case("(10 2)", vec![
Node::List(vec![
Node::Int(10),
Node::Int(2)])])]
#[case("[10 2]", vec![
Node::List(vec![
Node::Symbol("vector".into()),
Node::Int(10),
Node::Int(2)])])]
#[case("{10 2}", vec![
Node::List(vec![
Node::Symbol("hashmap".into()),
Node::Int(10),
Node::Int(2)])])]
#[case("(+ - * /)", vec![
Node::List(vec![
Node::Symbol("+".into()),
Node::Symbol("-".into()),
Node::Symbol("*".into()),
Node::Symbol("/".into())])])]
#[case("'(1 2 3)", vec![
Node::List(vec![
Node::Symbol("quote".into()),
Node::List(vec![
Node::Int(1),
Node::Int(2),
Node::Int(3)])])])]
#[case("`(1 2 3)", vec![
Node::List(vec![
Node::Symbol("quasiquote".into()),
Node::List(vec![
Node::Int(1),
Node::Int(2),
Node::Int(3)])])])]
#[case("~(1 2 3)", vec![
Node::List(vec![
Node::Symbol("unquote".into()),
Node::List(vec![
Node::Int(1),
Node::Int(2),
Node::Int(3)])])])]
#[case("~@(1 2 3)", vec![
Node::List(vec![
Node::Symbol("splice-unquote".into()),
Node::List(vec![
Node::Int(1),
Node::Int(2),
Node::Int(3)])])])]
#[case("(+ 1 2)", vec![
Node::List(vec![
Node::Symbol("+".into()),
Node::Int(1),
Node::Int(2)])])]
#[case("(+ 1 2 (- 1 2))", vec![
Node::List(vec![
Node::Symbol("+".into()),
Node::Int(1),
Node::Int(2),
Node::List(vec![
Node::Symbol("-".into()),
Node::Int(1),
Node::Int(2)])])])]
fn test_parsing(#[case] input: &str, #[case] expected: Vec<Node>) {
let tokens = lexer::read(input).unwrap();
let res = parse(tokens).unwrap();
assert_eq!(res, expected);
}
#[rstest]
#[case(")")]
#[case("]")]
#[case("}")]
#[case("(1 2")]
#[case("[1 2")]
#[case("{1 2")]
#[case("(1 2 '")]
#[case("(1 2 ')")]
#[case("(1 2 ~")]
#[case("(1 2 ~)")]
#[case("(1 2 `")]
#[case("(1 2 `)")]
#[case("(1 2 ~@")]
#[case("(1 2 ~@)")]
fn test_parsing_fail(#[case] input: &str) {
let tokens = lexer::read(input).unwrap();
let res = parse(tokens);
assert!(res.is_err());
}
}