rslc compiler.
E-ENUM-NARROW
A narrowed enum removes members of its parent. A narrowed enum is a parent enum minus some members:MetroTerritory = Territory - { rural }. The check asks every removed name to be a member of the parent.
A removed name that the parent lacks is usually a misspelling or a member someone renamed. The check catches it before the narrowed enum accepts a value you meant to exclude.
Example
MetroTerritory removes coastal, and Territory has only urban, suburban and rural.
- The narrowing subtracts a whole type,
Territory - Coastal, andCoastalis an undeclared name. Declare that enum, or list the members in braces.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-ENUM-BACKING
A CSV-backed enum needs its member file.Territory = Enum declares an enum whose members live in a CSV file. The compiler reads enums/Territory.csv beside the rater: a folder named enums and a file named after the type.
The file has an ident column for member names. A value column holds the text a request sends, and label holds the words a person reads. The check asks for at least one member, each with its own name and its own value.
A long member list, such as territories or occupations, stays in data this way. The check confirms that the list arrived whole before any input or table uses it.
Example
The rater declares Territory = Enum, and the folder beside it holds only rater.rsl.
enums/Territory.csv beside it, with one row per member. For a short list, another fix is to write the members inline: Territory = { urban, suburban, rural }.
enums/Territory.csv
- The file has a header row and zero member rows, or lacks both the
identandvaluecolumns. Add the columns and a row for each member. - Two rows share one
ident. Give each member its own name. - Two rows share one
value. Give each member its own value, so a request names exactly one member.
Complete files
Complete files
bad/rater.rsl
good/enums/Territory.csv
good/rater.rsl
E-RANGE
A range states two bounds in increasing order. A range states the values that an input, a calculation or a type accepts:where self in [0, 100000000]. The check asks for the lower bound first and the upper bound second. An underscore, _, marks an open side.
A square bracket includes its endpoint and a round bracket excludes it. An input accepts the values on its precision grid, so an excluded endpoint closes to the nearest grid value inside the range. At precision: 0.01, (0, 1] accepts 0.01 to 1.
Each range then has one reading, and the input contract states it. The check catches a range that accepts zero values before a request meets it.
Example
The range of payroll puts the upper bound first.
- A round bracket sits beside
_, as in(_, 5]. Write the open side with a square bracket:[_, 5]. - The range excludes its only value, as in
(5, 5]. Write[5, 5]or widen the range. - A date range uses a round bracket. Dates are whole days, so write the inclusive days:
[2026-01-02, 2026-12-31]. - A date range leaves both sides open. Give it at least one date.
- An input that lacks a precision excludes an endpoint. Declare a precision, or write the smallest accepted value as an inclusive bound.
- An excluded endpoint lies off the input’s grid, as in
(0.5, 100]atprecision: int. Write the endpoint on the grid. - The grid has zero values inside the range, as in
(0, 1)atprecision: int. Widen the range or refine the precision. - An
api_defaultlies outside the input’s range, so a request that omits the input fails. Write a default inside the range. - A calculation tests
x in [_, _]. Give the interval at least one finite bound. - A value states two ranges joined by
and. State one range; this cause also getsE-REFINEMENT.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-DEFAULT
An omitted input needs an explicit value. An input is a fact the request supplies. The check stopsdefault: in an input’s type, where a value would appear silently when a request omits the input.
Declare an input that a request may omit as Option(T). The calculation then handles both cases with match: Some(c) for a supplied value and None for a missing one.
The value used for a missing answer becomes a visible line of the calculation. A reviewer reads it beside the rest of the arithmetic.
Example
schedule_credit declares default: 1 in its type.
Option(Num) and name the credit that each case gives. Another fix keeps the input a plain number and adds [api_default: "1"] before where; the rating API then fills in an omitted input.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-INPUT-GENERATE
The rules admit at least one request. The ranges and rules of a rater state which requests it accepts. The check asks that at least one request passes all of them. A rater that refuses every request usually has a mistyped bound, or two rules that contradict each other. The message names the record, the input and the rules that leave it empty. The compiler proves this from each input’s own range and rules. It also tries every value of an input with a few thousand values against the record rules that read only that input. For rules that combine several inputs,rslc inputs sample rater.rsl searches for an accepted request.
Example
employees accepts 1 to 50, and the rule on Policy asks for at least 100.
- One input states two rules that share zero values, such as
where self in [0, 100] where self in {200, 300}. Change one of them. - A record rule reads zero inputs and is false, such as
where 1 = 2. Delete the rule or correct it. - A list requires at least one element, and the element’s record accepts zero values. Correct the element’s rules.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-ENUM-NUMERIC
An ordered scale of numbers is a number. An enum is a set of names, and only a number has order and arithmetic. The check finds an enum whose members are all numerals, such as"1", "2" and "3", and whose use depends on their order.
Two uses show that order. A calculation sorts a number into the members with comparisons such as < 3 and < 10. Or an if chain groups neighbouring members, such as tiers 1 and 2 together.
In both, the real key is a number and its bands. Declare the bands as a type, such as IntBands(0, 3, 10..), and key the table on the number. The compiler then checks that the bands cover the whole range of the input. An enum whose numeral members are only compared for equality passes the check.
Example
tier sorts years_in_business into the numeral members of Tier, and the table is keyed on Tier.
YearsInBusiness = IntBands(0, 3, 10..), key the table on that type, and look up the years directly. Each CSV row starts at its band’s lower bound.
- An input has the numeral enum as its type, and an
ifchain groups neighbouring members into bands. Declare the input as a number with a range, and key a table on a band type.
Complete files
Complete files
bad/rater.rsl
bad/tables/TierFactor.csv
good/rater.rsl
good/tables/TierFactor.csv
E-CODE-PATTERN
A code value matches its declared pattern. ACode type holds identifiers such as class codes and ZIP codes. Its pattern states the exact shape of a valid code: Code(pattern: "[0-9]{4}") accepts four digits.
The check asks every code written in the rater to match the whole pattern. It also asks the pattern itself to be one the compiler reads.
The same pattern refuses a request with a malformed code before rating starts. A literal that fails the pattern would compare equal to zero accepted codes, so its branch would be dead. The check finds that typing slip in the rater.
Example
ClassCode accepts four digits, and the comparison writes the five-digit code "70010".
"[0-9]{5}".
- The type is
Code()and lacks a pattern. Add one:Code(pattern: "[0-9]{4}"). - The pattern is a number or a name. Write it as a string in double quotes.
- The pattern accepts
_or.., which table cells reserve for wildcards and open bands. Tighten the pattern to exclude them. - The pattern has a syntax error, such as an unclosed
[, or uses.for any character. List the allowed characters in a class such as[0-9A-Z].
E-TABLE-COL, and that message names E-CODE-PATTERN in its text.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-INPUT-DOMAIN
A number input states its precision and range. A number input declares two things. Its precision is the grid of values a request may send:precision: int for whole numbers, or a decimal step such as precision: 0.01. Its range is the lowest and highest value the filing accepts: where self in [0, 100000000].
The check asks every number input for both. With them, the rating API refuses a payroll of -5 or 12.345 before any arithmetic runs. The compiler also uses the range to prove that each lookup finds a row and each division has a nonzero divisor.
Example
payroll declares a precision and omits the range.
_ for a side that the filing leaves open, as in [0, _].
- The input states a range and omits the precision, as in
payroll: Num where self in [0, 100000000]. Add{precision: int}or a decimal step. - The input declares
precision: factor, which belongs to calculated values. Give the input a decimal step such asprecision: 0.01. - The input declares
precision: exactand omits the range. Add the range; reserveexactfor a value whose scale the filing leaves unstated.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-REFINEMENT
A rule takes the form its position accepts. Awhere rule follows an input, a list, a record or a calculation, and each position accepts its own forms. The check finds a rule in a form that its position leaves out.
A rule on an input, a list or a record is a test: the rating API refuses a request that fails it. A range on a calculation is a claim, and the compiler proves it from the input ranges. A comparison or a condition over inputs is a test, so it belongs on an input or a record.
Example
The calculation
premium states its lower bound as the comparison self >= 0.
- A calculation’s rule tests an input, such as
where self.payroll >= 1000. Move the rule after the record. - A calculation’s type is an alias whose rule has a code or a condition. Use a type whose only rule is a range.
- A list length has fractional bounds or puts the larger bound first, as in
len(self) in [10, 1]. Write whole numbers in increasing order. uniquehas another shape thanunique(self as x, x.key). Bind the element withas, and name one required input of the element.uniquesits in a record rule. Write it after the list input:locations: List(Location) where unique(self as l, l.address).- A list rule follows an
Option(List(…))input. Declare a plainList(…), where an empty list stands for a missing one. - A rule follows an input that holds one child record. Write the rule after that record’s type.
- One input states two ranges or two sets. Merge them into one.
- An input carries
[child], which RSL 0.4.1 retired. Declarename: Locationfor one record orname: Option(Location)for an optional one.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-ANNOTATION
A refusal code follows its rule, in capitals. A rule that refuses a request names the refusal after its condition:[code: "…", message: "…", cite: source:region]. The rating API returns the code and the message to the caller. The citation points to the filed rule.
The check asks for three things. The annotation follows a where rule on an input, a list or a record. It holds only code, message and cite. The code starts with a capital letter and continues with capitals, digits, _ or -.
Other systems match on the code, so one fixed spelling keeps it stable. Annotations that describe the declaration itself, such as label, sit before where.
Example
The code payroll-range is in lower case.
- The message is an empty string. Write the sentence the caller reads.
codeormessagehas a number as its value, or stands alone as[code]. Give it a string in double quotes.- Another annotation follows the rule, such as
[label: "…"]or[required]. Move it to the declaration, beforewhere. codeandmessagesit on the input, beforewhere. Move them after the rule they name.- An input carries
codeandmessageand lacks a rule for them to name. Add awhererule or remove them. - A calculation or a constant carries
codeormessage. Remove them; a calculation’s range is a claim the compiler proves, so it refuses zero requests.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-REFINE-CANONICAL
A bound on an input is a range. A rule on an input that compares it with one number is a bound. The check asks for every bound in one spelling, the range:self in [0, _] in place of self >= 0.
With one spelling, the compiler reads the range of every input directly. It uses that range in the input contract, in its proofs about lookups and division, and when it generates sample requests. A reader of the rater finds each input’s limits in the same place and form.
A square bracket includes the endpoint, a round bracket excludes it, and _ leaves a side open. The message prints the range to write.
The check covers the rules written on one input. A rule written after a record, such as where self.payroll >= 1000, keeps its comparison.
Example
payroll states its lower bound as the comparison self >= 0.
self in [0, 100000000].
- A date input compares
selfwith one date. Write the range of dates:self in [2026-01-01, _]. - A list bounds its length with a comparison, such as
len(self) <= 5. Writelen(self) in [_, 5]. - An input fixes one value with
(self = 5). Write the set,self in {5}, in a secondwhereafter the range.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-REFINE-UPWARD
A rule reads its own record and below. Awhere rule decides whether the rating API accepts a request. The rule is part of the input contract, so it reads inputs: the value it follows and the records beneath that value.
The check finds a rule that reads further: a parent record, a neighbouring input, a calculation or a rating outcome. In the example, a rule after Location reads Policy, which sits above it.
Write a rule that relates a child to its parent on the parent, over its children. The parent reaches both sides: its own inputs through self, and each child through all.
Example
The rule after Location compares a location’s payroll with Policy.payroll.
Policy and state it over the locations with all.
- A record rule reads a calculation over the record’s own inputs, such as
self.payroll_thousands >= 1. Write the formula in the rule:self.payroll / 1000 >= 1. - A record rule reads a rating outcome, such as
self.premium >= 100. State the rule over the inputs that decide it. - A rule on an input or a list reads a calculation, such as
all(self as l, l.half <= 100). State the rule over inputs:all(self as l, l.payroll / 2 <= 100).
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-DERIVABLE-INPUT
Calculate a value that other inputs fix. An input is a fact that only the caller knows. The check finds an input that a rule forces to equal a formula of other inputs, so the caller has exactly one acceptable answer. Asking for such a value adds a question and a way for the request to disagree with itself. Calculate the value in the rater. The request gets shorter, and the total always matches its parts. A rule that sets two independent facts equal passes the check. An example is a filing that requires two coverage limits to match, where each limit also feeds the rating. Example The rule afterPolicy forces payroll to equal the sum of the other two inputs.
payroll as a calculation in a step.
- An input appears only in a rule that refuses a mismatch, such as
where not (self.declared_payroll != self.clerical_payroll + self.field_payroll). Delete the input and the rule, and read the formula where the value is needed. - A presence flag guards that comparison, as in
where not (self.has_declared_payroll and …). Delete the flag as well.
} where self.clerical_payroll = self.field_payroll.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl