Compare commits

...

2 Commits

Author SHA1 Message Date
bad 74770a7a31 Add basic unit tests 2022-04-03 21:54:39 +02:00
bad b7f6705d62 More refactoring 2022-04-03 21:54:26 +02:00
8 changed files with 139 additions and 56 deletions

View File

@ -12,8 +12,10 @@ pub enum ASTParsingError {
}
impl std::fmt::Display for ASTParsingError {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::UnmatchedBrace => write!(f, "Unmatched brace"),
}
}
}
impl std::error::Error for ASTParsingError {}

View File

@ -37,3 +37,37 @@ impl<'a> From<&'a OwnedLocation> for Location<'a> {
pub trait ErrorWithLocation: Error {
fn get_location(&self) -> Location;
}
#[derive(Debug)]
pub struct ErrorLocationWrapper<T: Error> {
inner: T,
location: OwnedLocation,
}
impl<T: Error> ErrorLocationWrapper<T> {
pub fn new(inner: T, location: OwnedLocation) -> Self {
Self { inner, location }
}
}
impl<T: std::error::Error> ErrorWithLocation for ErrorLocationWrapper<T> {
fn get_location(&self) -> Location {
(&self.location).into()
}
}
impl<T: Error> std::fmt::Display for ErrorLocationWrapper<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"An error occured: {} \n On line: {}, col: {}",
self.inner, self.location.line, self.location.col
)
}
}
impl<T: Error> Error for ErrorLocationWrapper<T> {
fn cause(&self) -> Option<&dyn Error> {
Some(&self.inner)
}
}

View File

@ -1,9 +1,18 @@
use std::fmt::Display;
use super::types;
use crate::ast::astnode::{self, UnaryOperator};
#[derive(Debug)]
pub struct RuntimeError;
impl Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "A runtime error occured")
}
}
impl std::error::Error for RuntimeError {}
pub trait Interpret {
fn interpret(&self) -> Result<types::Value, RuntimeError>;
}

View File

@ -2,29 +2,7 @@ use crate::error::{ErrorWithLocation, Location, OwnedLocation};
use core::fmt;
use std::error::Error;
#[derive(Debug)]
pub struct LexingError {
pub location: OwnedLocation,
pub kind: LexingErrorKind,
}
impl fmt::Display for LexingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error: {}", self.kind)
}
}
impl ErrorWithLocation for LexingError {
fn get_location<'a>(&'a self) -> Location<'a> {
(&self.location).into()
}
}
impl Error for LexingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.kind)
}
}
pub type LexingError = crate::error::ErrorLocationWrapper<LexingErrorKind>;
#[derive(Debug)]
pub enum LexingErrorKind {

View File

@ -68,6 +68,10 @@ impl<'a, 'b> Lexer<'a, 'b> {
self.get_token(token_type)
}
fn get_error(&self, error: LexingErrorKind) -> LexingError {
LexingError::new(error, self.source_iter.get_location(self.file).into())
}
fn scan_token(&mut self) -> Option<Result<Token<'b>, LexingError>> {
Some(Ok(match self.source_iter.next()? {
'(' => self.get_token(token::TokenType::LeftParen),
@ -103,11 +107,8 @@ impl<'a, 'b> Lexer<'a, 'b> {
),
'"' => {
let mut string = String::new();
let unmatched_char_error = Some(Err(LexingError {
kind: LexingErrorKind::UnmatchedQuote,
location: self.source_iter.get_location(self.file).into(),
}));
let unmatched_char_error =
Some(Err(self.get_error(LexingErrorKind::UnmatchedQuote)));
loop {
let next_char = self.source_iter.next();
match next_char {
@ -156,12 +157,7 @@ impl<'a, 'b> Lexer<'a, 'b> {
}
// Ignore whitespace
' ' | '\r' | '\t' | '\n' => return None,
c => {
return Some(Err(LexingError {
location: self.source_iter.get_location(self.file).into(),
kind: LexingErrorKind::UnexpectedCharacter(c),
}))
}
c => return Some(Err(self.get_error(LexingErrorKind::UnexpectedCharacter(c)))),
}))
}
}

62
src/lib.rs Normal file
View File

@ -0,0 +1,62 @@
pub mod ast;
pub mod error;
pub mod interpreter;
pub mod lexer;
use interpreter::ast_walker::Interpret;
use interpreter::types::Value;
use lexer::Lexer;
pub fn run(code: &str) -> Result<Value, run::Error> {
let mut lexer = Lexer::new(code, None);
let tokens = lexer.scan_tokens()?;
let mut parser = crate::ast::parser::Parser::new(tokens.into_iter());
let expressions = parser.scan_expressions()?;
Ok(expressions[0].interpret()?)
}
mod run {
use std::fmt::Display;
use super::ast;
use super::interpreter::ast_walker::RuntimeError;
use super::lexer;
use from_variants::FromVariants;
#[derive(Debug, FromVariants)]
pub enum Error {
Lexing(Vec<lexer::LexingError>),
ASTParsing(Vec<ast::parser::ASTParsingError>),
Runtime(RuntimeError),
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let error_kind = match *self {
Error::Lexing(_) => "lexing",
Error::ASTParsing(_) => "ast generation",
Error::Runtime(_) => "runtime",
};
write!(f, "Errors occured during {error_kind}\n")?;
match *self {
Error::Lexing(ref errors) => {
for error in errors {
write!(f, "{error} \n")?;
}
}
Error::ASTParsing(ref errors) => {
for error in errors {
write!(f, "{error} \n")?;
}
}
Error::Runtime(ref error) => write!(f, "{error} \n")?,
};
Ok(())
}
}
impl std::error::Error for Error {}
}

View File

@ -1,10 +1,4 @@
mod ast;
mod error;
mod interpreter;
mod lexer;
use interpreter::ast_walker::Interpret;
use lexer::Lexer;
use crftng_intrprtrs::run;
use std::{fs, io, path};
use tracing::Level;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
@ -27,7 +21,10 @@ fn run_repl() {
let line = line_buf.trim();
if !line.is_empty() {
run(line).unwrap();
match run(line) {
Ok(v) => println!("{:?}", v),
Err(e) => println!("{}", e),
}
line_buf.clear();
}
}
@ -36,18 +33,6 @@ fn run_repl() {
}
}
fn run(code: &str) -> Result<(), Vec<lexer::LexingError>> {
let mut lexer = Lexer::new(code, None);
let tokens = lexer.scan_tokens()?;
println!("{:?}", tokens);
let mut parser = crate::ast::parser::Parser::new(tokens.into_iter());
let expressions = parser.scan_expressions().unwrap();
println!("{:?}", expressions);
println!("{:?}", expressions[0].interpret());
Ok(())
}
fn main() {
dotenv::dotenv().ok();
color_eyre::install().expect("Failed to install color-eyre");

17
tests/integration_test.rs Normal file
View File

@ -0,0 +1,17 @@
use crftng_intrprtrs::run;
use crftng_intrprtrs::interpreter::types;
#[test]
fn test_one_equality() {
run_check_result_eq_bool("1 == 1", true);
run_check_result_eq_bool("1 >= 1", true);
run_check_result_eq_bool("1 <= 1", true);
run_check_result_eq_bool("1 != 1", false);
run_check_result_eq_bool("1 > 1", false);
run_check_result_eq_bool("1 < 1", false);
}
fn run_check_result_eq_bool(code: &str, value: bool) {
assert_eq!(run(code).unwrap(), types::Value::Primitive(types::Primitive::Bool(value)))
}