gnu-units 0.2.1

Safe Rust bindings for the GNU units conversion
Documentation
// PEG grammar for GNU units expressions (pest syntax).
// Operator precedence (lowest → highest): additive, product, power, unary, atom.

// Silent whitespace — consumed between any tokens
WHITESPACE = _{ " " | "\t" | "\n" | "\r" }

hex_number     = @{ ^"0x" ~ ASCII_HEX_DIGIT+ }
decimal_number = @{
    (ASCII_DIGIT* ~ "." ~ ASCII_DIGIT+ ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?
      | ASCII_DIGIT+ ~ "."? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?)
}
number = { hex_number | decimal_number }

// Unit/function names: start with letter|_|%|$|&|non-ASCII;
// continue with alphanumeric|_|'|$|&|non-ASCII.
name = @{
    (ASCII_ALPHA | "_" | "%" | "$" | "&" | (!ASCII ~ ANY))
    ~ (ASCII_ALPHANUMERIC | "_" | "'" | "$" | "&" | (!ASCII ~ ANY))*
}

// `per` keyword — matches only as a complete word, not as a prefix of a longer name.
per = @{ "per" ~ !(ASCII_ALPHANUMERIC | "_" | "'" | "$" | "&") }

minus  = { "-" }
add_op = { "+-" | "+" | "-" }
mul_op = { "*" | "/" | "|" | per }
pow_op = { "**" | "^" }

expr        = { additive }
additive    = { product ~ (add_op ~ product)* }
// juxt_factor guards against treating `+`/`-` as implicit multiplication.
product     = { power ~ ((mul_op ~ power) | juxt_factor)* }
juxt_factor = { !add_op ~ power }
// Exponent is a full unary expression; use parentheses for complex exponents, e.g. `m^(2/3)`.
power       = { unary ~ (pow_op ~ unary)? }
// `per` prefix inverts its operand, replicating GNU units' `per` keyword semantics.
unary       = { (minus | per) ~ unary | atom }
atom        = { number | tilde_call | func_call | name | group }
tilde_call  = { "~" ~ name ~ "(" ~ expr ~ ")" }
arg_list    = { expr ~ ("," ~ expr)* }
func_call   = { name ~ "(" ~ arg_list? ~ ")" }
group       = { "(" ~ expr ~ ")" }

input = { SOI ~ expr ~ EOI }