From 4808904571895cd52836be923ba8688e1ca81947 Mon Sep 17 00:00:00 2001 From: Roman Godmaire Date: Sat, 4 May 2024 08:08:37 -0400 Subject: [PATCH] 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 --- .gitignore | 9 +++ devenv.lock | 156 +++++++++++++++++++++++++++++++++++++++++++ src/evaluator/env.rs | 49 ++++++++++++-- src/evaluator/mod.rs | 9 ++- src/parser.rs | 118 +++----------------------------- 5 files changed, 222 insertions(+), 119 deletions(-) create mode 100644 devenv.lock diff --git a/.gitignore b/.gitignore index ea8c4bf..54bb18f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ /target +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..2ce0ea4 --- /dev/null +++ b/devenv.lock @@ -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 +} diff --git a/src/evaluator/env.rs b/src/evaluator/env.rs index 25b14d9..4a60d99 100644 --- a/src/evaluator/env.rs +++ b/src/evaluator/env.rs @@ -5,7 +5,7 @@ use super::{eval_ast_node, Error, Expression}; use crate::parser::Node; pub type RawEnvironment = HashMap>; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct Environment { current: RefCell, outer: Option>, @@ -104,6 +104,18 @@ pub fn core_environment() -> Rc { "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 { }), ), ( - "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 { (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); }; diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 43dad4d..730807a 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -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, ast_node: Node) -> Result> 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) diff --git a/src/parser.rs b/src/parser.rs index 13c5252..153df12 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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), @@ -12,9 +12,11 @@ pub enum Node { Keyword(String), Int(i64), String(String), - True, - False, + Boolean(bool), Nil, + + Vector(Vec), + Map(HashMap), } pub fn parse(tokens: Vec) -> Result> { @@ -68,8 +70,8 @@ fn next_statement(tokens: &mut Peekable>) -> Result 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>, 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) { - 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()); - } -}