From 8171b30adbc4cddd2c51f043c3379d78428666b8 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 25 Mar 2021 13:08:25 +0530 Subject: use new error kinds; track Environment nesting with stack --- src/app.rs | 62 +++++++++++++++++++++++++++++----------------------- src/lisp/lex.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++------- src/lisp/mod.rs | 3 ++- src/lisp/number.rs | 6 ++--- src/lisp/parse.rs | 55 ++++++++++++++++++++-------------------------- 5 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/app.rs b/src/app.rs index d35febf..09f52f9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,7 @@ use crate::{ command::CommandBox, consts::{colors::*, FONT_PATH}, dither, - lisp::{eval, lex::Lexer, parse::Parser, Environment}, + lisp::{eval, lex::Lexer, parse::Parser, prelude, EnvList, Environment}, message::Message, rect, symmetry::Symmetry, @@ -11,7 +11,12 @@ use crate::{ utils::{draw_text, is_copy_event, is_paste_event}, }; -use std::{convert::From, fs::File, io::prelude::*}; +use std::{ + convert::From, + fs::File, + io::prelude::*, + path::{Path, PathBuf}, +}; use obi::Image; use sdl2::{ @@ -32,7 +37,7 @@ pub enum Mode { Command, } -pub struct AppState<'ctx> { +pub struct AppState<'ctx, 'file> { pub active_color: bool, pub brush_size: u8, pub canvas: Canvas, @@ -40,9 +45,10 @@ pub struct AppState<'ctx> { pub context: &'ctx Sdl, pub current_operation: Operation, pub dither_level: u8, + pub file_name: Option<&'file Path>, pub grid: Grid, pub last_point: Option, - pub lisp_env: Environment, + pub lisp_env: EnvList, pub message: Message, pub mode: Mode, pub mouse: (i32, i32), @@ -69,7 +75,7 @@ impl Grid { } // private actions on appstate -impl<'ctx> AppState<'ctx> { +impl<'ctx, 'file> AppState<'ctx, 'file> { fn pan>(&mut self, direction: P) { self.start += direction.into(); } @@ -280,22 +286,22 @@ impl<'ctx> AppState<'ctx> { let lisp_expr = &self.command_box.text; let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); let res = parser.parse_single_expr(); - match eval::eval(&res.unwrap(), self, None) { - Ok(val) => { - self.message.text = format!("{}", val); - } - Err(_) => { - self.message.text = format!("Lisp Error!"); - } - } - - if let Some(path) = self.command_box.text.strip_prefix("(save ") { - let image = self.export(); - let encoded = image.encode().unwrap(); - let mut buffer = File::create(path).unwrap(); - buffer.write_all(&encoded[..]).unwrap(); - self.command_box.hist_append(); + match res { + Ok(expr) => match eval::eval(&expr, self) { + Ok(val) => self.message.set_info(format!("{}", val)), + Err(eval_err) => self.message.set_error(format!("{}", eval_err)), + }, + Err(parse_err) => self.message.set_error(format!("{}", parse_err)), } + self.command_box.hist_append(); + + // if let Some(path) = self.command_box.text.strip_prefix("(save ") { + // let image = self.export(); + // let encoded = image.encode().unwrap(); + // let mut buffer = File::create(path).unwrap(); + // buffer.write_all(&encoded[..]).unwrap(); + // self.command_box.hist_append(); + // } self.command_box.clear(); self.mode = Mode::Draw; } @@ -371,7 +377,7 @@ impl<'ctx> AppState<'ctx> { &mut self.canvas, self.ttf_context, &self.message.text[..], - WHITE, + self.message.kind.into(), (0, winsize_y - cmd_height), ); return; @@ -473,18 +479,19 @@ impl<'ctx> AppState<'ctx> { pub fn quit(&mut self) { let ev = self.context.event().unwrap(); - ev.push_event(Event::Quit { timestamp: 0u32 }); + ev.push_event(Event::Quit { timestamp: 0u32 }) + .expect("ohno unable to quit"); } } -// publicly available functions on appstate -impl<'ctx> AppState<'ctx> { +impl<'ctx, 'file> AppState<'ctx, 'file> { pub fn init( width: u32, height: u32, context: &'ctx Sdl, ttf_context: &'ctx Sdl2TtfContext, start_data: Option>, + file_name: Option<&'file Path>, ) -> Self { let video_subsystem = context.video().unwrap(); @@ -509,21 +516,22 @@ impl<'ctx> AppState<'ctx> { active_color: true, brush_size: 0, canvas, - context, command_box: CommandBox::new(), + context, current_operation: Vec::new(), dither_level: 16, + file_name, grid: Grid::new(), last_point: None, - mode: Mode::Draw, + lisp_env: vec![prelude::new_env()], message: Message::new().text(" "), + mode: Mode::Draw, mouse: (0, 0), pixmap, start: Point::new(60, 60), symmetry: Default::default(), ttf_context, undo_stack: UndoStack::new(), - lisp_env: eval::with_prelude(), zoom: 5, } } 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 @@ use std::{fmt, str::CharIndices}; -use super::error::LispError; +use crate::lisp::error::{LispError, ParseError, ParseErrorKind}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Token<'a> { @@ -19,6 +19,26 @@ pub enum Token<'a> { End, } +impl<'a> Token<'a> { + pub fn name(&self) -> &'static str { + match self { + Token::LeftParen => "(", + Token::RightParen => ")", + Token::Float(_) => "float", + Token::Integer(_) => "integer", + // Token::Char(_) => "char", + Token::String(_) => "string", + Token::Name(_) => "name", + // Token::Keyword(_) => "keyword", + Token::BackQuote => "`", + Token::Comma => ",", + Token::CommaAt => ",@", + Token::Quote => "'", + Token::End => "EOF", + } + } +} + impl<'a> fmt::Display for Token<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -54,6 +74,25 @@ impl Span { } } +#[derive(Debug, Clone)] +pub struct SpanDisplay<'src> { + pub source: &'src str, + pub line: usize, + pub col: usize, +} + +impl<'src> SpanDisplay<'src> { + pub fn highlight_span(span: Span, source: &'src str) -> Self { + let line_start = match source[..span.low as usize].rfind('\n') { + Some(pos) => pos + 1, + None => 0, + }; + let line = source[..line_start].chars().filter(|&c| c == '\n').count() + 1; + let col = source[line_start..span.low as usize].chars().count(); + Self { source, line, col } + } +} + pub struct Lexer<'input> { input: &'input str, cur_pos: u32, @@ -68,6 +107,7 @@ impl<'a> Lexer<'a> { offset, } } + pub fn next_token(&mut self) -> Result<(Span, Token<'a>), LispError> { let mut chars = self.input.char_indices(); @@ -94,11 +134,19 @@ impl<'a> Lexer<'a> { self.cur_pos += ch.len_utf8() as u32; continue; } - _ => Err(LispError::ParseError), + ch => Err(ParseErrorKind::InvalidChar(ch)), }; let (size, token) = match res { Ok(v) => v, - Err(_) => return Err(LispError::ParseError), + Err(kind) => { + return Err(LispError::Parse(ParseError { + span: Span { + low, + high: low + chr.len_utf8() as u32, + }, + kind, + })) + } }; self.cur_pos += size as u32; self.input = &self.input[ind + size..]; @@ -113,7 +161,7 @@ impl<'a> Lexer<'a> { } } -fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> { +fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { let mut dot = false; let mut minus = false; let mut size = 0; @@ -137,12 +185,12 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> dot = true; size += 1; } else { - return Err(LispError::ParseError); + return Err(ParseErrorKind::InvalidChar(chr)); } } else if !is_ident(chr) { break; } else { - return Err(LispError::ParseError); + return Err(ParseErrorKind::InvalidChar(chr)); } } @@ -156,7 +204,7 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), LispError> return Ok((size, tok)); } -fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), LispError> { +fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { // count opening quote let mut size = 1; let mut chars = input.char_indices().skip(1); @@ -197,7 +245,7 @@ fn consume_comment(start: usize, chars: &mut CharIndices) -> usize { last - start + 1 } -fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), LispError> { +fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { for (ind, chr) in input.char_indices() { if !is_ident(chr) { 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; pub mod lex; pub mod number; pub mod parse; -mod primitives; +pub mod prelude; use std::collections::HashMap; use expr::LispExpr; pub type Environment = HashMap; +pub type EnvList = Vec; 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 @@ use std::{ fmt, - ops::{Add, Div, Mul, Sub}, + ops::{Add, Mul, Sub}, }; -use crate::lisp::error::LispError; +use crate::lisp::error::{EvalError, LispError}; #[derive(Debug, Copy, Clone)] pub enum LispNumber { @@ -15,7 +15,7 @@ impl LispNumber { pub fn div(self, rhs: Self) -> Result { use LispNumber::*; if rhs == Integer(0) || rhs == Float(0.) { - return Err(LispError::EvalError); + return Err(EvalError::DivByZero.into()); } else { return Ok(match (self, rhs) { (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 @@ use crate::lisp::{ - error::LispError, + error::{LispError, ParseError, ParseErrorKind}, lex::{Lexer, Span, Token}, number::LispNumber, LispExpr, @@ -10,26 +10,6 @@ pub struct Parser<'lex> { cur_token: Option<(Span, Token<'lex>)>, } -// pub struct ParseError { -// pub span: Span, -// pub kind: ParseErrorKind, -// } -// -// pub enum ParseErrorKind { -// InvalidLiteral, -// InvalidToken, -// LiteralParseError, -// MissingCloseParen, -// UnbalancedComma, -// UnexpectedEof, -// UnexpectedToken { -// expected: &'static str, -// found: &'static str, -// }, -// UnmatchedParen, -// UnterminatedString, -// } - enum Group { Backticks(i32), CommaAt, @@ -49,7 +29,7 @@ impl<'lex> Parser<'lex> { let mut stack = Vec::new(); let mut total_backticks = 0; loop { - let (_, token) = self.next()?; + let (span, token) = self.next()?; let r: Result = match token { Token::LeftParen => { stack.push(Group::Parens(Vec::new())); @@ -57,21 +37,27 @@ impl<'lex> Parser<'lex> { } Token::RightParen => { let group = stack.pop().ok_or_else( - || LispError::ParseError, // unmatched paren here + || (ParseError::new(span, ParseErrorKind::UnmatchedParen)), // unmatched paren here )?; match group { Group::Parens(v) => Ok(LispExpr::List(v)), - _ => Err(LispError::ParseError), + _ => Err(From::from(ParseError::new( + span, + ParseErrorKind::UnexpectedToken { + expected: "expression", + found: "(", + }, + ))), } } Token::Float(f) => f .parse::() .map(|n| LispExpr::Number(LispNumber::Float(n))) - .map_err(|_| LispError::ParseError), + .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), Token::Integer(i) => i .parse::() .map(|n| LispExpr::Number(LispNumber::Integer(n))) - .map_err(|_| LispError::ParseError), + .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), Token::String(s) => Ok(LispExpr::StringLit(s.into())), Token::Name(n) => Ok(name_expr(n)), Token::BackQuote => { @@ -85,7 +71,7 @@ impl<'lex> Parser<'lex> { } Token::Comma => { if total_backticks <= 0 { - return Err(LispError::ParseError); // unbalanced comma + return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma).into()); } total_backticks -= 1; if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() { @@ -97,7 +83,7 @@ impl<'lex> Parser<'lex> { } Token::CommaAt => { if total_backticks <= 0 { - return Err(LispError::ParseError); // unbalanced comma + return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma).into()); } total_backticks -= 1; stack.push(Group::CommaAt); @@ -118,9 +104,9 @@ impl<'lex> Parser<'lex> { }); if any_paren { - Err(LispError::ParseError) // unbalanced paren + Err(ParseError::new(span, ParseErrorKind::MissingCloseParen).into()) } else { - Err(LispError::ParseError) // unexpected eof + Err(ParseError::new(span, ParseErrorKind::UnexpectedEof).into()) } } }; @@ -178,7 +164,14 @@ impl<'lex> Parser<'lex> { match self.next()? { (_, Token::End) => Ok(expr), - _ => Err(LispError::ParseError), // too many tokens + (span, token) => Err(ParseError::new( + span, + ParseErrorKind::UnexpectedToken { + expected: "EOF", + found: token.name(), + }, + ) + .into()), } } -- cgit v1.2.3