### Run JSON Parser Example Source: https://logos.maciej.codes/print.html/index Provides the command to compile and run the JSON parser example. It requires cloning the repository and executing the `cargo run` command with the `--example json` flag and the path to an example JSON file. ```bash cargo run --example json examples/example.json ``` -------------------------------- ### Running the String Interpolation Example Source: https://logos.maciej.codes/print.html/index This command demonstrates how to execute the string interpolation example provided within the Logos crate. It assumes you have cloned the Logos repository and are in the correct directory. ```bash cargo run --example string-interpolation ``` -------------------------------- ### Install mdBook Source: https://logos.maciej.codes/print.html/index Installs mdBook, a tool used to build and serve documentation written in Markdown. This is required for building the Logos book. ```bash cargo install mdbook ``` -------------------------------- ### String Interpolation Example Source: https://logos.maciej.codes/print.html/index This code snippet provides an example of string interpolation. It defines variables and demonstrates how to substitute these variables within strings. The example showcases nested interpolation as well. ```text __ name = 'Mark' greeting = 'Hi ${name}!' surname = 'Scott' greeting2 = 'Hi ${name ' ' surname}!' greeting3 = 'Hi ${name ' ${surname}!'}' ``` -------------------------------- ### Install Rust Nightly Toolchain Source: https://logos.maciej.codes/print.html/index Installs the latest stable version of the Rust nightly toolchain, which is required for building Logos documentation. ```bash rustup install nightly ``` -------------------------------- ### Build Documentation with Nightly Rust Source: https://logos.maciej.codes/print.html/index Builds the documentation for Logos using the nightly Rust toolchain with specific flags for docs.rs compatibility and example scraping. The '--open' flag will open the documentation in a browser. ```bash RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc \ --features debug \ -Zunstable-options \ -Zrustdoc-scrape-examples \ --no-deps \ --open \ ``` -------------------------------- ### Install mdBook Linkchecker Source: https://logos.maciej.codes/print.html/index Installs mdbook-linkcheck2, a plugin for mdBook that checks for broken links within the documentation. ```bash cargo install mdbook-linkcheck2 ``` -------------------------------- ### Full Example: Lexing with Extras for Location (Rust) Source: https://logos.maciej.codes/print.html/index This complete Rust program demonstrates using `Logos::Extras` to track token locations. It defines tokens, implements callback functions for newline and word processing, and then uses the lexer to iterate through input, printing word tokens with their line and column numbers. ```rust use logos::{Lexer, Logos, Skip}; use std::env; use std::fs; /// Update the line count and the char index. fn newline_callback(lex: &mut Lexer) -> Skip { lex.extras.0 += 1; lex.extras.1 = lex.span().end; Skip } /// Compute the line and column position for the current word. fn word_callback(lex: &mut Lexer) -> (usize, usize) { let line = lex.extras.0; let column = lex.span().start - lex.extras.1; (line, column) } /// Simple tokens to retrieve words and their location. #[derive(Debug, Logos)] #[logos(extras = (usize, usize))] #[logos(skip(r"\n", newline_callback))] enum Token { #[regex(r"\w+", word_callback)] Word((usize, usize)), } fn main() { let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) .expect("Failed to read file"); let mut lex = Token::lexer(src.as_str()); while let Some(token) = lex.next() { if let Ok(Token::Word((line, column))) = token { println!("Word '{}' found at ({}, {})", lex.slice(), line, column); } } } ``` -------------------------------- ### Logos Lexer Tokenization Example (Rust) Source: https://logos.maciej.codes/print.html/index Demonstrates basic tokenization using Logos with literal strings and regular expressions. It defines an enum `Token` with variants for 'fast', '.', and general text, then lexes an input string, printing each recognized token. ```rust use logos::Logos; #[derive(Debug, Logos, PartialEq)] enum Token { // Tokens can be literal strings, of any length. #[token("fast")] Fast, #[token(".")] Period, // Or regular expressions. #[regex("[a-zA-Z]+")] Text, } fn main() { let input = "Create ridiculously fast Lexers."; let mut lexer = Token::lexer(input); while let Some(token) = lexer.next() { println!("{:?}", token); } } ``` -------------------------------- ### Full Brainfuck Interpreter Setup Source: https://logos.maciej.codes/print.html/index Provides the complete Rust code for a Brainfuck interpreter. It includes the Op enum definition, helper functions for I/O, and the main execution logic with pre-processing for jump commands. This code can be compiled and run using Cargo. ```rust use logos::Logos; use std::collections::HashMap; use std::env; use std::fs; use std::io::{self, Read}; /// Each [`Op`] variant is a single character. #[derive(Debug, Logos)] // skip all non-op characters #[logos(skip(".|\n", priority = 0))] enum Op { /// Increment pointer. #[token(">")] IncPointer, /// Decrement pointer. #[token("<")] DecPointer, /// Increment data at pointer. #[token("+")] IncData, /// Decrement data at pointer. #[token("-")] DecData, /// Output data at pointer. #[token(".")] OutData, /// Input (read) to data at pointer. #[token(",")] InpData, /// Conditionally jump to matching `']'`. #[token("[")] CondJumpForward, /// Conditionally jump to matching `'['`. #[token("]")] CondJumpBackward, } /// Print one byte to the terminal. #[inline(always)] fn print_byte(byte: u8) { print!("{}", byte as char); } /// Read one byte from the terminal. #[inline(always)] fn read_byte() -> u8 { let mut input = [0u8; 1]; io::stdin() .read_exact(&mut input) .expect("An error occurred while reading byte!"); input[0] } /// Execute Brainfuck code from a string slice. pub fn execute(code: &str) { let operations: Vec<_> = Op::lexer(code).collect::>().unwrap(); let mut data = [0u8; 30_000]; // Minimum recommended size let mut pointer: usize = 0; let len = operations.len(); // We pre-process matching jump commands, and we create // a mapping between them. let mut queue = Vec::new(); let mut pairs = HashMap::new(); let mut pairs_reverse = HashMap::new(); for (i, op) in operations.iter().enumerate() { match op { Op::CondJumpForward => queue.push(i), Op::CondJumpBackward => { if let Some(start) = queue.pop() { pairs.insert(start, i); pairs_reverse.insert(i, start); } else { panic!( "Unexpected conditional backward jump at position {}, does not match any '['", i ); } } _ => (), } } if !queue.is_empty() { // This part of the code is incomplete in the provided snippet. // It should handle unmatched '[' characters. } let mut i: usize = 0; // True program execution. loop { match operations[i] { Op::IncPointer => pointer += 1, Op::DecPointer => pointer -= 1, Op::IncData => data[pointer] = data[pointer].wrapping_add(1), Op::DecData => data[pointer] = data[pointer].wrapping_sub(1), Op::OutData => print_byte(data[pointer]), Op::InpData => data[pointer] = read_byte(), Op::CondJumpForward => { if data[pointer] == 0 { // Skip until matching end. i = *pairs.get(&i).unwrap(); } } Op::CondJumpBackward => { if data[pointer] != 0 { // Go back to matching start. i = *pairs_reverse.get(&i).unwrap(); } } } i += 1; if i >= len { break; } } } ``` -------------------------------- ### Iterate Through Tokens using for loop with Logos in Rust Source: https://logos.maciej.codes/print.html/index Shows how to iterate over the tokens generated by the Logos lexer using a standard `for .. in` loop in Rust. This example demonstrates matching on the `Result` of each token and handling potential errors. ```rust #![allow(unused)] fn main() { for result in Token::lexer("Create ridiculously fast Lexers.") { match result { Ok(token) => println!("{:#?}", token), Err(e) => panic!("some error occurred: {}", e), } } } ``` -------------------------------- ### Define and Use Subpatterns in Logos (Rust) Source: https://logos.maciej.codes/print.html/index This Rust code snippet demonstrates how to define reusable subpatterns using the `logos` crate. It defines `alpha`, `digit`, and `alphanum` subpatterns and then uses them within `#[regex]` attributes to match `Word`, `Number`, `TwoAlphanum`, and `ThreeAlphanum` tokens. The example includes a `main` function to lex a sample string and assert the matched tokens and slices. ```rust #[macro_use] extern crate logos; use logos::Logos; #[derive(Logos, Debug, PartialEq)] #[logos(skip r"\s+")] #[logos(subpattern alpha = r"[a-zA-Z]")] #[logos(subpattern digit = r"[0-9]")] #[logos(subpattern alphanum = r"(?&alpha)|(?&digit)")] enum Token { #[regex("(?&alpha)+")] Word, #[regex("(?&digit)+")] Number, #[regex("(?&alphanum){2}")] TwoAlphanum, #[regex("(?&alphanum){3}")] ThreeAlphanum, } fn main() { let mut lex = Token::lexer("Word 1234 ab3 12"); assert_eq!(lex.next(), Some(Ok(Token::Word))); assert_eq!(lex.slice(), "Word"); assert_eq!(lex.next(), Some(Ok(Token::Number))); assert_eq!(lex.slice(), "1234"); assert_eq!(lex.next(), Some(Ok(Token::ThreeAlphanum))); assert_eq!(lex.slice(), "ab3"); assert_eq!(lex.next(), Some(Ok(Token::TwoAlphanum))); assert_eq!(lex.slice(), "12"); assert_eq!(lex.next(), None); } ``` -------------------------------- ### Tokenize String using Logos Lexer Iterator in Rust Source: https://logos.maciej.codes/print.html/index Illustrates how to use the `Logos::lexer` method to convert a string slice into an iterator of tokens. This example shows how to consume the iterator, check token types, spans, and slices, and handle the end of the input. ```rust #![allow(unused)] fn main() { let mut lex = Token::lexer("Create ridiculously fast Lexers."); assert_eq!(lex.next(), Some(Ok(Token::Text))); assert_eq!(lex.span(), 0..6); assert_eq!(lex.slice(), "Create"); assert_eq!(lex.next(), Some(Ok(Token::Text))); assert_eq!(lex.span(), 7..19); assert_eq!(lex.slice(), "ridiculously"); assert_eq!(lex.next(), Some(Ok(Token::Fast))); assert_eq!(lex.span(), 20..24); assert_eq!(lex.slice(), "fast"); assert_eq!(lex.next(), Some(Ok(Token::Text))); assert_eq!(lex.slice(), "Lexers"); assert_eq!(lex.span(), 25..31); assert_eq!(lex.next(), Some(Ok(Token::Period))); assert_eq!(lex.span(), 31..32); assert_eq!(lex.slice(), "."); assert_eq!(lex.next(), None); } ``` -------------------------------- ### Build Logos Crate for Fuzzing with cargo-afl Source: https://logos.maciej.codes/print.html/index Builds the Logos crate specifically for fuzzing using cargo-afl. Ensure cargo-afl is installed and you are in the 'fuzz' directory before running this command. ```bash cargo afl build ``` -------------------------------- ### Define Lexer Contexts for Variable Definitions and String Interpolation Source: https://logos.maciej.codes/print.html/index Defines three distinct lexer contexts using the Logos derive macro. `VariableDefinitionContext` handles general syntax like variable names and assignment. `StringContext` parses string literals and detects interpolation start. `StringInterpolationContext` processes the content within interpolation blocks. Each lexer can carry extra data, like a `SymbolTable` for storing variable definitions. ```rust use logos::Logos; use std::collections::HashMap; type SymbolTable = HashMap; #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] #[logos(extras = SymbolTable)] enum VariableDefinitionContext { #[regex(r"[[:alpha:]][[:alnum:]]*", variable_definition)] Id((String /* variable name */, String /* value */)), #[token("=")] Equals, #[token("'")] Quote, } #[derive(Logos, Debug, PartialEq, Clone)] #[logos(extras = SymbolTable)] enum StringContext { #[token("'")] Quote, #[regex("[^'$"]+")] Content, #[token("${", evaluate_interpolation)] InterpolationStart(String /* evaluated value of the interpolation */), #[token("$")] DollarSign, } #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] #[logos(extras = SymbolTable)] enum StringInterpolationContext { #[regex(r"[[:alpha:]][[:alnum:]]*", get_variable_value)] Id(String /* value for the given id */), #[token("'")] Quote, #[token("}")] InterpolationEnd, } ``` -------------------------------- ### Implement Callbacks for Extras in Logos (Rust) Source: https://logos.maciej.codes/print.html/index These Rust functions are callbacks for the `Logos` lexer. `newline_callback` increments the line count and updates the character index for the start of the next line. `word_callback` calculates the line and column of a word token based on the current extras. ```rust /// Update the line count and the char index. fn newline_callback(lex: &mut Lexer) -> Skip { lex.extras.0 += 1; lex.extras.1 = lex.span().end; Skip } /// Compute the line and column position for the current word. fn word_callback(lex: &mut Lexer) -> (usize, usize) { let line = lex.extras.0; let column = lex.span().start - lex.extras.1; (line, column) } ``` -------------------------------- ### Serve Logos Book Source: https://logos.maciej.codes/print.html/index Builds and serves the Logos book locally using mdBook. Changes in the './book' folder will trigger automatic rebuilds and live reloads in the browser. ```bash mdbook serve book --open ``` -------------------------------- ### Logos Debug Output: Leaf Patterns (Text) Source: https://logos.maciej.codes/print.html/index This is an example of the debug output from Logos when the `debug` feature is enabled, showing the 'leaves' or patterns identified by the lexer. Each leaf corresponds to a `#[token(...)]` or `#[regex(...)]` attribute. ```text 0: #[token("fast")] ::Fast (priority: 8) 1: #[token(".")] ::Period (priority: 2) 2: #[regex("[a-zA-Z]+")] ::Text (priority: 2) ``` -------------------------------- ### Rust: Context Switching with Lexer Morphing Source: https://logos.maciej.codes/print.html/index Illustrates dynamic context switching in Logos using the `morph` method. This allows switching from a `CToken` lexer to a `PythonToken` lexer within a callback, enabling the parsing of nested language structures like Python blocks within C code. The `python_block_callback` function demonstrates this by cloning the lexer, morphing it, processing Python tokens, and then morphing back. ```rust #![allow(unused)] fn main() { #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] enum CToken { /* Tokens supporting C syntax */ // ... #[regex(r#"extern\s+\"python\"\s*\{"#, python_block_callback)] PythonBlock(Vec), } #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] enum PythonToken { #[token("}")] ExitPythonBlock, /* Tokens supporting Python syntax */ // ... } fn python_block_callback(lex: &mut Lexer) -> Option> { let mut python_lexer = lex.clone().morph::(); let mut tokens = Vec::new(); while let Some(token) = python_lexer.next() { match token { Ok(PythonToken::ExitPythonBlock) => break, Err(_) => return None, Ok(tok) => tokens.push(tok), } } *lex = python_lexer.morph(); Some(tokens) } } ``` -------------------------------- ### Implement Main Function for Testing Variable Definitions and Interpolation Source: https://logos.maciej.codes/print.html/index Provides a `main` function that initializes a lexer for `VariableDefinitionContext` with sample input containing variable assignments and string interpolations. It then uses a helper function `test_variable_definition` to assert that the lexer correctly identifies and extracts variable names and their interpolated values. ```rust fn test_variable_definition( expected_id: &str, expected_value: &str, token: Option>, ) { if let Some(Ok(VariableDefinitionContext::Id((id, value)))) = token { assert_eq!(id, expected_id); assert_eq!(value, expected_value); } else { panic!("Expected key: {} not found", expected_id); } } fn main() { let mut lex = VariableDefinitionContext::lexer( "\\ name = 'Mark'\n\ greeting = 'Hi ${name}!'\n\ surname = 'Scott'\n\ greeting2 = 'Hi ${name ' ' surname}!'\n\ greeting3 = 'Hi ${name ' ${surname}!'}!'\n\ ", ); test_variable_definition("name", "Mark", lex.next()); test_variable_definition("greeting", "Hi Mark!", lex.next()); test_variable_definition("surname", "Scott", lex.next()); test_variable_definition("greeting2", "Hi Mark Scott!", lex.next()); test_variable_definition("greeting3", "Hi Mark Scott!!", lex.next()); } ``` -------------------------------- ### Rust Calculator: Tokenization, Parsing, and Evaluation Source: https://logos.maciej.codes/print.html/index This Rust code defines a calculator that tokenizes input expressions using Logos, builds an Abstract Syntax Tree (AST) with Chumsky, and evaluates the AST. It handles integers, arithmetic operators (+, -, *, /), and parentheses. The output includes the AST representation and the computed result. ```rust use std::env; use chumsky::prelude::*; use logos::Logos; #[derive(Logos, Debug, PartialEq, Eq, Hash, Clone)] #[logos(skip r"[ \t\n]+")] #[logos(error = String)] enum Token { #[token("+")] Plus, #[token("-")] Minus, #[token("*")] Multiply, #[token("/")] Divide, #[token("(")] LParen, #[token(")")] RParen, #[regex("[0-9]+", |lex| lex.slice().parse::().unwrap())] Integer(isize), } #[derive(Debug)] enum Expr { // Integer literal. Int(isize), // Unary minus. Neg(Box), // Binary operators. Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), } impl Expr { fn eval(&self) -> isize { match self { Expr::Int(n) => *n, Expr::Neg(rhs) => -rhs.eval(), Expr::Add(lhs, rhs) => lhs.eval() + rhs.eval(), Expr::Sub(lhs, rhs) => lhs.eval() - rhs.eval(), Expr::Mul(lhs, rhs) => lhs.eval() * rhs.eval(), Expr::Div(lhs, rhs) => lhs.eval() / rhs.eval(), } } } #[allow(clippy::let_and_return)] fn parser<'src>( ) -> impl Parser<'src, &'src [Token], Expr, chumsky::extra::Err>> { recursive(|p| { let atom = { let parenthesized = p .clone() .delimited_by(just(Token::LParen), just(Token::RParen)); let integer = select! { Token::Integer(n) => Expr::Int(n), }; parenthesized.or(integer) }; let unary = just(Token::Minus) .repeated() .foldr(atom, |_op, rhs| Expr::Neg(Box::new(rhs))); let binary_1 = unary.clone().foldl( just(Token::Multiply) .or(just(Token::Divide)) .then(unary) .repeated(), |lhs, (op, rhs)| match op { Token::Multiply => Expr::Mul(Box::new(lhs), Box::new(rhs)), Token::Divide => Expr::Div(Box::new(lhs), Box::new(rhs)), _ => unreachable!(), }, ); let binary_2 = binary_1.clone().foldl( just(Token::Plus) .or(just(Token::Minus)) .then(binary_1) .repeated(), |lhs, (op, rhs)| match op { Token::Plus => Expr::Add(Box::new(lhs), Box::new(rhs)), Token::Minus => Expr::Sub(Box::new(lhs), Box::new(rhs)), _ => unreachable!(), }, ); binary_2 }) } fn main() { //reads the input expression from the command line let input = env::args() .nth(1) .expect("Expected expression argument (e.g. `1 + 7 * (3 - 4) / 5`)"); //creates a lexer instance from the input let lexer = Token::lexer(&input); //splits the input into tokens, using the lexer let mut tokens = vec![]; for (token, span) in lexer.spanned() { match token { Ok(token) => tokens.push(token), Err(e) => { println!("lexer error at {:?}: {}", span, e); return; } } } //parses the tokens to construct an AST let ast = match parser().parse(&tokens).into_result() { Ok(expr) => { println!("[AST]\n{:#?}", expr); expr } Err(e) => { println!("parse error: {:#?}", e); return; } }; //evaluates the AST to get the result println!("\n[result]\n{}", ast.eval()); } ``` -------------------------------- ### Main Function: File Parsing and Error Reporting in Rust Source: https://logos.maciej.codes/print.html/index The main function reads a file specified as a command-line argument, lexes the file content, and attempts to parse it as JSON. If parsing is successful, it prints the resulting value. If an error occurs during parsing, it uses the `ariadne` crate to generate a detailed error report, including the error message and the location of the error in the source file. ```rust fn main() { let filename = env::args().nth(1).expect("Expected file argument"); let src = fs::read_to_string(&filename).expect("Failed to read file"); let mut lexer = Token::lexer(src.as_str()); match parse_value(&mut lexer) { Ok(value) => println!("{:#?}", value), Err((msg, span)) => { use ariadne::{ColorGenerator, Label, Report, ReportKind, Source}; let mut colors = ColorGenerator::new(); let a = colors.next(); Report::build(ReportKind::Error, &filename, 12) .with_message("Invalid JSON".to_string()) .with_label( Label::new((&filename, span)) .with_message(msg) .with_color(a), ) .finish() .eprint((&filename, Source::from(src))) .unwrap(); } } } ``` -------------------------------- ### Run Workspace Benchmarks Source: https://logos.maciej.codes/print.html/index Executes the benchmarks defined for the Logos workspace. This helps in performance analysis and optimization. ```bash cargo bench --workspace --benches ``` -------------------------------- ### Rust: Custom Number Parsing with Lexer Callbacks Source: https://logos.maciej.codes/print.html/index Demonstrates using callbacks in Logos to parse numbers with suffixes like 'k' (thousands) and 'm' (millions). The `kilo` and `mega` functions parse the numeric part and apply the multiplier. The `Token` enum uses these callbacks for specific regex patterns. The `main` function shows how to use the lexer with these custom tokens. ```rust #[allow(unused)] use logos::{Logos, Lexer}; // Note: callbacks can return `Option` or `Result` fn kilo(lex: &mut Lexer) -> Option { let slice = lex.slice(); let n: u64 = slice[..slice.len() - 1].parse().ok()?; // skip 'k' Some(n * 1_000) } fn mega(lex: &mut Lexer) -> Option { let slice = lex.slice(); let n: u64 = slice[..slice.len() - 1].parse().ok()?; // skip 'm' Some(n * 1_000_000) } #[derive(Logos, Debug, PartialEq)] #[logos(skip r"[ \t\n\f]+")] enum Token { // Callbacks can use closure syntax, or refer // to a function defined elsewhere. // // Each pattern can have its own callback. #[regex("[0-9]+", |lex| lex.slice().parse().ok())] #[regex("[0-9]+k", kilo)] #[regex("[0-9]+m", mega)] Number(u64), } fn main() { let mut lex = Token::lexer("5 42k 75m"); assert_eq!(lex.next(), Some(Ok(Token::Number(5)))); assert_eq!(lex.slice(), "5"); assert_eq!(lex.next(), Some(Ok(Token::Number(42_000)))); assert_eq!(lex.slice(), "42k"); assert_eq!(lex.next(), Some(Ok(Token::Number(75_000_000)))); assert_eq!(lex.slice(), "75m"); assert_eq!(lex.next(), None); } ``` -------------------------------- ### Clone Logos Repository Source: https://logos.maciej.codes/print.html/index Clones the Logos project repository from GitHub. Ensure you clone your fork if you have one. ```bash git clone https://github.com/maciejhirsz/logos.git ``` -------------------------------- ### Logos Attribute Syntax and Options Source: https://logos.maciej.codes/print.html/index Demonstrates the various optional attributes that can be applied to an enum for Logos lexer customization. These include skipping patterns, defining extra data types, custom error types, specifying the Logos crate path, enabling/disabling UTF-8 support, and defining subpatterns. ```rust #![allow(unused)] fn main() { #[derive(Logos)] #[logos(skip "regex literal")] #[logos(skip(("regex literal"[, callback, priority = ])))] #[logos(extras = ExtrasType)] #[logos(error = ErrorType)] #[logos(crate = path::to::logos)] #[logos(utf8 = true)] #[logos(subpattern subpattern_name = "regex literal")] enum Token { /* ... */ } } ``` -------------------------------- ### Logos Graph Export Configuration (Rust) Source: https://logos.maciej.codes/print.html/index Configures Logos to export Mermaid and DOT graph representations of the lexer's state machine. The `export_dir` attribute specifies the directory or file path for saving these visualizations. ```rust #[derive(Logos)] #[logos(export_dir = "path/to/export/dir")] enum Token { #[token("fast")] Fast, #[token(".")] Period, #[regex("[a-zA-Z]+")] Text, } ``` ```rust #[logos(export_dir = "export/graph.mmd")] ``` -------------------------------- ### Logos Lexer with Byte Slice Input (Rust) Source: https://logos.maciej.codes/print.html/index Demonstrates configuring the Logos lexer to accept a byte slice (`&[u8]`) as input instead of a UTF-8 string (`&str`) by using the `#[logos(utf8 = false)]` attribute. ```rust use logos::Logos; #[derive(Logos, Debug, PartialEq)] #[logos(utf8 = false)] // Instructs Logos to accept &[u8] enum Token { #[regex("[a-z]+")] Word, #[error] Error, } fn main() { let input_bytes = b"hello world"; // Input as byte slice let mut lexer = Token::lexer(input_bytes); assert_eq!(lexer.next(), Some(Token::Word)); assert_eq!(lexer.next(), Some(Token::Word)); assert_eq!(lexer.next(), None); } ``` -------------------------------- ### Run Fuzz Tests for Logos Crate with cargo-afl Source: https://logos.maciej.codes/print.html/index Executes fuzz tests on the Logos crate using cargo-afl. This command requires an input directory ('in') and an output directory ('out'). The target binary is specified as '../target/debug/logos-fuzz'. ```bash cargo afl fuzz -i in -o out ../target/debug/logos-fuzz ``` -------------------------------- ### Add Logos to Rust Project using Cargo Source: https://logos.maciej.codes/print.html/index This snippet shows how to include the Logos crate in your Rust project. You can either use the `cargo add logos` command or manually edit your `Cargo.toml` file to add the dependency. ```toml [dependencies] logos = "0.16.1" ``` -------------------------------- ### Logos Token and Regex Attribute Syntax (Rust) Source: https://logos.maciej.codes/print.html/index This Rust code snippet illustrates the syntax for using `#[token]` and `#[regex]` attributes with the `Logos` derive macro. It shows how to associate string literals or regular expressions with enum variants for token matching. Optional parameters like `callback`, `priority`, and `ignore` are also indicated. ```rust #![allow(unused)] fn main() { #[derive(Logos)] enum Token { #[token(literal [, callback, priority = , ignore(, ...)]] #[regex(literal [, callback, priority = , ignore(, ...)]] SomeVariant, } } ``` -------------------------------- ### Enabling Logos Debug Feature (TOML) Source: https://logos.maciej.codes/print.html/index Shows how to enable the `debug` feature for the Logos crate in a `Cargo.toml` file. This is necessary to generate detailed debugging output for the lexer's state machine. ```toml // Cargo.toml [dependencies] logos = { version = "1.2.3", features = ["debug"] } ``` -------------------------------- ### Format Code with Cargo Source: https://logos.maciej.codes/print.html/index Formats the code in the Logos project according to standard Rust formatting conventions. This should be run before submitting changes. ```bash cargo fmt ``` -------------------------------- ### Main Function for Calculator in Rust Source: https://logos.maciej.codes/print.html/index The `main` function orchestrates the calculator's operation in Rust. It reads an expression from command-line arguments, tokenizes it using a lexer, parses the tokens into an AST, and then evaluates the AST to print the final result. ```rust fn main() { //reads the input expression from the command line let input = env::args() .nth(1) .expect("Expected expression argument (e.g. `1 + 7 * (3 - 4) / 5`)"); //creates a lexer instance from the input let lexer = Token::lexer(&input); //splits the input into tokens, using the lexer let mut tokens = vec![]; for (token, span) in lexer.spanned() { match token { Ok(token) => tokens.push(token), Err(e) => { println!("lexer error at {:?}: {}", span, e); return; } } } //parses the tokens to construct an AST let ast = match parser().parse(&tokens).into_result() { Ok(expr) => { println!("[AST]\n{:#?}", expr); expr } Err(e) => { println!("parse error: {:#?}", e); return; } }; //evaluates the AST to get the result println!("\n[result]\n{}", ast.eval()); } ``` -------------------------------- ### Logos Lexer with Callback Error Handling Source: https://logos.maciej.codes/print.html/index Demonstrates using a callback function with the `#[logos(error(...))]` attribute to provide lexer context, such as the span, for error reporting. This allows for more informative error types than just a default value. ```rust #[derive(Logos)] #[logos(error(Range, callback = |lex| lex.span()))] enum Token { #[token("a")] A, #[token("b")] B, } ``` -------------------------------- ### Define JSON Tokens with Logos Source: https://logos.maciej.codes/print.html/index Defines the various tokens that constitute valid JSON syntax using the `logos` crate. It includes boolean values, braces, brackets, colon, comma, null, numbers (f64), and strings. Regular expressions are used for number and string tokenization. ```rust use logos::{Lexer, Logos, Span}; use std::collections::HashMap; use std::env; use std::fs; type Error = (String, Span); type Result = std::result::Result; /// All meaningful JSON tokens. /// /// > NOTE: regexes for [`Token::Number`] and [`Token::String`] may not /// > catch all possible values, especially for strings. If you find /// > errors, please report them so that we can improve the regex. #[derive(Debug, Logos)] #[logos(skip r"[ \t\r\n\f]+")] enum Token { #[token("false", |_| false)] #[token("true", |_| true)] Bool(bool), #[token("{ ")] BraceOpen, #[token("}")] BraceClose, #[token("[")] BracketOpen, #[token("]")] BracketClose, #[token(":")] Colon, #[token(",")] Comma, #[token("null")] Null, #[regex(r"-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?", |lex| lex.slice().parse::().unwrap())] Number(f64), #[regex(r#"([^"\\\x00-\x1F]|\\(["\\bnfrt/]|u[a-fA-F0-9]{4}))*"#", |lex| lex.slice().to_owned())] String(String), } ``` -------------------------------- ### Tokenize JSON with Logos in Rust Source: https://logos.maciej.codes/print.html/index Implements a lexer for JSON using the Logos library in Rust. It defines various token types like braces, brackets, colons, commas, null, booleans, numbers, and strings, using regular expressions for matching. It skips whitespace characters. ```rust /// All meaningful JSON tokens. /// /// > NOTE: regexes for [`Token::Number`] and [`Token::String`] may not /// > catch all possible values, especially for strings. If you find /// > errors, please report them so that we can improve the regex. #[derive(Debug, Logos)] #[logos(skip r"[ \t\r\n\f]+")] enum Token { #[token("false", |_| false)] #[token("true", |_| true)] Bool(bool), #[token("{ ")] BraceOpen, #[token("}")] BraceClose, #[token("[")] BracketOpen, #[token("]")] BracketClose, #[token(":")] Colon, #[token(",")] Comma, #[token("null")] Null, #[regex(r"-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?", |lex| lex.slice().parse::().unwrap())] Number(f64), #[regex(r#"([^"\\\x00-\x1F]|\\(["\\bnfrt/]|u[a-fA-F0-9]{4}))*"#", |lex| lex.slice().to_owned())] String(String), } ``` -------------------------------- ### Logos Regex with Allow Greedy Flag (Rust) Source: https://logos.maciej.codes/print.html/index Shows how to enable unbounded greedy dot repetition (`.*`) in Logos by adding `allow_greedy = true` to the `#[regex]` attribute. This is generally discouraged due to potential O(n^2) performance. ```rust use logos::Logos; #[derive(Logos, Debug, PartialEq)] enum Token { #[regex(r"a(.*)", allow_greedy = true)] AWithGreedyDot, #[error] Error, } fn main() { let mut lexer = Token::lexer("abc"); assert_eq!(lexer.next(), Some(Token::AWithGreedyDot)); // In a real scenario, this would consume the rest of the input // For demonstration, we just show the tokenization possibility. } ``` -------------------------------- ### Rust String Interpolation with Variable Definitions using Logos Source: https://logos.maciej.codes/print.html/index This Rust code defines lexer contexts for handling variable definitions and string interpolations. It uses the `logos` crate to tokenize input strings, resolve variable values from a symbol table, and construct interpolated strings. The `SymbolTable` is used to store variable names and their corresponding string values. ```rust use std::collections::HashMap; use logos::{Lexer, Logos}; type SymbolTable = HashMap; #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] #[logos(extras = SymbolTable)] enum VariableDefinitionContext { #[regex(r"[[:alpha:]][[:alnum:]]*", variable_definition)] Id((String /* variable name */, String /* value */)), #[token("=")] Equals, #[token("'")] Quote, } #[derive(Logos, Debug, PartialEq, Clone)] #[logos(extras = SymbolTable)] enum StringContext { #[token("'")] Quote, #[regex("[^'$"]+")] Content, #[token("${", evaluate_interpolation)] InterpolationStart(String /* evaluated value of the interpolation */), #[token("$")] DollarSign, } #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"\s+")] #[logos(extras = SymbolTable)] enum StringInterpolationContext { #[regex(r"[[:alpha:]][[:alnum:]]*", get_variable_value)] Id(String /* value for the given id */), #[token("'")] Quote, #[token("}")] InterpolationEnd, } fn get_string_content(lex: &mut Lexer) -> String { let mut s = String::new(); while let Some(Ok(token)) = lex.next() { match token { StringContext::Content => s.push_str(lex.slice()), StringContext::DollarSign => s.push('$'), StringContext::InterpolationStart(value) => s.push_str(&value), StringContext::Quote => break, } } s } fn variable_definition(lex: &mut Lexer) -> Option<(String, String)> { let id = lex.slice().to_string(); if let Some(Ok(VariableDefinitionContext::Equals)) = lex.next() { if let Some(Ok(VariableDefinitionContext::Quote)) = lex.next() { let mut lex2 = lex.clone().morph::(); let value = get_string_content(&mut lex2); *lex = lex2.morph(); lex.extras.insert(id.clone(), value.clone()); return Some((id, value)); } } None } fn evaluate_interpolation(lex: &mut Lexer) -> Option { let mut lex2 = lex.clone().morph::(); let mut interpolation = String::new(); while let Some(result) = lex2.next() { match result { Ok(token) => match token { StringInterpolationContext::Id(value) => interpolation.push_str(&value), StringInterpolationContext::Quote => { *lex = lex2.morph(); interpolation.push_str(&get_string_content(lex)); lex2 = lex.clone().morph(); } StringInterpolationContext::InterpolationEnd => break, }, Err(()) => panic!("Interpolation error"), } } *lex = lex2.morph(); Some(interpolation) } fn get_variable_value(lex: &mut Lexer) -> Option { if let Some(value) = lex.extras.get(lex.slice()) { return Some(value.clone()); } None } fn test_variable_definition( expected_id: &str, expected_value: &str, token: Option>, ) { if let Some(Ok(VariableDefinitionContext::Id((id, value)))) = token { assert_eq!(id, expected_id); assert_eq!(value, expected_value); } else { panic!("Expected key: {} not found", expected_id); } } fn main() { let mut lex = VariableDefinitionContext::lexer( "name = 'Mark'\n\ greeting = 'Hi ${name}!'\n\ surname = 'Scott'\n\ greeting2 = 'Hi ${name ' ' surname}!'\n\ greeting3 = 'Hi ${name ' ${surname}!'}!'\n\ ", ); test_variable_definition("name", "Mark", lex.next()); test_variable_definition("greeting", "Hi Mark!", lex.next()); test_variable_definition("surname", "Scott", lex.next()); test_variable_definition("greeting2", "Hi Mark Scott!", lex.next()); test_variable_definition("greeting3", "Hi Mark Scott!!", lex.next()); } ``` -------------------------------- ### State Machine Codegen: Loop with Match Statement in Rust Source: https://logos.maciej.codes/print.html/index Illustrates the state machine code generation strategy in Rust, where each state is an enum variant and transitions are handled within a loop using a match statement. This method avoids stack overflows but may have performance implications compared to tail call generation. ```Rust let mut state = State::State0; loop { match state { State::State0 => { match lexer.read() { 'a' => state = State::State1, 'b' => state = State::State2, _ => return Token::Error, } } // Etc... } } ```