From 3c163eabc78ddbd26bb250ef5ad6da28cd61adc6 Mon Sep 17 00:00:00 2001 From: Sophie Forrest Date: Fri, 30 Aug 2024 23:35:45 +1200 Subject: feat: split engine into crates --- crates/brainf_lexer/Cargo.toml | 8 +++ crates/brainf_lexer/src/lexer.rs | 75 ++++++++++++++++++++++++++++ crates/brainf_lexer/src/lib.rs | 105 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 crates/brainf_lexer/Cargo.toml create mode 100644 crates/brainf_lexer/src/lexer.rs create mode 100644 crates/brainf_lexer/src/lib.rs (limited to 'crates/brainf_lexer') diff --git a/crates/brainf_lexer/Cargo.toml b/crates/brainf_lexer/Cargo.toml new file mode 100644 index 0000000..58be7a5 --- /dev/null +++ b/crates/brainf_lexer/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "brainf_lexer" +version = "0.1.0" +edition = "2021" + +[dependencies] +logos = "0.13.0" +thiserror = "1.0.44" diff --git a/crates/brainf_lexer/src/lexer.rs b/crates/brainf_lexer/src/lexer.rs new file mode 100644 index 0000000..b95cd87 --- /dev/null +++ b/crates/brainf_lexer/src/lexer.rs @@ -0,0 +1,75 @@ +//! Lexer implementation using logos. + +#![expect(clippy::indexing_slicing)] + +use logos::{Lexer, Logos}; + +/// List of operator codes for the lexer +/// Note: Any input symbol that is not in this list is a comment + +fn loop_callback(lex: &Lexer) -> (usize, usize) { + let span = lex.span(); + + (span.start, span.len()) +} + +/// List of Tokens for the lexer +/// Note: Any input symbol that is not in this list is a comment +#[derive(Clone, Copy, Debug, Logos, PartialEq, Eq)] +#[logos(skip r"[^<>+\-.,\[\]]+")] +pub enum Token { + /// `>` + /// + /// Increment the data pointer by one (to point to the next cell to the + /// right). + #[token(">")] + IncrementPointer, + + /// `<` + /// + /// Decrement the data pointer by one (to point to the next cell to the + /// left). + #[token("<")] + DecrementPointer, + + /// `+` + /// + /// Increment the byte at the data pointer by one. + #[token("+")] + IncrementByte, + + /// `-` + /// + /// Decrement the byte at the data pointer by one. + #[token("-")] + DecrementByte, + + /// `.` + /// + /// Output the byte at the data pointer. + #[token(".")] + OutputByte, + + /// `,` + /// + /// Accept one byte of input, storing its value in the byte at the data + /// pointer. + #[token(",")] + InputByte, + + /// `[` + /// + /// If the byte at the data pointer is zero, then instead of moving the + /// instruction pointer forward to the next command, jump it forward to the + /// command after the matching ] command. + #[token("[", loop_callback)] + StartLoop((usize, usize)), + + /// `]` + /// + /// If the byte at the data pointer is nonzero, then instead of moving the + /// instruction pointer forward to the next command, jump it back to the + /// command after the matching [ command. + #[token("]", loop_callback)] + EndLoop((usize, usize)), +} diff --git a/crates/brainf_lexer/src/lib.rs b/crates/brainf_lexer/src/lib.rs new file mode 100644 index 0000000..7f6e5be --- /dev/null +++ b/crates/brainf_lexer/src/lib.rs @@ -0,0 +1,105 @@ +#![feature(lint_reasons)] +#![deny(clippy::complexity)] +#![deny(clippy::nursery)] +#![deny(clippy::pedantic)] +#![deny(clippy::perf)] +#![deny(clippy::suspicious)] +#![deny(clippy::alloc_instead_of_core)] +#![deny(clippy::as_underscore)] +#![deny(clippy::clone_on_ref_ptr)] +#![deny(clippy::create_dir)] +#![warn(clippy::dbg_macro)] +#![deny(clippy::default_numeric_fallback)] +#![deny(clippy::default_union_representation)] +#![deny(clippy::deref_by_slicing)] +#![deny(clippy::empty_structs_with_brackets)] +#![deny(clippy::exit)] +#![deny(clippy::expect_used)] +#![deny(clippy::filetype_is_file)] +#![deny(clippy::fn_to_numeric_cast)] +#![deny(clippy::format_push_string)] +#![deny(clippy::get_unwrap)] +#![deny(clippy::if_then_some_else_none)] +#![allow( + clippy::implicit_return, + reason = "returns should be done implicitly, not explicitly" +)] +#![deny(clippy::indexing_slicing)] +#![deny(clippy::large_include_file)] +#![deny(clippy::let_underscore_must_use)] +#![deny(clippy::lossy_float_literal)] +#![deny(clippy::map_err_ignore)] +#![deny(clippy::mem_forget)] +#![deny(clippy::missing_docs_in_private_items)] +#![deny(clippy::missing_trait_methods)] +#![deny(clippy::mod_module_files)] +#![deny(clippy::multiple_inherent_impl)] +#![deny(clippy::mutex_atomic)] +#![deny(clippy::needless_return)] +#![deny(clippy::non_ascii_literal)] +#![deny(clippy::panic_in_result_fn)] +#![deny(clippy::pattern_type_mismatch)] +#![deny(clippy::rc_buffer)] +#![deny(clippy::rc_mutex)] +#![deny(clippy::rest_pat_in_fully_bound_structs)] +#![deny(clippy::same_name_method)] +#![deny(clippy::separated_literal_suffix)] +#![deny(clippy::str_to_string)] +#![deny(clippy::string_add)] +#![deny(clippy::string_slice)] +#![deny(clippy::string_to_string)] +#![allow( + clippy::tabs_in_doc_comments, + reason = "tabs are preferred for this project" +)] +#![deny(clippy::try_err)] +#![deny(clippy::undocumented_unsafe_blocks)] +#![deny(clippy::unnecessary_self_imports)] +#![deny(clippy::unneeded_field_pattern)] +#![deny(clippy::unwrap_in_result)] +#![deny(clippy::unwrap_used)] +#![warn(clippy::use_debug)] +#![deny(clippy::verbose_file_reads)] +#![deny(clippy::wildcard_dependencies)] +#![deny(clippy::wildcard_enum_match_arm)] +#![deny(missing_copy_implementations)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(single_use_lifetimes)] +#![deny(unsafe_code)] +#![deny(unused)] + +//! # `brainf_lexer` +//! +//! Implementation of a Brainfuck lexer in Rust. + +mod lexer; + +pub use lexer::Token; +use logos::Logos; +use thiserror::Error; + +/// Error type for lexer. +#[derive(Clone, Copy, Debug, Error)] +pub enum Error { + /// Logos was unable to lex part of the input. + #[error("lexer was unable to lex input")] + LexingError, +} + +/// Lexes the Brainfuck input, returning a Vec of Tokens. +/// +/// # Errors +/// +/// This function will return an error if the lexer is unable to lex one or more +/// of the input characters. +pub fn lex(input: &str) -> Result, Error> { + lexer::Token::lexer(input).try_fold(Vec::new(), |mut arr, result| { + result + .map_or(Err(()), |token| { + arr.push(token); + Ok(arr) + }) + .map_err(|_err| Error::LexingError) + }) +} -- cgit 1.4.1