From 305bf638f823a41f391936712eef302bc6733d00 Mon Sep 17 00:00:00 2001 From: Akshay Date: Tue, 23 Mar 2021 18:56:25 +0530 Subject: expose functions to lisp interface, add primitives with macros --- src/lisp/env.rs | 88 +++++++++++++++++++++++--------------------------------- src/lisp/expr.rs | 19 +++++++++++- src/lisp/lex.rs | 5 +--- src/lisp/mod.rs | 2 ++ 4 files changed, 57 insertions(+), 57 deletions(-) (limited to 'src/lisp') diff --git a/src/lisp/env.rs b/src/lisp/env.rs index ad7cc2b..94c0e05 100644 --- a/src/lisp/env.rs +++ b/src/lisp/env.rs @@ -1,8 +1,11 @@ use crate::{ app::AppState, lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, + primitive, }; +use log::warn; + pub fn is_bound>(env: &mut Environment, name: S) -> bool { env.get(name.as_ref()).is_some() } @@ -13,21 +16,39 @@ pub fn new_binding>(env: &mut Environment, name: S, value: LispExp pub fn with_prelude() -> Environment { let mut env = Environment::new(); - new_binding( - &mut env, - "+", - LispExpr::PrimitiveFunc(|args, _| { - if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { - Err(LispError::EvalError) - } else { - let result = args - .iter() - .map(|n| unwrap_number(n)) - .fold(LispNumber::Integer(0), |acc, x| acc + *x); - Ok(LispExpr::Number(result)) + primitive!(env, Some(2), "+", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + Ok(LispExpr::Number( + args.iter() + .map(|arg| unwrap_number(arg)) + .fold(LispNumber::Integer(0), |acc, x| acc + *x), + )) + } + }); + primitive!(env, Some(2), "sub", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + let mut acc = unwrap_number(&args[0]).clone(); + for arg in args.into_iter().skip(1) { + acc = acc - *unwrap_number(&arg); } - }), - ); + Ok(LispExpr::Number(acc)) + } + }); + primitive!(env, Some(2), "*", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + Ok(LispExpr::Number( + args.iter() + .map(|arg| unwrap_number(arg)) + .fold(LispNumber::Integer(1), |acc, x| acc * *x), + )) + } + }); env } @@ -56,7 +77,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result for item in li[1..].iter() { args.push(eval(item, app)?); } - (f)(&args, None) + f.call(&args, app) } _ => Err(LispError::EvalError), } @@ -78,40 +99,3 @@ pub fn unwrap_number(n: &LispExpr) -> &LispNumber { _ => panic!("unwrap_number expected number"), } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn eval_primitive_call() { - let mut env = Environment::new(); - new_binding(&mut env, "age", LispExpr::Number(LispNumber::Float(1.4))); - new_binding( - &mut env, - "+", - LispExpr::PrimitiveFunc(|args, _| { - if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { - Err(LispError::EvalError) - } else { - let result = args - .iter() - .map(|n| unwrap_number(n)) - .fold(LispNumber::Integer(0), |acc, x| acc + *x); - Ok(LispExpr::Number(result)) - } - }), - ); - let mut numbers = (1..=3) - .map(LispNumber::Integer) - .map(LispExpr::Number) - .collect::>(); - let mut expr = Vec::new(); - expr.push(LispExpr::Ident("+".into())); - expr.append(&mut numbers); - // assert!(matches!( - // eval(&LispExpr::List(expr), ).unwrap(), - // LispExpr::Number(LispNumber::Integer(6)) - // )); - } -} diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 4676a3e..adacc1c 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -11,7 +11,7 @@ pub enum LispExpr { StringLit(String), BoolLit(bool), Ident(String), - PrimitiveFunc(fn(&[LispExpr], Option<&mut AppState>) -> Result), + PrimitiveFunc(PrimitiveFunc), Function(LispFunction), // none of these depths should be zero @@ -21,6 +21,23 @@ pub enum LispExpr { Quote(Box, u32), } +#[derive(Clone)] +pub struct PrimitiveFunc { + pub arity: Option, + pub closure: fn(&[LispExpr], &mut AppState) -> Result, +} + +impl PrimitiveFunc { + pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result { + if let Some(arity) = self.arity { + if args.len() < arity { + return Err(LispError::EvalError); + } + } + (self.closure)(args, app) + } +} + impl LispExpr { pub fn comma(self, n: u32) -> LispExpr { match self { diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs index 3b1389d..30f49fa 100644 --- a/src/lisp/lex.rs +++ b/src/lisp/lex.rs @@ -94,10 +94,7 @@ impl<'a> Lexer<'a> { self.cur_pos += ch.len_utf8() as u32; continue; } - ch => { - eprintln!("some unexpected character: {}", ch); - Err(LispError::ParseError) - } + _ => Err(LispError::ParseError), }; let (size, token) = match res { Ok(v) => v, diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 5166a04..2a19314 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs @@ -4,6 +4,8 @@ pub mod expr; pub mod lex; pub mod number; pub mod parse; +#[macro_use] +mod primitives; use std::collections::HashMap; -- cgit v1.2.3