aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lisp/error.rs20
-rw-r--r--src/lisp/eval.rs53
-rw-r--r--src/lisp/lex.rs25
-rw-r--r--src/lisp/parse.rs7
4 files changed, 82 insertions, 23 deletions
diff --git a/src/lisp/error.rs b/src/lisp/error.rs
index 4f90d14..b90e211 100644
--- a/src/lisp/error.rs
+++ b/src/lisp/error.rs
@@ -1,4 +1,7 @@
1use crate::lisp::lex::{Span, SpanDisplay}; 1use crate::lisp::{
2 expr::Arity,
3 lex::{Span, SpanDisplay},
4};
2 5
3use std::fmt; 6use std::fmt;
4 7
@@ -81,23 +84,34 @@ impl From<ParseError> for LispError {
81 84
82#[derive(Debug, PartialEq, Clone)] 85#[derive(Debug, PartialEq, Clone)]
83pub enum EvalError { 86pub enum EvalError {
84 ArgumentCount(Option<u32>), // expected 87 ArgumentCount(Arity),
85 BadForm, 88 BadForm,
86 UnboundVariable(String), 89 UnboundVariable(String),
87 DivByZero, 90 DivByZero,
88 TypeMismatch, 91 TypeMismatch,
92 NoFileName,
89} 93}
90 94
91impl fmt::Display for EvalError { 95impl fmt::Display for EvalError {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self { 97 match self {
94 Self::ArgumentCount(i) => { 98 Self::ArgumentCount(i) => {
95 write!(f, "invalid number of arguments, expected atleast {:?}", i) 99 write!(f, "invalid number of arguments, expected ")?;
100 match i {
101 Arity::Exact(a) => write!(f, "exactly {} argument(s)", a),
102 Arity::Atleast(a) => write!(f, "atleast {} argument(s)", a),
103 Arity::Atmost(a) => write!(f, "atmost {} argument(s)", a),
104 Arity::Range(low, high) => {
105 write!(f, "between {} and {} argument(s)", low, high)
106 }
107 Arity::None => write!(f, "any number of arguments"),
108 }
96 } 109 }
97 Self::BadForm => write!(f, "bad expression form"), 110 Self::BadForm => write!(f, "bad expression form"),
98 Self::UnboundVariable(s) => write!(f, "unbound variable {}", s), 111 Self::UnboundVariable(s) => write!(f, "unbound variable {}", s),
99 Self::TypeMismatch => write!(f, "mismatched types"), 112 Self::TypeMismatch => write!(f, "mismatched types"),
100 Self::DivByZero => write!(f, "attempt to divide by zero"), 113 Self::DivByZero => write!(f, "attempt to divide by zero"),
114 Self::NoFileName => write!(f, "no file name specified"),
101 } 115 }
102 } 116 }
103} 117}
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs
index 5accec4..370b624 100644
--- a/src/lisp/eval.rs
+++ b/src/lisp/eval.rs
@@ -2,7 +2,7 @@ use crate::{
2 app::AppState, 2 app::AppState,
3 lisp::{ 3 lisp::{
4 error::{EvalError, LispError}, 4 error::{EvalError, LispError},
5 expr::{Ident, LispExpr, LispFunction}, 5 expr::{Arity, Ident, LispExpr, LispFunction},
6 number::LispNumber, 6 number::LispNumber,
7 EnvList, Environment, 7 EnvList, Environment,
8 }, 8 },
@@ -16,6 +16,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError>
16 match expr { 16 match expr {
17 LispExpr::Unit => Ok(expr.clone()), 17 LispExpr::Unit => Ok(expr.clone()),
18 LispExpr::StringLit(_) => Ok(expr.clone()), 18 LispExpr::StringLit(_) => Ok(expr.clone()),
19 LispExpr::Char(_) => Ok(expr.clone()),
19 LispExpr::Number(_) => Ok(expr.clone()), 20 LispExpr::Number(_) => Ok(expr.clone()),
20 LispExpr::BoolLit(_) => Ok(expr.clone()), 21 LispExpr::BoolLit(_) => Ok(expr.clone()),
21 LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), 22 LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id),
@@ -26,6 +27,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError>
26 "define" => define_var(&li[1..], app), 27 "define" => define_var(&li[1..], app),
27 "set!" => set_var(&li[1..], app), 28 "set!" => set_var(&li[1..], app),
28 "lambda" => create_lambda(&li[1..]), 29 "lambda" => create_lambda(&li[1..]),
30 "if" => eval_if(&li[1..], app),
29 _ => { 31 _ => {
30 let func_expr = eval(&func_expr, app)?; 32 let func_expr = eval(&func_expr, app)?;
31 match func_expr { 33 match func_expr {
@@ -37,17 +39,17 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError>
37 f.call(&args, app) 39 f.call(&args, app)
38 } 40 }
39 LispExpr::Function(f) => { 41 LispExpr::Function(f) => {
40 info!("eval custom func");
41 let mut args = Vec::new(); 42 let mut args = Vec::new();
42 for item in li[1..].iter() { 43 for item in li[1..].iter() {
43 args.push(eval(item, app)?); 44 let i = eval(item, app)?;
45 args.push(i);
44 } 46 }
45 if f.params.len() != args.len() { 47 if f.params.len() != args.len() {
46 info!("too many or too little number of args"); 48 info!("too many or too little number of args");
47 Err(EvalError::ArgumentCount(Some(f.params.len() as u32)) 49 Err(EvalError::ArgumentCount(Arity::Exact(f.params.len()))
48 .into()) 50 .into())
49 } else { 51 } else {
50 let mut nested_env: Environment = 52 let nested_env: Environment =
51 f.params.into_iter().zip(args).collect(); 53 f.params.into_iter().zip(args).collect();
52 app.lisp_env.push(nested_env); 54 app.lisp_env.push(nested_env);
53 let result = if f.body.is_empty() { 55 let result = if f.body.is_empty() {
@@ -71,9 +73,9 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError>
71} 73}
72 74
73pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { 75pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> {
74 if args.len() != 2 { 76 let arity = Arity::Exact(2);
75 error!("Invalid arity for `define`"); 77 if !arity.is_valid(args) {
76 return Err(EvalError::ArgumentCount(Some(2)).into()); 78 return Err(arity.to_error());
77 } 79 }
78 match args { 80 match args {
79 [LispExpr::Ident(id), expr] => { 81 [LispExpr::Ident(id), expr] => {
@@ -107,6 +109,7 @@ pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, Lis
107 109
108 let local_env = app.lisp_env.last_mut(); 110 let local_env = app.lisp_env.last_mut();
109 if let Some(env) = local_env { 111 if let Some(env) = local_env {
112 info!("creating function {}", id);
110 env.insert(id.into(), value); 113 env.insert(id.into(), value);
111 } else { 114 } else {
112 error!("Unable to create global definition"); 115 error!("Unable to create global definition");
@@ -122,9 +125,9 @@ pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, Lis
122} 125}
123 126
124pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { 127pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> {
125 if args.len() != 2 { 128 let arity = Arity::Exact(2);
126 error!("Invalid arity for `define`"); 129 if !arity.is_valid(args) {
127 return Err(EvalError::ArgumentCount(Some(2)).into()); 130 return Err(arity.to_error());
128 } 131 }
129 match args { 132 match args {
130 [LispExpr::Ident(id), expr] => { 133 [LispExpr::Ident(id), expr] => {
@@ -147,10 +150,9 @@ pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispEr
147} 150}
148 151
149pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { 152pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> {
150 if cdr.len() != 2 { 153 let arity: Arity = Arity::Exact(2);
151 // needs params and body 154 if !arity.is_valid(cdr) {
152 error!("needs params and body"); 155 return Err(arity.to_error());
153 return Err(EvalError::ArgumentCount(Some(2)).into());
154 } 156 }
155 info!("creating lambda"); 157 info!("creating lambda");
156 match cdr { 158 match cdr {
@@ -172,6 +174,27 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> {
172 } 174 }
173} 175}
174 176
177pub fn eval_if(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> {
178 let arity = Arity::Exact(3);
179 if !arity.is_valid(args) {
180 return Err(arity.to_error());
181 } else {
182 match args {
183 [predicate, then, else_] => {
184 let predicate = eval(&predicate, app)?;
185 if matches!(predicate, LispExpr::BoolLit(false)) {
186 return eval(&else_, app);
187 } else {
188 return eval(&then, app);
189 }
190 }
191 _ => {
192 panic!("panicked at `if` expression")
193 }
194 }
195 }
196}
197
175pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { 198pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> {
176 if env_list.is_empty() { 199 if env_list.is_empty() {
177 return Err(EvalError::UnboundVariable(key.into()).into()); 200 return Err(EvalError::UnboundVariable(key.into()).into());
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs
index b307e80..1a34e53 100644
--- a/src/lisp/lex.rs
+++ b/src/lisp/lex.rs
@@ -8,7 +8,7 @@ pub enum Token<'a> {
8 RightParen, 8 RightParen,
9 Float(&'a str), 9 Float(&'a str),
10 Integer(&'a str), 10 Integer(&'a str),
11 // Char(&'a str), 11 Char(&'a str),
12 String(&'a str), 12 String(&'a str),
13 Name(&'a str), 13 Name(&'a str),
14 // Keyword(&'a str), 14 // Keyword(&'a str),
@@ -26,7 +26,7 @@ impl<'a> Token<'a> {
26 Token::RightParen => ")", 26 Token::RightParen => ")",
27 Token::Float(_) => "float", 27 Token::Float(_) => "float",
28 Token::Integer(_) => "integer", 28 Token::Integer(_) => "integer",
29 // Token::Char(_) => "char", 29 Token::Char(_) => "char",
30 Token::String(_) => "string", 30 Token::String(_) => "string",
31 Token::Name(_) => "name", 31 Token::Name(_) => "name",
32 // Token::Keyword(_) => "keyword", 32 // Token::Keyword(_) => "keyword",
@@ -46,7 +46,7 @@ impl<'a> fmt::Display for Token<'a> {
46 Token::RightParen => write!(f, ")"), 46 Token::RightParen => write!(f, ")"),
47 Token::Float(_) => write!(f, "float"), 47 Token::Float(_) => write!(f, "float"),
48 Token::Integer(_) => write!(f, "integer"), 48 Token::Integer(_) => write!(f, "integer"),
49 // Token::Char(_) => write!(f, "char"), 49 Token::Char(_) => write!(f, "char"),
50 Token::String(_) => write!(f, "string"), 50 Token::String(_) => write!(f, "string"),
51 Token::Name(_) => write!(f, "name"), 51 Token::Name(_) => write!(f, "name"),
52 // Token::Keyword(_) => write!(f, "keyword"), 52 // Token::Keyword(_) => write!(f, "keyword"),
@@ -122,7 +122,10 @@ impl<'a> Lexer<'a> {
122 Some((_, '@')) => Ok((2, Token::CommaAt)), 122 Some((_, '@')) => Ok((2, Token::CommaAt)),
123 _ => Ok((1, Token::Comma)), 123 _ => Ok((1, Token::Comma)),
124 }, 124 },
125 '#' => parse_name(&self.input[ind..]), 125 '#' => match chars.next() {
126 Some((_, '\\')) => parse_char(&self.input[ind..]),
127 _ => parse_name(&self.input[ind..]),
128 },
126 '-' | '0'..='9' => parse_number(&self.input[ind..]), 129 '-' | '0'..='9' => parse_number(&self.input[ind..]),
127 '"' => parse_string(&self.input[ind..]), 130 '"' => parse_string(&self.input[ind..]),
128 ';' => { 131 ';' => {
@@ -254,6 +257,12 @@ fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind>
254 return Ok((input.len(), Token::Name(input))); 257 return Ok((input.len(), Token::Name(input)));
255} 258}
256 259
260fn parse_char<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> {
261 // first two chars of input are '#' and '\'
262 let chr = &input[..3];
263 return Ok((chr.len(), Token::Char(chr)));
264}
265
257#[cfg(test)] 266#[cfg(test)]
258mod tests { 267mod tests {
259 use super::*; 268 use super::*;
@@ -283,6 +292,14 @@ mod tests {
283 } 292 }
284 293
285 #[test] 294 #[test]
295 fn char_parsing() {
296 let input = r##"#\a"##;
297 let parsed = parse_char(input).unwrap();
298 assert_eq!(parsed.0, 3);
299 assert_eq!(parsed.1, Token::Char(r##"#\a"##));
300 }
301
302 #[test]
286 fn integer_parsing() { 303 fn integer_parsing() {
287 let input = "12345"; 304 let input = "12345";
288 let parsed = parse_number(input).unwrap(); 305 let parsed = parse_number(input).unwrap();
diff --git a/src/lisp/parse.rs b/src/lisp/parse.rs
index 4e0f427..7cca434 100644
--- a/src/lisp/parse.rs
+++ b/src/lisp/parse.rs
@@ -58,7 +58,12 @@ impl<'lex> Parser<'lex> {
58 .parse::<i64>() 58 .parse::<i64>()
59 .map(|n| LispExpr::Number(LispNumber::Integer(n))) 59 .map(|n| LispExpr::Number(LispNumber::Integer(n)))
60 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), 60 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()),
61 Token::String(s) => Ok(LispExpr::StringLit(s.into())), 61 Token::String(s) => Ok(LispExpr::StringLit(s[1..s.len() - 1].into())),
62 Token::Char(s) => Ok(LispExpr::Char(s.chars().nth(2).ok_or_else(
63 || -> LispError {
64 ParseError::new(span, ParseErrorKind::LiteralParseError).into()
65 },
66 )?)),
62 Token::Name(n) => Ok(name_expr(n)), 67 Token::Name(n) => Ok(name_expr(n)),
63 Token::BackQuote => { 68 Token::BackQuote => {
64 total_backticks += 1; 69 total_backticks += 1;