// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.

message = _{ (text | percent | placeholder)* ~ EOI }

// # Format specification:
//
// The placeholders in the format string are marked by % and are followed by one or more of these elements, in this order:
//
// - An optional number followed by a $ sign that selects which argument index to use for the value. If not specified,
//   arguments will be placed in the same order as the placeholders in the input string.
arg_indexed = { !"0" ~ number ~ "$" }
// TODO: the named argument can be a complex expression
arg_named = { "(" ~ ident ~ ")" }

// - An optional + sign that forces to precede the result with a plus or minus sign on numeric values.
//   By default, only the - sign is used on negative numbers.
plus_sign = { "+" }

// - An optional padding specifier that says what character to use for padding (if specified).
//   Possible values are 0 or any other character preceded by a ' (single quote). The default is to pad with spaces.
padding_specifier = { "0" | "'" ~ ANY }

// - An optional - sign, that causes sprintf to left-align the result of this placeholder.
//   The default is to right-align the result.
left_align = { "-" }

// - An optional number, that says how many characters the result should have.
//   If the value to be returned is shorter than this number, the result will be padded.
//   When used with the j (JSON) type specifier, the padding length specifies the tab size used for indentation.
width = { number }

// - An optional precision modifier, consisting of a . (dot) followed by a number, that says how many digits should be
//  displayed for floating point numbers. When used with the g type specifier, it specifies the number of significant
//  digits. When used on a string, it causes the result to be truncated.
precision = { "." ~ number }

// - A type specifier that can be any of:
//   - % — yields a literal % character -- This is handled separately
//   - b — yields an integer as a binary number
//   - c — yields an integer as the character with that ASCII value
//   - d or i — yields an integer as a signed decimal number
//   - e — yields a float using scientific notation
//   - u — yields an integer as an unsigned decimal number
//   - f — yields a float as is; see notes on precision above
//   - g — yields a float as is; see notes on precision above
//   - o — yields an integer as an octal number
//   - s — yields a string as is
//   - t — yields true or false
//   - T — yields the type of the argument
//   - v — yields the primitive value of the specified argument
//   - x — yields an integer as a hexadecimal number (lower-case)
//   - X — yields an integer as a hexadecimal number (upper-case)
//   - j — yields a JavaScript object or array as a JSON encoded string
type_specifier = { "b" | "c" | "d" | "i" | "e" | "u" | "f" | "g" | "o" | "s" | "t" | "T" | "v" | "x" | "X" | "j" }

percent = @{ start ~ start }
placeholder = {
    start ~
    (arg_indexed | arg_named)? ~
    plus_sign? ~
    padding_specifier? ~
    left_align? ~
    width? ~
    precision? ~
    type_specifier
}
text = @{ (!start ~ ANY)+ }

start = _{ "%" }
number = @{ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
ident = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
