rantlr-core 0.1.0

Rantlr core: .gr lexer, parser, linter, and in-process runtime
Documentation
//! Grammar AST shapes produced by the parser and consumed by linter / codegen.

use serde::Serialize;

use crate::span::Span;
use crate::token::BuiltinType;

/// Top-level compiled grammar unit.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Grammar {
    pub name: String,
    #[serde(skip)]
    pub name_span: Span,
    pub items: Vec<GrammarItem>,
    #[serde(skip)]
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum GrammarItem {
    Token(TokenDecl),
    Rule(RuleDecl),
    Example(ExampleDecl),
    Import(ImportDecl),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct TokenDecl {
    pub name: String,
    #[serde(skip)]
    pub name_span: Span,
    pub body: TokenBody,
    pub skip: bool,
    #[serde(skip)]
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
pub enum TokenBody {
    Builtin(BuiltinType),
    /// Explicit literal pattern, e.g. `token Plus = "+";`
    Literal(String),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RuleDecl {
    pub name: String,
    #[serde(skip)]
    pub name_span: Span,
    pub body: Expr,
    #[serde(skip)]
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Expr {
    /// Alternatives separated by `|`.
    Alt { alts: Vec<Expr> },
    /// Juxtaposed sequence.
    Seq { items: Vec<Expr> },
    /// `match a | b | c` — first-class choice of match arms.
    Match { arms: Vec<Expr> },
    /// `repeat { ... }` or `repeat(0..) { ... }`
    Repeat {
        min: Option<u64>,
        max: Option<u64>,
        body: Box<Expr>,
    },
    /// `optional { ... }`
    Optional { body: Box<Expr> },
    /// Reference to a rule or token name.
    Ref {
        name: String,
        #[serde(skip)]
        span: Span,
    },
    /// String literal (auto-inferred token).
    Literal {
        value: String,
        #[serde(skip)]
        span: Span,
    },
    Group { body: Box<Expr> },
}

impl Expr {
    pub fn seq(mut items: Vec<Expr>) -> Self {
        match items.len() {
            0 => Self::Seq { items },
            1 => items.remove(0),
            _ => Self::Seq { items },
        }
    }

    pub fn alt(mut alts: Vec<Expr>) -> Self {
        match alts.len() {
            0 => Self::Alt { alts },
            1 => alts.remove(0),
            _ => Self::Alt { alts },
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ExampleDecl {
    pub label: Option<String>,
    pub input: String,
    pub expect: Option<String>,
    #[serde(skip)]
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ImportDecl {
    pub path: String,
    #[serde(skip)]
    pub span: Span,
}