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.