MotoGarage_parser

Crates.ioMotoGarage_parser
lib.rsMotoGarage_parser
version0.1.5
created_at2025-11-08 21:02:00.686723+00
updated_at2025-11-11 19:15:07.763297+00
descriptionA parser and interpreter for MotoGarage DSL, a language for managing motorcycle collections.
homepage
repositoryhttps://github.com/YaroslavShakh/MotoGarage_Parser
max_upload_size
id1923270
size52,794
Ярослав Шах (YaroslavShakh)

documentation

README

motogarage_parser

Project Name: motogarage_parser Description: A parser and interpreter for MotoGarage DSL, a language for managing motorcycle collections. Crates.io link : https://crates.io/crates/MotoGarage_parser Docs link : https://docs.rs/MotoGarage_parser/0.1.5/motogarage_parser


Technical Description of the Parsing Process

This project implements a parser and interpreter for a custom Domain-Specific Language (DSL) designed for managing a "garage" of motorcycles.

The data processing pipeline is as follows:

1. Input Data

The program accepts a text file (usually with a .moto extension) that contains a set of commands. There are three types of commands:

  • DEFINE (for adding a motorcycle)
  • GET (for querying the collection)
  • COUNT (for counting items in the collection)

2. Parsing (Lexer + Parser)

  • We use the pest library with a PEG (Parsing Expression Grammars) approach.
  • The language grammar is defined in the src/grammar.pest file (see full grammar below). It describes the syntax: what "atoms" (numbers, strings, identifiers) exist and how they combine into "rules" (like DEFINE, GET, COUNT, WHERE clauses, etc.).
  • pest automatically generates a parser (MotogarageParser in src/lib.rs) that reads the input string and builds a "tree of pairs" (Pairs), which accurately reflects the code's syntax while ignoring whitespace and comments (//...).

3. AST (Abstract Syntax Tree) Generation

  • The "tree of pairs" from pest is generic. For practical use, we transform it into our own, strongly-typed Abstract Syntax Tree (AST).
  • Our AST structures (Command, Motorcycle, Query) are defined in src/lib.rs.
  • The parse_moto_file function and its helpers (parse_command, parse_definition, etc.) recursively traverse the pest tree and build a Vec<Command>.

4. Interpretation (How the results are used)

  • Once the input text is converted into an AST, we can "execute" it.
  • The Garage struct (src/lib.rs) acts as the interpreter. It maintains the state (a Vec<Motorcycle>).
  • The Garage::execute method iterates over the AST (Vec<Command>):
    • If the command is Command::Definition, it adds a new Motorcycle object to the internal list.
    • If the command is Command::Get, it calls the filtering logic (run_query_get), which checks the motorcycles against the query conditions and returns a list of names.
    • If the command is Command::Count, it calls the filtering logic (filter_bikes), counts the results, and returns a string with the total (e.g., "Bikes found: 2").

5. Output

  • The binary file (src/main.rs) uses clap to parse CLI arguments.
  • It reads the specified file, passes its content to motogarage_parser::parse_moto_file, receives the AST, passes the AST to Garage::execute, and prints the results returned by the interpreter (or any errors that occurred).

CLI Usage

To compile and run the project:

# Ensure the code is formatted
cargo fmt

# Check the code for lints and style
cargo clippy

# Build the binary (in release mode)
cargo build --release

# Use `cargo run --` to run during development

# 1. Run the parser on a file 'my_garage1.moto': 
# There are  'my_garage2.moto' and 'my_garage3.moto'with different results to show parser work

cargo run -- parse my_garage1.moto

# 2. Display help 
cargo run -- --help
cargo run -- parse --help

# 3. Display credits
cargo run -- credits


Grammar (src/grammar.pest)

Сomplete grammar used by the parser:

/// Ignores whitespace, tabs, and newlines
WHITESPACE = _{ " " | "\t" | "\n" | "\r" | line_comment }

/// Ignores single-line comments (from // to end of line)
line_comment = _{ "//" ~ (!"\n" ~ ANY)* }

// --- Top-Level Rules ---

/// A file is a Start of Input (SOI), followed by zero or more commands,
/// and ending with an End of Input (EOI).
file = { SOI ~ (command)* ~ EOI }

/// A command is either a definition (DEFINE), a GET query, or a COUNT query.
command = { definition | query_get | query_count }

// --- 1. Definition (DEFINE) Rules ---

/// Defines a new bike.
/// Example: DEFINE bike "Honda CBR" { year: 2021 }
definition = { "DEFINE" ~ "bike" ~ string_literal ~ "{" ~ properties ~ "}" }

/// A list of one or more properties, separated by commas.
properties = { property ~ ("," ~ property)* }

/// A single 'key: value' pair.
/// Example: year: 2021
property = { ident ~ ":" ~ value }

// --- 2. Query Rules ---

/// A 'GET BIKES' query, which can have an optional 'WHERE' clause.
query_get = { "GET" ~ "BIKES" ~ (where_clause)? }

/// A 'COUNT BIKES' query, which can have an optional 'WHERE' clause.
query_count = { "COUNT" ~ "BIKES" ~ (where_clause)? }

/// An optional 'WHERE' clause containing a single condition.
where_clause = { "WHERE" ~ condition }

/// A filter condition 'key <operator> value'.
/// Example: type = sport
condition = { ident ~ operator ~ value }

/// A comparison operator.
operator = { "=" | ">" | "<" }

// --- Basic Building Blocks (Atoms) ---

/// An identifier (a field name or type).
/// Starts with a letter or '_', followed by letters, numbers, or '_'.
ident = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }

/// A value, which can be a number with unit (cc), a plain number,
/// a quoted string, or an identifier (like 'sport').
value = { number_with_unit | number | string_literal | ident }

/// A string of text in double quotes.
/// Example: "Honda CBR600RR"
string_literal = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }

/// An integer.
/// Example: 2021
number = @{ ASCII_DIGIT+ }

/// A number with a 'cc' suffix (for engine displacement).
/// Example: 599cc
number_with_unit = @{ number ~ "cc" }

Makefile Documentation

Available Commands

Main Quality Check

This is the most important command to run before committing new code.

  • make check Runs the full quality-check suite. It executes fmt, clippy, and test all in one go.

Running the Program

  • make run ARGS="..." Runs the program in debug mode (for development).

    Important: You must provide the ARGS variable to pass arguments to the program.

    Examples:

    # Run the parser on a file
    make run ARGS="parse my_garage1.moto"
    
    # Show the credits
    make run ARGS="credits"
    

Building & Testing

  • make build Builds the final, optimized release version of the program. The executable will be located at target/release/moto. This is also the default command if you just run make.

  • make test Runs all unit and integration tests in the project.

Code Quality & Formatting

  • make fmt Formats all Rust code according to the project's style (using cargo fmt).

  • make clippy Runs the Clippy linter to check for common mistakes and un-idiomatic code. This command treats all warnings as errors (-D warnings), forcing you to write clean code.

Documentation & Cleanup

  • make doc Generates all project documentation from /// comments (including those in src/grammar.pest) and automatically opens the result in your web browser.

  • make clean Removes the target directory, deleting all build artifacts, cached dependencies, and compiled executables.

Commit count: 0

cargo fmt