diff options
-rw-r--r-- | src/lisp/error.rs | 20 | ||||
-rw-r--r-- | src/lisp/eval.rs | 53 | ||||
-rw-r--r-- | src/lisp/lex.rs | 25 | ||||
-rw-r--r-- | src/lisp/parse.rs | 7 |
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 @@ | |||
1 | use crate::lisp::lex::{Span, SpanDisplay}; | 1 | use crate::lisp::{ |
2 | expr::Arity, | ||
3 | lex::{Span, SpanDisplay}, | ||
4 | }; | ||
2 | 5 | ||
3 | use std::fmt; | 6 | use 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)] |
83 | pub enum EvalError { | 86 | pub 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 | ||
91 | impl fmt::Display for EvalError { | 95 | impl 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 | ||
73 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 75 | pub 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 | ||
124 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 127 | pub 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 | ||
149 | pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | 152 | pub 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 | ||
177 | pub 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 | |||
175 | pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { | 198 | pub 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 | ||
260 | fn 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)] |
258 | mod tests { | 267 | mod 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; |