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/lisp/lex.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++------- src/lisp/mod.rs | 3 ++- src/lisp/number.rs | 6 ++--- src/lisp/parse.rs | 55 ++++++++++++++++++++-------------------------- 4 files changed, 85 insertions(+), 43 deletions(-) (limited to 'src/lisp') 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