rslc compiler.
E-UNRESOLVED
Every name matches a declaration. A rater reads each value by name: an input, a calculation, a constant, a table, a type, an enum member or a function. The check asks every name to match a declaration the compiler has read. A misspelt name stops the check at its line and column. The rater then reads only values that exist, and a typing slip surfaces before any request is rated. The same code reports a file the compiler fails to read as a whole, such as a syntax error or a missing import. Example The premium readsrate_per_100, and the constant is named rate_per_1000.
- A misspelt input or calculation after
self.getsunknown attribute 'payrol' on current entity. - A misspelt function gets a message that names it and lists the functions RSL has.
- A misspelt type gets
unknown type 'Mony'. - A lookup of a table that
Tableslacks getsunknown table 'BaseRate'. - A misspelt enum or member gets
unknown enum 'Teir'orunknown member 'prefered' in enum 'Tier'. - A
Refto a name thatRecordslacks gets`Ref(Driver)` names no record type. - An outcome that names a missing calculation gets
rating outcome `premium` references unknown attribute `Policy.premum`. - A syntax error gets
parse error in …: expected value, with the place where reading stopped. - An
importof a missing file getscannot resolve import "other.rsl", with the folders searched. - A name declared twice in one section gets
duplicate Constants declaration 'rate_per_1000'.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-DATE
A date literal names a real calendar day. A date in a rater is writtenYYYY-MM-DD. The check asks each date literal in a calculation to name a day the calendar has.
A date such as 2026-06-31 reads like a date and names a day that June lacks. The check reports it where it is written, so a comparison or a date difference always works from a real day.
Example
The rate changes on 2026-06-31, and June has 30 days.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-PATTERN
A match arm uses a pattern RSL knows. Amatch chooses a result by the value of its subject. Each arm starts with a pattern, and the pattern follows the subject’s type.
An enum subject takes @Enum.member arms. A Bool takes true and false, an Option takes None and Some(x), and a union takes one arm per record type. _ matches every remaining value of an enum, Bool or Option.
The check asks each arm to use one of these patterns, of the kind its subject takes. A pattern of another kind would match zero requests, so the compiler reports it instead of skipping the arm.
Example
The first arm names the enum member as the string "standard".
@Tier.standard.
NoneorSome(x)on a subject of another type gets`None` and `Some(…)` match an `Option`, with the subject’s type.- A capitalised arm such as
Standard(s)on an enum getsarm pattern `Standard` matches a union input. _or@Enum.memberon a union getsa union is matched arm by arm, by record type.- A union arm that names another record type gets
`Unrted` is not an arm of this union, with the union’s arms.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-TYPE
Each value has the type its place expects. Every value in a rater has a type: a number, aBool, a Date, an enum member, a record or a list. Each place that takes a value states the type it takes, such as the condition of an if or a key of a lookup.
The check asks the two to agree. A number where a Bool belongs, or an enum member in arithmetic, is a mistake in the formula. The compiler reports it before any request is rated.
RSL converts between types only where you write the conversion. A condition is a comparison such as self.payroll > 0, and an enum becomes a factor through a match or a table.
Example
The condition of the if is the number self.payroll, and an if takes a Bool.
- A calculation whose value differs from its declared type gets
attribute `premium` expects Premium, got Bool. - An enum member in arithmetic gets
arithmetic operand expects Num. - A comparison of an enum with a number gets
this comparison compares enum … with Num. - An ordering such as
>on an enum getsonly numbers and dates are ordered. - A lookup with more or fewer keys than the table has dimensions gets
pass one key per dim, in order. - A lookup key of another type than its dimension gets
lookup argument 1 of … takes a number. - A read through a list, such as
self.items.charge, getsis a list of instances; bind the list in a fold such assum. - A read through a field that holds a plain value, such as
self.payroll.amount, getsis not an entity navigation field. - A
[key]after a value that is a single number getsattribute `item_charge` is not indexed. Noneas the value of a calculation of a plain type gets`None` requires expected type Option(T).- An enum that lists one member twice gets
declares member … more than once.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-DATA-AS-CODE
A long list of values needs a file. A rater’s source holds rating logic, and its data lives in CSV files beside it. The check asks a list of more than 256 values to move out of the source and into a file. The limit applies to three forms: the members of an enum written inTypes, the values of a membership test such as self in {…}, and the arms of a match.
A list this long is data the filing publishes as a table or an appendix. In a CSV file it loads fast, a reviewer compares it with the filing row by row, and the rater stays short enough to read.
Example
The input class_code lists 257 eligible class codes inside the rater.
values: Csv and list the values in enums/ClassCode.csv, under a value column. The file takes the name of the type.
enums/ClassCode.csv
- An enum with more than 256 members gets
inline enum `Territory` has 257 members. DeclareTerritory = Enumand list the members inenums/Territory.csv, or type an identifier such as a ZIP code asCode(pattern: "…"). - A
matchwith more than 256 arms getsmatch has 257 literal value arms. Move the rows to a table and read the result withlookup!.
Complete files
Complete files
bad/rater.rsl
good/enums/ClassCode.csv
good/rater.rsl
E-SELECT-TIE
A selection states how a tie breaks.argmax, argmin, topn, find and rank pick records from a list. Two records can tie: two locations with the same payroll both have the greatest score.
The check asks the rater to state which record wins a tie. The list’s key decides: declare the list keyed by an input that names each record once, and the lowest key wins.
One request then rates the same way however its list is ordered. Where the filing ranks tied records by a rule of its own, write that rule first, as then [cite: …]: priority.
Example
argmax picks the location with the greatest payroll, and the list locations lacks a key.
- A tie priority written as a plain last argument gets
a tie priority is written `then`. Writethen [cite: manual:p3]: l.hazard_factor. - A
thenpriority onfindgets a message that namesargmax,argmin,topnandrankas the functions that take one.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-KEYED-ORDER
A keyed list is unordered. A list declaredkeyed by is a map from key to record. The key names each item, and the order of the request is free to change.
index(self) and index(v) read the position of an item in its list. The check refuses them on a keyed list, so one request rates the same way however its list is ordered.
Where a filing orders the items, state that order in the rater: as an input of the item, or with rank over a rating value.
Example
position_factor reads the position of a vehicle, and the list vehicles is keyed by number.
=between two lists of keyed records gets`=` compares lists by position. Compare the items by key withallorexists, or compare the counts.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-UNION
A union arm holds inputs only. A union input such asexperience: Rated | Unrated accepts one of several record types. Each request sends one arm, named by its tag, with that arm’s fields.
The check asks each arm to be a record type that holds inputs and their refinements, each input a single value. The record that owns the union computes from the arm, inside a match that names every arm.
Every calculation that depends on the arm then sits in one place, and the match shows what each arm contributes to the premium.
Example
The arm Rated computes capped_modifier in a step of its own.
match, in the arm that binds Rated.
- A refinement on the union field gets
union `experience` takes no metadata, annotations or refinements. Refine the arm record types. - A union that lists one arm twice gets
union `experience` names arm `Rated` twice. - An arm that
Recordslacks getsunion arm `Unrate` must be a record type. - An arm field named
taggets`tag` names the arm in the request. Give the field another name. - An arm field that holds a list or a record gets
arm field `years` must be a number, Bool, Date, enum or Code, or an `Option` of one.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-UNION-USE
A union is read through a match. A union input such asexperience: Rated | Unrated holds one of several record types. Each arm has its own fields, so a field such as modifier exists only when the request sends the Rated arm.
The check asks every read of a union to be a match that names each arm. Inside an arm such as Rated(r), the name r reads that arm’s fields.
Each field is then read only where it exists, and a new arm added later is reported by every match that omits it.
Example
The factor compares the union with an arm name in an if.
match. The Rated arm binds its record as r and reads the modifier from it.
- An arm that reads a field its record lacks gets
`r.modifer`: arm `Rated` has no such field; its fields are `modifier`. - A record refinement that reads an arm field as
self.experience.modifiergets the same message for each arm that lacks the field. - A
[key]after an arm field getsvariant field `r.modifier` is not indexed.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-RECORD-ROOT
A rater has one top record. The records of a rater form one tree. A field typed with a record type, such asworksites: List(Worksite), places that record under the record that declares the field.
The check asks for one record type that sits at the top, under which every other record hangs. That top record is the request: one policy, with its worksites inside it.
One tree gives every value one place in a request. A record outside the tree stays empty in every request, so its inputs and calculations would be dead.
Example
The rater declares Worksite and leaves it outside Policy, so both sit at the top.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-RECORD-POSITION
Each record type sits at one field. A field typed with a record type places that record in the request tree.worksites: List(Worksite) puts every Worksite inside a Policy, in that list.
The check asks each record type to be the type of one field. A record type then has one enclosing record and one path in a request, such as Policy.worksites[].
A calculation in the record relies on that place. It reads its enclosing record by type name, and the compiler counts its instances from the one field that holds it.
Example
Worksite types two fields of Policy: headquarters and worksites.
Types for each input.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-RECORD-RECURSIVE
The record tree is finite. A field typed with a record type nests one record inside another. The check asks that nesting to end: a path down the fields passes each record type at most once. A record type that holds itself, directly or through other records, describes a request of unbounded depth. The compiler needs a finite tree to list every input, to prove each calculation and to state the cost of a rating. A record that sits under another record and also holds itself types two fields, so it getsE-RECORD-POSITION first.
Example
Policy holds its prior term as another Policy.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-REF-SCOPE
A reference points into a keyed list. A field typedRef(Worksite) points at one record of a list in the same request. The request sends that record’s key: a placement names its worksite by number.
The check asks for two things. The list of Worksite records is keyed by one of its inputs. The Ref field sits on a record below the one that holds the list.
A key names each record of the list once, so every reference resolves to exactly one record. The rater then reads through it, as in self.worksite.hazard_factor.
Example
Placement refers to a Worksite, and the list worksites lacks a key.
- A
keyed bythat names a field the record lacks gets`Worksite` has no such input `numbr`. The key is an input that holds a number, aDate, an enum member or aCode. - A
keyed byon a field of another shape, such as one record, gets`worksites` is not a `List(R)` field. - A
Refon the record that holds the list getsthe field must sit on a record that `Policy` encloses. Move the field to a record below it.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-ANCESTOR-READ
A type name reads an enclosing record. A calculation reads values in three directions.self.x reads its own record, a field reads a child, and a record type name reads a record that encloses this one.
Inside Worksite, Policy.experience_modifier reads the one policy that holds the worksite. The check asks every read by type name to name such an enclosing record.
A record lower in the tree, or beside this one, can occur many times in a request, so its type name matches several records. A list field and a fold, or a Ref field, say which records the calculation means.
Example
Policy reads Worksite.payroll, and a policy holds a list of worksites.
- A read of the record’s own value by type name gets
`Policy.payroll` names the record whose calculation this is. Read it asself.payroll. - A read of a record beside this one gets the message of the example. Add a
Reffield that names the record, and read through it.
Complete files
Complete files
bad/rater.rsl
good/rater.rsl
E-INTERNAL
Report a compiler fault to Effective.E-INTERNAL reports a fault in the compiler itself. The compiler met a case that its rules leave out, and it stops with this code in place of a wrong answer.
The message names what stopped it. Send the message to Effective with the rater that caused it, so the compiler gains a clear check message for that case.
Meanwhile, look at the place the message names. A rater that gets this code often writes a form another way than RSL does, and the rewrite clears the message.
Example
sum adds two numbers here, and in RSL sum folds a list or an enum.
+. Keep sum for a list, as in sum(self.worksites as w, w.payroll).
- A file in
enums/saved in another encoding than UTF-8 getsenum CSV … is not UTF-8, together withE-ENUM-BACKING. Save the file as UTF-8. - A path in
enums/that the compiler fails to open getsfailed to read enum CSV ….
Complete files
Complete files
bad/rater.rsl
good/rater.rsl