IPFLang Formal Grammar

Extended Backus-Naur Form (EBNF). Every IPFLang program accepted by the reference parser conforms to the productions below — this is the authoritative syntax, not a simplification of it. For a guided walkthrough with worked examples, see the language reference.

Program Structure
<program>              ::= <comment>* <version>? <group>* <input_definition>*
                          <fee_computation>+ <verification>* <return>*
Comments
<comment>              ::= "#" {<any_char> - <newline>} <newline>
Version Declaration

Optional, at most one per script, and must come first if present.

<version>              ::= "VERSION" <string> "EFFECTIVE" <date>
                          ["DESCRIPTION" <string>] ["REFERENCE" <string>]
Group Definition

Assigns a display label and ordering weight to a set of inputs.

<group>                ::= "DEFINE" "GROUP" <identifier> "AS" <string>
                          "WITH" "WEIGHT" <number>
Input Definitions

Six input types: LIST, MULTILIST, NUMBER, BOOLEAN, DATE, AMOUNT.

<input_definition>     ::= "DEFINE" <input_type> <identifier> "AS" <string>
                          <type_specifics> ["DEFAULT" <default_value>]
                          ["GROUP" <identifier>] "ENDDEFINE"
<input_type>           ::= "LIST" | "MULTILIST" | "NUMBER" | "BOOLEAN" | "DATE" | "AMOUNT"
<type_specifics>       ::= <choices> | <numeric_constraint> | <date_constraint>
                          | <currency_spec> | ε
<choices>              ::= <choice>+
<choice>               ::= "CHOICE" <identifier> "AS" <string>
<numeric_constraint>   ::= "BETWEEN" <number> "AND" <number>
<date_constraint>      ::= "BETWEEN" <date> "AND" (<date> | "TODAY")
<currency_spec>        ::= "CURRENCY" <currency_code>
<default_value>        ::= <number> | <identifier> | <boolean_literal> | <date>
                          | <currency_literal> | <identifier_list>
<identifier_list>      ::= <identifier> ("," <identifier>)*
<boolean_literal>      ::= "TRUE" | "FALSE"
Fee Computation

<type_params> makes a fee polymorphic over a currency — see Polymorphic Fees in the reference.

<fee_computation>      ::= "COMPUTE" "FEE" <identifier> [<type_params>]
                          ["RETURN" <currency_code>] ["OPTIONAL"]
                          <let_statement>* <case_or_yield>*
                          "ENDCOMPUTE"
<type_params>          ::= "<" <type_variable> ">"
<type_variable>        ::= <upper_letter>
<let_statement>        ::= "LET" <identifier> "AS" <expression>
<case_or_yield>        ::= <case_block> | <yield_statement>
<case_block>           ::= "CASE" <condition> "AS" <yield_statement>+ "ENDCASE"
<yield_statement>      ::= "YIELD" <expression> ["IF" <condition>]
Verification Directives

Checked statically when the schedule is saved — see Verification Directives for what each one guarantees.

<verification>         ::= <verify_complete> | <verify_monotonic>
<verify_complete>      ::= "VERIFY" "COMPLETE" "FEE" <identifier>
<verify_monotonic>     ::= "VERIFY" "MONOTONIC" "FEE" <identifier>
                          "WITH" "RESPECT" "TO" <identifier>
                          ["DIRECTION" <direction>]
<direction>            ::= "NonDecreasing" | "NonIncreasing"
                          | "StrictlyIncreasing" | "StrictlyDecreasing"
Return Statement
<return>               ::= "RETURN" <identifier> "AS" <string>
Expressions
<expression>           ::= <term> (("+" | "-") <term>)*
<term>                 ::= <factor> (("*" | "/") <factor>)*
<factor>               ::= <number> | <currency_literal> | <identifier>
                          | <property_access> | <function_call> | "(" <expression> ")"
<property_access>      ::= <identifier> "!" <property_name>
<property_name>        ::= "COUNT" | "YEARSTONOW" | "MONTHSTONOW" | "DAYSTONOW"
                          | "MONTHSTONOW_FROMLASTDAY"
<function_call>        ::= ("ROUND" | "FLOOR" | "CEIL") "(" <expression> ")"
                          | "CONVERT" "(" <expression> "," <currency_code> "," <currency_code> ")"
Conditions

A bare <identifier> in a condition must reference a BOOLEAN input; it evaluates to that variable's value.

<condition>            ::= <or_condition>
<or_condition>         ::= <and_condition> ("OR" <and_condition>)*
<and_condition>        ::= <primary_condition> ("AND" <primary_condition>)*
<primary_condition>    ::= <expression> <comparison_op> <expression>
                          | <identifier> ("IN" | "NIN") <identifier>
                          | <identifier>
                          | "(" <condition> ")"
<comparison_op>        ::= "EQ" | "NEQ" | "GT" | "LT" | "GTE" | "LTE"
Lexical Elements
<identifier>           ::= <letter> (<letter> | <digit> | "_")*
<number>               ::= <digit>+ ("." <digit>+)?
<string>               ::= "'" {<any_char> - "'"} "'"
<currency_code>        ::= <upper_letter> <upper_letter> <upper_letter>
<date>                 ::= <day> "." <month> "." <year> | "TODAY"
<day>                  ::= <digit> <digit>?
<month>                ::= <digit> <digit>?
<year>                 ::= <digit> <digit> <digit> <digit>
<letter>               ::= "A" | ... | "Z" | "a" | ... | "z"
<upper_letter>         ::= "A" | ... | "Z"
<digit>                ::= "0" | ... | "9"

Currency literals (100<EUR>) and the CONVERT function are documented with worked examples in the reference, along with jurisdiction composition (inheritance between .ipf files), which operates above this grammar rather than inside it. The engine's source lives in the IPFLang repository.