openinfer-dsl 0.1.3

Rust-embedded DSL for defining OpenInfer graphs with explicit control flow and memory semantics.
Documentation
use syn::parse::{ParseStream, Result};
use syn::Token;

use crate::kw;
use crate::types::InitValue;

mod init;
mod pattern;
mod ref_attr;

pub struct ParsedAttrs {
    pub init: Option<InitValue>,
    pub ref_name: Option<syn::LitStr>,
    pub pattern: Option<syn::LitStr>,
    pub table: bool,
    pub auto_dim: Vec<syn::Ident>,
    pub fixed: Vec<(syn::Ident, syn::LitInt)>,
}

pub fn parse_attrs(input: ParseStream) -> Result<ParsedAttrs> {
    let mut init = None;
    let mut ref_name = None;
    let mut pattern = None;
    let mut table = false;
    let mut auto_dim = Vec::new();
    let mut fixed = Vec::new();
    while input.peek(Token![@]) {
        input.parse::<Token![@]>()?;
        if input.peek(kw::init) {
            if init.is_some() {
                return Err(input.error("duplicate @init attribute"));
            }
            input.parse::<kw::init>()?;
            init = Some(init::parse_init_value(input)?);
        } else if input.peek(Token![ref]) {
            if ref_name.is_some() {
                return Err(input.error("duplicate @ref attribute"));
            }
            input.parse::<Token![ref]>()?;
            ref_name = Some(ref_attr::parse_ref_name(input)?);
        } else if input.peek(kw::pattern) {
            if pattern.is_some() {
                return Err(input.error("duplicate @pattern attribute"));
            }
            input.parse::<kw::pattern>()?;
            pattern = Some(pattern::parse_pattern(input)?);
        } else if input.peek(kw::table) {
            if table {
                return Err(input.error("duplicate @table attribute"));
            }
            input.parse::<kw::table>()?;
            table = true;
        } else if input.peek(kw::auto_dim) {
            if !auto_dim.is_empty() {
                return Err(input.error("duplicate @auto_dim attribute"));
            }
            input.parse::<kw::auto_dim>()?;
            let content;
            syn::parenthesized!(content in input);
            while !content.is_empty() {
                auto_dim.push(content.parse()?);
                if content.peek(Token![,]) {
                    content.parse::<Token![,]>()?;
                }
            }
            if auto_dim.is_empty() {
                return Err(input.error("@auto_dim requires at least one index"));
            }
        } else if input.peek(kw::fixed) {
            if !fixed.is_empty() {
                return Err(input.error("duplicate @fixed attribute"));
            }
            input.parse::<kw::fixed>()?;
            let content;
            syn::parenthesized!(content in input);
            while !content.is_empty() {
                let ident: syn::Ident = content.parse()?;
                content.parse::<Token![=]>()?;
                let value: syn::LitInt = content.parse()?;
                fixed.push((ident, value));
                if content.peek(Token![,]) {
                    content.parse::<Token![,]>()?;
                }
            }
            if fixed.is_empty() {
                return Err(input.error("@fixed requires at least one entry"));
            }
        } else {
            return Err(input.error("unsupported attribute"));
        }
    }
    Ok(ParsedAttrs {
        init,
        ref_name,
        pattern,
        table,
        auto_dim,
        fixed,
    })
}