readcon-core 0.14.7

An oxidized single and multiple CON file reader and writer with FFI bindings for ergonomic C/C++ usage.
Documentation
// CON / convel surface syntax (PEG / Pest).
//
// Aligns with docs/orgmode/spec.org (CON v2–v3). This grammar defines the
// *syntactic* shape of one or more concatenated frames. Semantic rules
// (atom counts matching header N_i, Component index order, JSON metadata
// keys, fixed-mask values, validate=true geometry) are enforced by the
// reference implementation in src/parser.rs — not by this PEG alone.
//
// Hot-path I/O does not depend on Pest; this file is the formal grammar
// artifact shipped with the repo, validated in tests via the optional
// `grammar` feature / dev-dependencies.
//
// Section row arities (spec §sections):
//   vector: velocities, forces, magmoms  →  v0 v1 v2 fixed [atom_id]
//   scalar: energies, charges, spins     →  value fixed [atom_id]

/// One or more frames, no inter-frame marker (frames are concatenated).
file = { SOI ~ frame+ ~ EOI }

/// Header + per-type coordinate blocks + optional section blocks.
frame = { header ~ type_block+ ~ section* }

/// Nine-line header (lines 1–9 of the specification).
header = {
    free_line ~ NL
    ~ free_line ~ NL
    ~ float3_line
    ~ float3_line
    ~ free_line ~ NL
    ~ free_line ~ NL
    ~ integer_line
    ~ integer_list_line
    ~ float_list_line
}

/// Free-form line (generator comment, JSON metadata, or reserved text).
/// Does not consume the trailing newline.
free_line = { (!NL ~ ANY)* }

float3_line = { ws* ~ float ~ ws+ ~ float ~ ws+ ~ float ~ ws* ~ NL }

integer_line = { ws* ~ integer ~ ws* ~ NL }

integer_list_line = { ws* ~ integer ~ (ws+ ~ integer)* ~ ws* ~ NL }

float_list_line = { ws* ~ float ~ (ws+ ~ float)* ~ ws* ~ NL }

/// One chemical-type block: symbol, "Coordinates of Component k", atom lines.
type_block = {
    symbol_line ~ NL
    ~ coord_label ~ NL
    ~ vector_atom_line ~ (NL ~ vector_atom_line)*
    ~ NL?
}

symbol_line = { ws* ~ symbol ~ ws* }

/// Element / type symbol (letters, optional trailing digits/underscore).
symbol = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }

/// Wire labels may use legacy "component" casing (e.g. eOn sulfolene fixtures).
/// Pest ^string is case-insensitive ASCII.
coord_label = {
    ws* ~ ^"Coordinates of Component" ~ ws+ ~ integer ~ ws*
}

/// Coordinate / 3-vector section row: v0 v1 v2 fixed_mask [atom_id]
vector_atom_line = {
    ws* ~ float ~ ws+ ~ float ~ ws+ ~ float
    ~ ws+ ~ integer
    ~ (ws+ ~ integer)?
    ~ ws*
}

/// Scalar section row: value fixed_mask [atom_id]
scalar_atom_line = {
    ws* ~ float
    ~ ws+ ~ integer
    ~ (ws+ ~ integer)?
    ~ ws*
}

/// Backward-compatible alias used by tests and older notes.
atom_line = { vector_atom_line }

/// Optional section: blank separator + homogeneous type blocks (vector or scalar).
/// After a type_block's trailing newline, one more newline is the blank line
/// required by the specification before a section.
section = {
    ws* ~ NL
    ~ (section_type_block_vector+ | section_type_block_scalar+)
}

section_type_block_vector = {
    symbol_line ~ NL
    ~ section_label_vector ~ NL
    ~ vector_atom_line ~ (NL ~ vector_atom_line)*
    ~ NL?
}

section_type_block_scalar = {
    symbol_line ~ NL
    ~ section_label_scalar ~ NL
    ~ scalar_atom_line ~ (NL ~ scalar_atom_line)*
    ~ NL?
}

section_kind_vector = { ^"Velocities" | ^"Forces" | ^"Magmoms" }
section_kind_scalar = { ^"Energies" | ^"Charges" | ^"Spins" }

/// Union of all section kinds (docs / tests).
section_kind = { section_kind_vector | section_kind_scalar }

section_label_vector = {
    ws* ~ section_kind_vector ~ ws+ ~ ^"of Component" ~ ws+ ~ integer ~ ws*
}

section_label_scalar = {
    ws* ~ section_kind_scalar ~ ws+ ~ ^"of Component" ~ ws+ ~ integer ~ ws*
}

/// ASCII decimal float (optional sign, fraction, scientific exponent).
float = @{
    "-"?
    ~ ASCII_DIGIT+
    ~ ("." ~ ASCII_DIGIT*)?
    ~ (("e" | "E") ~ ("+" | "-")? ~ ASCII_DIGIT+)?
}

integer = @{ "-"? ~ ASCII_DIGIT+ }

ws = _{ " " | "\t" }
NL = _{ "\r\n" | "\n" }