diff options
-rw-r--r-- | src/app.rs | 62 | ||||
-rw-r--r-- | src/lisp/lex.rs | 64 | ||||
-rw-r--r-- | src/lisp/mod.rs | 3 | ||||
-rw-r--r-- | src/lisp/number.rs | 6 | ||||
-rw-r--r-- | src/lisp/parse.rs | 55 |
5 files changed, 120 insertions, 70 deletions
@@ -3,7 +3,7 @@ use crate::{ | |||
3 | command::CommandBox, | 3 | command::CommandBox, |
4 | consts::{colors::*, FONT_PATH}, | 4 | consts::{colors::*, FONT_PATH}, |
5 | dither, | 5 | dither, |
6 | lisp::{eval, lex::Lexer, parse::Parser, Environment}, | 6 | lisp::{eval, lex::Lexer, parse::Parser, prelude, EnvList, Environment}, |
7 | message::Message, | 7 | message::Message, |
8 | rect, | 8 | rect, |
9 | symmetry::Symmetry, | 9 | symmetry::Symmetry, |
@@ -11,7 +11,12 @@ use crate::{ | |||
11 | utils::{draw_text, is_copy_event, is_paste_event}, | 11 | utils::{draw_text, is_copy_event, is_paste_event}, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use std::{convert::From, fs::File, io::prelude::*}; | 14 | use std::{ |
15 | convert::From, | ||
16 | fs::File, | ||
17 | io::prelude::*, | ||
18 | path::{Path, PathBuf}, | ||
19 | }; | ||
15 | 20 | ||
16 | use obi::Image; | 21 | use obi::Image; |
17 | use sdl2::{ | 22 | use sdl2::{ |
@@ -32,7 +37,7 @@ pub enum Mode { | |||
32 | Command, | 37 | Command, |
33 | } | 38 | } |
34 | 39 | ||
35 | pub struct AppState<'ctx> { | 40 | pub struct AppState<'ctx, 'file> { |
36 | pub active_color: bool, | 41 | pub active_color: bool, |
37 | pub brush_size: u8, | 42 | pub brush_size: u8, |
38 | pub canvas: Canvas<Window>, | 43 | pub canvas: Canvas<Window>, |
@@ -40,9 +45,10 @@ pub struct AppState<'ctx> { | |||
40 | pub context: &'ctx Sdl, | 45 | pub context: &'ctx Sdl, |
41 | pub current_operation: Operation, | 46 | pub current_operation: Operation, |
42 | pub dither_level: u8, | 47 | pub dither_level: u8, |
48 | pub file_name: Option<&'file Path>, | ||
43 | pub grid: Grid, | 49 | pub grid: Grid, |
44 | pub last_point: Option<MapPoint>, | 50 | pub last_point: Option<MapPoint>, |
45 | pub lisp_env: Environment, | 51 | pub lisp_env: EnvList, |
46 | pub message: Message, | 52 | pub message: Message, |
47 | pub mode: Mode, | 53 | pub mode: Mode, |
48 | pub mouse: (i32, i32), | 54 | pub mouse: (i32, i32), |
@@ -69,7 +75,7 @@ impl Grid { | |||
69 | } | 75 | } |
70 | 76 | ||
71 | // private actions on appstate | 77 | // private actions on appstate |
72 | impl<'ctx> AppState<'ctx> { | 78 | impl<'ctx, 'file> AppState<'ctx, 'file> { |
73 | fn pan<P: Into<Point>>(&mut self, direction: P) { | 79 | fn pan<P: Into<Point>>(&mut self, direction: P) { |
74 | self.start += direction.into(); | 80 | self.start += direction.into(); |
75 | } | 81 | } |
@@ -280,22 +286,22 @@ impl<'ctx> AppState<'ctx> { | |||
280 | let lisp_expr = &self.command_box.text; | 286 | let lisp_expr = &self.command_box.text; |
281 | let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); | 287 | let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); |
282 | let res = parser.parse_single_expr(); | 288 | let res = parser.parse_single_expr(); |
283 | match eval::eval(&res.unwrap(), self, None) { | 289 | match res { |
284 | Ok(val) => { | 290 | Ok(expr) => match eval::eval(&expr, self) { |
285 | self.message.text = format!("{}", val); | 291 | Ok(val) => self.message.set_info(format!("{}", val)), |
286 | } | 292 | Err(eval_err) => self.message.set_error(format!("{}", eval_err)), |
287 | Err(_) => { | 293 | }, |
288 | self.message.text = format!("Lisp Error!"); | 294 | Err(parse_err) => self.message.set_error(format!("{}", parse_err)), |
289 | } | ||
290 | } | ||
291 | |||
292 | if let Some(path) = self.command_box.text.strip_prefix("(save ") { | ||
293 | let image = self.export(); | ||
294 | let encoded = image.encode().unwrap(); | ||
295 | let mut buffer = File::create(path).unwrap(); | ||
296 | buffer.write_all(&encoded[..]).unwrap(); | ||
297 | self.command_box.hist_append(); | ||
298 | } | 295 | } |
296 | self.command_box.hist_append(); | ||
297 | |||
298 | // if let Some(path) = self.command_box.text.strip_prefix("(save ") { | ||
299 | // let image = self.export(); | ||
300 | // let encoded = image.encode().unwrap(); | ||
301 | // let mut buffer = File::create(path).unwrap(); | ||
302 | // buffer.write_all(&encoded[..]).unwrap(); | ||
303 | // self.command_box.hist_append(); | ||
304 | // } | ||
299 | self.command_box.clear(); | 305 | self.command_box.clear(); |
300 | self.mode = Mode::Draw; | 306 | self.mode = Mode::Draw; |
301 | } | 307 | } |
@@ -371,7 +377,7 @@ impl<'ctx> AppState<'ctx> { | |||
371 | &mut self.canvas, | 377 | &mut self.canvas, |
372 | self.ttf_context, | 378 | self.ttf_context, |
373 | &self.message.text[..], | 379 | &self.message.text[..], |
374 | WHITE, | 380 | self.message.kind.into(), |
375 | (0, winsize_y - cmd_height), | 381 | (0, winsize_y - cmd_height), |
376 | ); | 382 | ); |
377 | return; | 383 | return; |
@@ -473,18 +479,19 @@ impl<'ctx> AppState<'ctx> { | |||
473 | 479 | ||
474 | pub fn quit(&mut self) { | 480 | pub fn quit(&mut self) { |
475 | let ev = self.context.event().unwrap(); | 481 | let ev = self.context.event().unwrap(); |
476 | ev.push_event(Event::Quit { timestamp: 0u32 }); | 482 | ev.push_event(Event::Quit { timestamp: 0u32 }) |
483 | .expect("ohno unable to quit"); | ||
477 | } | 484 | } |
478 | } | 485 | } |
479 | 486 | ||
480 | // publicly available functions on appstate | 487 | impl<'ctx, 'file> AppState<'ctx, 'file> { |
481 | impl<'ctx> AppState<'ctx> { | ||
482 | pub fn init( | 488 | pub fn init( |
483 | width: u32, | 489 | width: u32, |
484 | height: u32, | 490 | height: u32, |
485 | context: &'ctx Sdl, | 491 | context: &'ctx Sdl, |
486 | ttf_context: &'ctx Sdl2TtfContext, | 492 | ttf_context: &'ctx Sdl2TtfContext, |
487 | start_data: Option<Vec<bool>>, | 493 | start_data: Option<Vec<bool>>, |
494 | file_name: Option<&'file Path>, | ||
488 | ) -> Self { | 495 | ) -> Self { |
489 | let video_subsystem = context.video().unwrap(); | 496 | let video_subsystem = context.video().unwrap(); |
490 | 497 | ||
@@ -509,21 +516,22 @@ impl<'ctx> AppState<'ctx> { | |||
509 | active_color: true, | 516 | active_color: true, |
510 | brush_size: 0, | 517 | brush_size: 0, |
511 | canvas, | 518 | canvas, |
512 | context, | ||
513 | command_box: CommandBox::new(), | 519 | command_box: CommandBox::new(), |
520 | context, | ||
514 | current_operation: Vec::new(), | 521 | current_operation: Vec::new(), |
515 | dither_level: 16, | 522 | dither_level: 16, |
523 | file_name, | ||
516 | grid: Grid::new(), | 524 | grid: Grid::new(), |
517 | last_point: None, | 525 | last_point: None, |
518 | mode: Mode::Draw, | 526 | lisp_env: vec![prelude::new_env()], |
519 | message: Message::new().text(" "), | 527 | message: Message::new().text(" "), |
528 | mode: Mode::Draw, | ||
520 | mouse: (0, 0), | 529 | mouse: (0, 0), |
521 | pixmap, | 530 | pixmap, |
522 | start: Point::new(60, 60), | 531 | start: Point::new(60, 60), |
523 | symmetry: Default::default(), | 532 | symmetry: Default::default(), |
524 | ttf_context, | 533 | ttf_context, |
525 | undo_stack: UndoStack::new(), | 534 | undo_stack: UndoStack::new(), |
526 | lisp_env: eval::with_prelude(), | ||
527 | zoom: 5, | 535 | zoom: 5, |
528 | } | 536 | } |
529 | } | 537 | } |
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs index a7b9586..b307e80 100644 --- a/src/lisp/lex.rs +++ b/src/lisp/lex.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt, str::CharIndices}; | 1 | use std::{fmt, str::CharIndices}; |
2 | 2 | ||
3 | use super::error::LispError; | 3 | use crate::lisp::error::{LispError, ParseError, ParseErrorKind}; |
4 | 4 | ||
5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
6 | pub enum Token<'a> { | 6 | pub enum Token<'a> { |
@@ -19,6 +19,26 @@ pub enum Token<'a> { | |||
19 | End, | 19 | End, |
20 | } | 20 | } |
21 | 21 | ||
22 | impl<'a> Token<'a> { | ||
23 | pub fn name(&self) -> &'static str { | ||
24 | match self { | ||
25 | Token::LeftParen => "(", | ||
26 | Token::RightParen => ")", | ||
27 | Token::Float(_) => "float", | ||
28 | Token::Integer(_) => "integer", | ||
29 | // Token::Char(_) => "char", | ||
30 | Token::String(_) => "string", | ||
31 | Token::Name(_) => "name", | ||
32 | // Token::Keyword(_) => "keyword", | ||
33 | Token::BackQuote => "`", | ||
34 | Token::Comma => ",", | ||
35 | Token::CommaAt => ",@", | ||
36 | Token::Quote => "'", | ||
37 | Token::End => "EOF", | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
22 | impl<'a> fmt::Display for Token<'a> { | 42 | impl<'a> fmt::Display for Token<'a> { |
23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 43 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
24 | match self { | 44 | match self { |
@@ -54,6 +74,25 @@ impl Span { | |||
54 | } | 74 | } |
55 | } | 75 | } |
56 | 76 | ||
77 | #[derive(Debug, Clone)] | ||
78 | pub struct SpanDisplay<'src> { | ||
79 | pub source: &'src str, | ||
80 | pub line: usize, | ||
81 | pub col: usize, | ||
82 | } | ||
83 | |||
84 | impl<'src> SpanDisplay<'src> { | ||
85 | pub fn highlight_span(span: Span, source: &'src str) -> Self { | ||
86 | let line_start = match source[..span.low as usize].rfind('\n') { | ||
87 | Some(pos) => pos + 1, | ||
88 | None => 0, | ||
89 | }; | ||
90 | let line = source[..line_start].chars().filter(|&c| c == '\n').count() + 1; | ||
91 | let col = source[line_start..span.low as usize].chars().count(); | ||
92 | Self { source, line, col } | ||
93 | } | ||
94 | } | ||
95 | |||
57 | pub struct Lexer<'input> { | 96 | pub struct Lexer<'input> { |
58 | input: &'input str, | 97 | input: &'input str, |
59 | cur_pos: u32, | 98 | cur_pos: u32, |
@@ -68,6 +107,7 @@ impl<'a> Lexer<'a> { | |||
68 | offset, | 107 | offset, |
69 | } | 108 | } |
70 | } | 109 | } |
110 | |||
71 | pub fn next_token(&mut self) -> Result<(Span, Token<'a>), LispError> { | 111 | pub fn next_token(&mut self) -> Result<(Span, Token<'a>), LispError> { |
72 | let mut chars = self.input.char_indices(); | 112 | let mut chars = self.input.char_indices(); |
73 | 113 | ||
@@ -94,11 +134,19 @@ impl<'a> Lexer<'a> { | |||
94 | self.cur_pos += ch.len_utf8() as u32; | 134 | self.cur_pos += ch.len_utf8() as u32; |
95 | continue; | 135 | continue; |
96 | } | 136 | } |
97 | _ => Err(LispError::ParseError), | 137 | ch => Err(ParseErrorKind::InvalidChar(ch)), |
98 | }; | 138 | }; |
99 | let (size, token) = match res { | 139 | let (size, token) = match res { |
100 | Ok(v) => v, | 140 | Ok(v) => v, |
101 | Err(_) => return Err(LispError::ParseError), | 141 | Err(kind) => { |
142 | return Err(LispError::Parse(ParseError { | ||
143 | span: Span { | ||
144 | low, | ||
145 | high: low + chr.len_utf8() as u32, | ||
146 | }, | ||
147 | kind, | ||
148 | })) | ||
149 | } | ||
102 | }; | 150 | }; |
103 | self.cur_pos += size as u32; | 151 | self.cur_pos += size as u32; |
104 | self.input = &self.input[ind + size..]; | 152 | self.input = &self.input[ind + size..]; |
@@ -113,7 +161,7 @@ impl<'a> Lexer<'a> { | |||
113 | } | 161 | } |
114 | } | 162 | } |
115 | 163 | ||
116 | fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> { | 164 | fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { |
117 | let mut dot = false; | 165 | let mut dot = false; |
118 | let mut minus = false; | 166 | let mut minus = false; |
119 | let mut size = 0; | 167 | let mut size = 0; |
@@ -137,12 +185,12 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> | |||
137 | dot = true; | 185 | dot = true; |
138 | size += 1; | 186 | size += 1; |
139 | } else { | 187 | } else { |
140 | return Err(LispError::ParseError); | 188 | return Err(ParseErrorKind::InvalidChar(chr)); |
141 | } | 189 | } |
142 | } else if !is_ident(chr) { | 190 | } else if !is_ident(chr) { |
143 | break; | 191 | break; |
144 | } else { | 192 | } else { |
145 | return Err(LispError::ParseError); | 193 | return Err(ParseErrorKind::InvalidChar(chr)); |
146 | } | 194 | } |
147 | } | 195 | } |
148 | 196 | ||
@@ -156,7 +204,7 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> | |||
156 | return Ok((size, tok)); | 204 | return Ok((size, tok)); |
157 | } | 205 | } |
158 | 206 | ||
159 | fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), LispError> { | 207 | fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { |
160 | // count opening quote | 208 | // count opening quote |
161 | let mut size = 1; | 209 | let mut size = 1; |
162 | let mut chars = input.char_indices().skip(1); | 210 | let mut chars = input.char_indices().skip(1); |
@@ -197,7 +245,7 @@ fn consume_comment(start: usize, chars: &mut CharIndices) -> usize { | |||
197 | last - start + 1 | 245 | last - start + 1 |
198 | } | 246 | } |
199 | 247 | ||
200 | fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), LispError> { | 248 | fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { |
201 | for (ind, chr) in input.char_indices() { | 249 | for (ind, chr) in input.char_indices() { |
202 | if !is_ident(chr) { | 250 | if !is_ident(chr) { |
203 | return Ok((ind, Token::Name(&input[..ind]))); | 251 | return Ok((ind, Token::Name(&input[..ind]))); |
diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index a52baa0..87a2720 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs | |||
@@ -4,10 +4,11 @@ pub mod expr; | |||
4 | pub mod lex; | 4 | pub mod lex; |
5 | pub mod number; | 5 | pub mod number; |
6 | pub mod parse; | 6 | pub mod parse; |
7 | mod primitives; | 7 | pub mod prelude; |
8 | 8 | ||
9 | use std::collections::HashMap; | 9 | use std::collections::HashMap; |
10 | 10 | ||
11 | use expr::LispExpr; | 11 | use expr::LispExpr; |
12 | 12 | ||
13 | pub type Environment = HashMap<String, LispExpr>; | 13 | pub type Environment = HashMap<String, LispExpr>; |
14 | pub type EnvList = Vec<Environment>; | ||
diff --git a/src/lisp/number.rs b/src/lisp/number.rs index 18a41f7..23d7997 100644 --- a/src/lisp/number.rs +++ b/src/lisp/number.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | fmt, | 2 | fmt, |
3 | ops::{Add, Div, Mul, Sub}, | 3 | ops::{Add, Mul, Sub}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::lisp::error::LispError; | 6 | use crate::lisp::error::{EvalError, LispError}; |
7 | 7 | ||
8 | #[derive(Debug, Copy, Clone)] | 8 | #[derive(Debug, Copy, Clone)] |
9 | pub enum LispNumber { | 9 | pub enum LispNumber { |
@@ -15,7 +15,7 @@ impl LispNumber { | |||
15 | pub fn div(self, rhs: Self) -> Result<LispNumber, LispError> { | 15 | pub fn div(self, rhs: Self) -> Result<LispNumber, LispError> { |
16 | use LispNumber::*; | 16 | use LispNumber::*; |
17 | if rhs == Integer(0) || rhs == Float(0.) { | 17 | if rhs == Integer(0) || rhs == Float(0.) { |
18 | return Err(LispError::EvalError); | 18 | return Err(EvalError::DivByZero.into()); |
19 | } else { | 19 | } else { |
20 | return Ok(match (self, rhs) { | 20 | return Ok(match (self, rhs) { |
21 | (Integer(a), Integer(b)) => Float(a as f64 / b as f64), | 21 | (Integer(a), Integer(b)) => Float(a as f64 / b as f64), |
diff --git a/src/lisp/parse.rs b/src/lisp/parse.rs index 89a272a..4e0f427 100644 --- a/src/lisp/parse.rs +++ b/src/lisp/parse.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use crate::lisp::{ | 1 | use crate::lisp::{ |
2 | error::LispError, | 2 | error::{LispError, ParseError, ParseErrorKind}, |
3 | lex::{Lexer, Span, Token}, | 3 | lex::{Lexer, Span, Token}, |
4 | number::LispNumber, | 4 | number::LispNumber, |
5 | LispExpr, | 5 | LispExpr, |
@@ -10,26 +10,6 @@ pub struct Parser<'lex> { | |||
10 | cur_token: Option<(Span, Token<'lex>)>, | 10 | cur_token: Option<(Span, Token<'lex>)>, |
11 | } | 11 | } |
12 | 12 | ||
13 | // pub struct ParseError { | ||
14 | // pub span: Span, | ||
15 | // pub kind: ParseErrorKind, | ||
16 | // } | ||
17 | // | ||
18 | // pub enum ParseErrorKind { | ||
19 | // InvalidLiteral, | ||
20 | // InvalidToken, | ||
21 | // LiteralParseError, | ||
22 | // MissingCloseParen, | ||
23 | // UnbalancedComma, | ||
24 | // UnexpectedEof, | ||
25 | // UnexpectedToken { | ||
26 | // expected: &'static str, | ||
27 | // found: &'static str, | ||
28 | // }, | ||
29 | // UnmatchedParen, | ||
30 | // UnterminatedString, | ||
31 | // } | ||
32 | |||
33 | enum Group { | 13 | enum Group { |
34 | Backticks(i32), | 14 | Backticks(i32), |
35 | CommaAt, | 15 | CommaAt, |
@@ -49,7 +29,7 @@ impl<'lex> Parser<'lex> { | |||
49 | let mut stack = Vec::new(); | 29 | let mut stack = Vec::new(); |
50 | let mut total_backticks = 0; | 30 | let mut total_backticks = 0; |
51 | loop { | 31 | loop { |
52 | let (_, token) = self.next()?; | 32 | let (span, token) = self.next()?; |
53 | let r: Result<LispExpr, LispError> = match token { | 33 | let r: Result<LispExpr, LispError> = match token { |
54 | Token::LeftParen => { | 34 | Token::LeftParen => { |
55 | stack.push(Group::Parens(Vec::new())); | 35 | stack.push(Group::Parens(Vec::new())); |
@@ -57,21 +37,27 @@ impl<'lex> Parser<'lex> { | |||
57 | } | 37 | } |
58 | Token::RightParen => { | 38 | Token::RightParen => { |
59 | let group = stack.pop().ok_or_else( | 39 | let group = stack.pop().ok_or_else( |
60 | || LispError::ParseError, // unmatched paren here | 40 | || (ParseError::new(span, ParseErrorKind::UnmatchedParen)), // unmatched paren here |
61 | )?; | 41 | )?; |
62 | match group { | 42 | match group { |
63 | Group::Parens(v) => Ok(LispExpr::List(v)), | 43 | Group::Parens(v) => Ok(LispExpr::List(v)), |
64 | _ => Err(LispError::ParseError), | 44 | _ => Err(From::from(ParseError::new( |
45 | span, | ||
46 | ParseErrorKind::UnexpectedToken { | ||
47 | expected: "expression", | ||
48 | found: "(", | ||
49 | }, | ||
50 | ))), | ||
65 | } | 51 | } |
66 | } | 52 | } |
67 | Token::Float(f) => f | 53 | Token::Float(f) => f |
68 | .parse::<f64>() | 54 | .parse::<f64>() |
69 | .map(|n| LispExpr::Number(LispNumber::Float(n))) | 55 | .map(|n| LispExpr::Number(LispNumber::Float(n))) |
70 | .map_err(|_| LispError::ParseError), | 56 | .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), |
71 | Token::Integer(i) => i | 57 | Token::Integer(i) => i |
72 | .parse::<i64>() | 58 | .parse::<i64>() |
73 | .map(|n| LispExpr::Number(LispNumber::Integer(n))) | 59 | .map(|n| LispExpr::Number(LispNumber::Integer(n))) |
74 | .map_err(|_| LispError::ParseError), | 60 | .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), |
75 | Token::String(s) => Ok(LispExpr::StringLit(s.into())), | 61 | Token::String(s) => Ok(LispExpr::StringLit(s.into())), |
76 | Token::Name(n) => Ok(name_expr(n)), | 62 | Token::Name(n) => Ok(name_expr(n)), |
77 | Token::BackQuote => { | 63 | Token::BackQuote => { |
@@ -85,7 +71,7 @@ impl<'lex> Parser<'lex> { | |||
85 | } | 71 | } |
86 | Token::Comma => { | 72 | Token::Comma => { |
87 | if total_backticks <= 0 { | 73 | if total_backticks <= 0 { |
88 | return Err(LispError::ParseError); // unbalanced comma | 74 | return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma).into()); |
89 | } | 75 | } |
90 | total_backticks -= 1; | 76 | total_backticks -= 1; |
91 | if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() { | 77 | if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() { |
@@ -97,7 +83,7 @@ impl<'lex> Parser<'lex> { | |||
97 | } | 83 | } |
98 | Token::CommaAt => { | 84 | Token::CommaAt => { |
99 | if total_backticks <= 0 { | 85 | if total_backticks <= 0 { |
100 | return Err(LispError::ParseError); // unbalanced comma | 86 | return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma).into()); |
101 | } | 87 | } |
102 | total_backticks -= 1; | 88 | total_backticks -= 1; |
103 | stack.push(Group::CommaAt); | 89 | stack.push(Group::CommaAt); |
@@ -118,9 +104,9 @@ impl<'lex> Parser<'lex> { | |||
118 | }); | 104 | }); |
119 | 105 | ||
120 | if any_paren { | 106 | if any_paren { |
121 | Err(LispError::ParseError) // unbalanced paren | 107 | Err(ParseError::new(span, ParseErrorKind::MissingCloseParen).into()) |
122 | } else { | 108 | } else { |
123 | Err(LispError::ParseError) // unexpected eof | 109 | Err(ParseError::new(span, ParseErrorKind::UnexpectedEof).into()) |
124 | } | 110 | } |
125 | } | 111 | } |
126 | }; | 112 | }; |
@@ -178,7 +164,14 @@ impl<'lex> Parser<'lex> { | |||
178 | 164 | ||
179 | match self.next()? { | 165 | match self.next()? { |
180 | (_, Token::End) => Ok(expr), | 166 | (_, Token::End) => Ok(expr), |
181 | _ => Err(LispError::ParseError), // too many tokens | 167 | (span, token) => Err(ParseError::new( |
168 | span, | ||
169 | ParseErrorKind::UnexpectedToken { | ||
170 | expected: "EOF", | ||
171 | found: token.name(), | ||
172 | }, | ||
173 | ) | ||
174 | .into()), | ||
182 | } | 175 | } |
183 | } | 176 | } |
184 | 177 | ||