syn-grammar

Crates.iosyn-grammar
lib.rssyn-grammar
version0.1.0
created_at2026-01-25 21:46:07.715407+00
updated_at2026-01-25 21:46:07.715407+00
descriptionA parser generator for Rust that compiles EBNF-like grammars into syn::parse::Parse implementations.
homepage
repositoryhttps://github.com/keywan-ghadami/syn-grammar
max_upload_size
id2069661
size23,658
Keywan Ghadami (keywan-ghadami)

documentation

README

syn-grammar

Crates.io Documentation License

syn-grammar is a parser generator for Rust that allows you to define EBNF-like grammars directly inside your code (or in external files) and compiles them into syn parsers.

It is designed to make writing procedural macros and Domain Specific Languages (DSLs) in Rust significantly easier by handling the parsing boilerplate for you.

Features

  • EBNF Syntax: Define rules using sequences, alternatives (|), optionals (?), repetitions (*, +), and groups (...).
  • Type-Safe Actions: Attach Rust code blocks (-> { ... }) to rules to transform parsed tokens into your own AST or syn structures.
  • Syn Integration: Built-in support for parsing Rust identifiers (ident), integers (int_lit), and strings (string_lit).
  • Left Recursion: Automatically handles direct left recursion (e.g., expr = expr "+" term), making expression parsing intuitive.
  • Backtracking: Supports speculative parsing for ambiguous grammars.
  • External Files: Keep your Rust code clean by moving grammars to .g files with include_grammar!.

Installation

Add this to your Cargo.toml:

[dependencies]
syn-grammar = "0.1"
syn = { version = "2.0", features = ["full", "extra-traits"] }
quote = "1.0"
proc-macro2 = "1.0"

Quick Start

Inline Grammar

You can define a grammar directly inside a macro:

use syn_grammar::grammar;
use syn::parse::Parser; // Required for .parse_str()

grammar! {
    grammar Calc {
        // The return type of the rule is defined after `->`
        rule expression -> i32 =
            l:expression "+" r:term -> { l + r }
          | t:term                  -> { t }

        rule term -> i32 =
            f:factor "*" t:term -> { f * t }
          | f:factor            -> { f }

        rule factor -> i32 =
            i:int_lit      -> { i }
          | paren(e:expression) -> { e }
    }
}

fn main() {
    // The macro generates a module `Calc` with functions `parse_<rule_name>`
    let result = Calc::parse_expression.parse_str("1 + 2 * 3");
    assert_eq!(result.unwrap(), 7);
}

External Grammar File

src/grammar.g

grammar MyGrammar {
    rule main -> String = "hello" "world" -> { "Success".to_string() }
}

src/lib.rs

use syn_grammar::include_grammar;

include_grammar!("grammar.g");

fn test() {
    let res = MyGrammar::parse_main.parse_str("hello world");
    assert!(res.is_ok());
}

Syntax Reference

Syntax Description Example
"lit" Literal match "fn"
ident Rust Identifier my_var
int_lit Integer Literal 42
string_lit String Literal "hello"
name:rule Rule call with binding e:expr
( A B ) Grouping ("a" "b")
A | B Alternatives "true" | "false"
A? Optional ","?
A* Zero or more item*
A+ One or more digit+
paren(A) Parentheses (...) paren(expr)
bracketed[A] Brackets [...] bracketed[expr]
braced{A} Braces {...} braced{expr}
A => B Cut Operator (Commit) "let" => "mut"

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Commit count: 146

cargo fmt