Coverage
Category | Ratio | Coverage | OK Items | Total Items |
Specification | 49.8% | 103 | 207 | |
Code | 56.2% | 172 | 306 | |
Test | 96.2% | 77 | 80 |
Category | Ratio | Coverage | OK Items | Total Items |
Specification | 49.8% | 103 | 207 | |
Code | 56.2% | 172 | 306 | |
Test | 96.2% | 77 | 80 |
text: It is important to have design goals for a language. The
design goals of TRLC are:
bullets: ['The intended domain is requirements writing in an industrial\nsafety-critical context, where requirements carry a\nlot of meta-data and linkage.', 'Future language releases should be backwards compatible, as\nwe anticipate that there will be a large body of\nrequirements.', 'Language design should be extensible and flexible for future\nchanges.', 'Requirement files (but not necessarily model and check\nfiles) should be human-readable with minimal\ntraining or understanding of the language.', 'No business logic should be encoded in the language design.']
text: All input files are encoded in UTF-8. Passing a file with a
different encoding to TRLC results in
undefined behaviour.
text: It is recommended to try to detect this in an implementation
and raise a fatal error; but in the interest
of compatibility and sanity an
implementation shall not provide a way to
switch the encoding (at the command-line or
otherwise).
text: Whitespace is ignored.
text: Comments are C-Style `/* .. */` and C++ style `//`, and are
ignored. The matching for C-style comments
is non-greedy.
text: Identifiers start with a letter and are followed by letters,
numbers, or underscores.
text: A keyword is an identifier that is one of the following
reserved words:
bullets: ['abs', 'abstract', 'and', 'checks', 'else', 'elsif', 'enum', 'error', 'exists', 'extends', 'false', 'fatal', 'final', 'forall', 'freeze', 'if', 'implies', 'import', 'in', 'not', 'null', 'optional', 'or', 'package', 'section', 'separator', 'then', 'true', 'tuple', 'type', 'warning', 'xor']
text: Single character delimiters:
bullets: ['Brackets: `(` `)` `[` `]` `{` `}`', 'Punctuation: `,` `.` `=`', 'Operators: `*` `/` `%` `+` `-`', 'Boolean operators: `<` `>`', 'Symbols: `@` `:` `;`']
text: Double character delimiters:
bullets: ['Operators: `**`', 'Boolean operators: `==` `<=` `>=` `!=`', 'Punctuation: `=>` `..`']
text: Preference is (obviously) given to longer delimiters if
there is ambiguity; i.e. `==` takes
precedence over two `=` tokens.
text: Integers are base 2 (prefixed by `0b`), 10, or 16 (prefixed
by `0x`), and leading zeros are effectively
ignored. It is possible to separate digit
groups with an underscore for
readability. It shall be a lexing error to
use digits that do not fit into the base.
text: Decimals are base 10, and leading and trailing zeros are
effectively ignored.
text: There are three versions of a string, double quoted,
triple single quoted, and triple double quoted:
text: The value of a double quoted string is precisely the
characters between the two double quotes,
with all instances of the `\"` escape
sequence being replaced with `"`.
text: The value of a triple quoted string is the whitespace
trimmed string of characters between the triple quotes
(including the line breaks), The common whitespace at
the beginning of each line (ignoring blank lines) starting
at the second is removed. The trailing whitespace on
every line is removed. There is no escaping in a triple
quoted string.
text: There are three types of files:
bullets: ['`.rsl` They contains the user-defined type definitions and\noptionally user-defined warnings or checks', '(deprecated) `.check` They contain only user-defined warning\nor error messages for types declared in `.rsl`\nfiles', '`.trlc` They contain instances of the types (this is where\nyour requirements go)']
text: First, all `.rsl` files are parsed. Then, if no errors are
raised, all `.check` files are parsed. Finally, if no errors
are raised, all `.trlc` files are parsed.
text: After all files are parsed, references are resolved and
user-defined checks are applied.
text: It is unspecified how an implementation treats errors
in `.trlc` files, but it is recommended to not stop
processing after the first error.
text: All files start with a package indication and an optional import list.
bnf: file_preamble ::= package_indication
{ import_clause }
package_indication ::= 'package' IDENTIFIER_name
import_clause ::= 'import' IDENTIFIER_name
text: The package indication defines the "current package".
text: A package may be imported, in which case its name may be
used as the prefix of a `qualified_name`.
text: A package may not import itself.
text: A `.rsl` file starts with a package declaration and is
followed by type declarations.
bnf: rsl_file ::= file_preamble
{ type_declaration | check_block }
text: A package indication in an `.rsl` file declares a
package. Any given package name may only be
declared once globally.
text: In a `.rsl` file, a package may not import a package that
imports itself, directly or indirectly.
text: An implementation shall support the following builtin types,
that shall be made available for all packages:
bullets: ['Boolean', 'Integer', 'Decimal', 'String', 'Markup_String']
text: A `Boolean` has two values, `false` and `true`.
text: An `Integer` is a signed integer, with an implementation
defined range. This range shall be at least
-1000 to 1000, and this can be infinite.
text: A `Decimal` is a signed rational with a power-of-ten
denominator, with an implementation defined
range. This range for the numerator shall be
at least -1000 to 1000, and denominator is
always a natural number with a range of at
least 1 to 1000. Any of these ranges can be
infinite (but the denominator is always
non-negative).
text: A `String` is a sequence of implementation defined
characters. *(The decision to support Unicode
or not is left unspecified.)* The maximum
length of a String, if any, is implementation
defined, but shall be at least 1000.
text: A `Markup_String` is identical to a `String`, except for a
few additional constraints on the string
contents. Any value of type `Markup_String` is
a valid instance of `String`, but not the
other way around.
text: A package also includes a number of builtin functions that
are made available:
bullets: ['len', 'startswith', 'endswith', 'matches']
text: A package also makes available a number of numeric type
conversion functions:
bullets: ['Integer', 'Decimal']
bnf: described_name ::= IDENTIFIER_name [ STRING_description ]
text: A described name names an entity. The optional string
description has no static or dynamic
semantics.
text: Two described names are considered equal if their names are
equal.
text: The description has no bearing on equality. The string
description should be exported to the API, so
that downstream tools can make use of it. This
way we don't have to give some comments special meaning,
and we don't have to implement error-prone heuristics in
downstream tools for discovering these descriptions.
bnf: qualified_name ::= [ IDENTIFIER_package_name '.' ] IDENTIFIER_name
text: The package name of a qualified name must be the current
package or an imported package.
text: The name must be a valid symbol in the current scope (if no
package is provided) or a valid symbol of the
indicated package.
bnf: type_declaration ::= enumeration_declaration
| tuple_declaration
| record_declaration
text: It is an error to create a type with a name that is already
visible. (Note: this especially includes
shadowing one of the builtin types, but also
means you cannot shadow packages.)
bnf: enumeration_declaration ::= 'enum' described_name
'{' { enumeration_literal_specification } '}'
enumeration_literal_specification ::= described_name
text: Each described name declares an enumeration literal
specification, which shall have a unique name
in that enumeration.
text: It is not an error to have the same literal specification
for different enumerations.
text: It is not an error to have an enumeration literal with the
same name as a record or enumeration - as
there is no ambiguity - but it is recommended
that an implementation emits a warning in this
case.
text: It is an error to specify an (empty) enumeration without any
literals.
bnf: tuple_declaration ::= 'tuple' described_name
'{' field_declaration
{ [ separator_declaration ]
field_declaration }
'}'
field_declaration ::=
described_name [ 'optional' ] qualified_name_FIELD_TYPE
separator_declaration ::=
'separator' separator_symbol
separator_symbol ::= IDENTIFIER
| '@'
| ':'
| ';'
text: A field type names any valid visible and complete
type. *(Specifically you may not have a recursive
tuple reference.)*
text: A tuple type shall declare separators between all
fields, or none of them.
text: A tuple type with separators may not contain fields
that have tuple types with separators. *(This is to
avoid parsing ambiguities for values such as `1; 2;
3`.)*
text: Only tuples with separators may use optional fields. Tuples
without separators cannot have optional fields.
text: After an optional field has been declared, all fields
following it must also be optional.
text: It is an error to declare two fields with the same
name in the same tuple. *(Note that it is possible,
but a bad idea, to have a separator with the same
identifier as a field.)*
text: There is a potential source of ambiguity between
tuples with two or more integers separated by a `x`
or `b`. This is resolved by giving precedence to the
lexer. For example the string `0x123` should yield
the hex integer 291; but the string `0 x123` or `0 x
123` will generate the tuple. It is recommended to
warn when building tuples where a `b` or `x`
separator follows an integer.
bnf: record_declaration ::= [ inheritance_qualifier ] 'type' described_name
[ 'extends' qualified_name_ROOT_TYPE ]
'{' { component_declaration |
component_freezing } '}'
inheritance_qualifier ::= 'abstract' | 'final'
component_declaration ::=
described_name [ 'optional' ]
qualified_name_COMPONENT_TYPE [ array_declaration ]
array_declaration ::= '[' INTEGER_lower '..' '*' ']'
| '[' INTEGER_lower '..' INTEGER_upper ']'
component_freezing ::= 'freeze' IDENTIFIER_component '=' value
text: The root type for a record extension must be a valid record type.
text: A record may be marked `abstract`, in which case no objects of this
type may be declared. It must be extended *(even if that extension is
empty)* before instances of that type can be declared.
*(Note that it is possible to have components of an abstract type.)*
text: A record may be marked `final`, and the extension of a `final` type
is also considered to be final *(even if it is not explicitly marked
as such)*. An extension of a final type may not contain any
component declarations.
text: Each component declaration shall have a unique name in that
record declaration, or any of its root
types. *(Two separate record extensions may
defined the same component, and they may even
have different types. But you cannot re-define
a component from a parent type.)*
text: Each component type shall refer to a valid type, or to the
name of the record type itself. It is an error
to refer to a type that has not been declared
yet. *(As there are no forward declarations in
the language, this forces the user type
hierarchy to be a DAG, but permitting
self-references.)*
text: A component with an array declaration introduces an
anonymous array type. This means the type of
the record component is not the type as
specified, but rather an array where the
element type is that type.
text: It is an error to specify an upper bound that is less than
the lower bound. *(Note that it is permitted
for them to be equal.)*
text: Any valid previously declared record component may be
frozen, providing a value of the correct type
for the given field in all instances of the
record type. It is an error to freeze a
component that is already frozen.
text: Arrays are indexed by natural numbers and the first index is
0.
text: An instance of an array shall not have fewer elements than
its lower bound.
text: An array type has no upper bound if `*` is specified. An
array instance for an array type with an upper
bound shall not have more elements than its
upper bound.
text: An implementation may impose an arbitrary limit to the
actual upper bound of an array *(that is treat
the `*` as if it was that limit)*. This limit,
if it exists, shall be greater than or equal
to 1000.
text: A record type that extends another inherits all components,
user defined checks, and component freezes
specified for its root type.
text: Instances of an extension may be
provided as a reference where a root type is
required; but not the other way around. *(This
provide a limited form of polymorphism, but
with the Liskov substitution principle
guaranteed.)*
text: It is recommended to emit a warning on array types with an
upper bound of zero.
text: It is recommended to emit a warning on array types with an
upper bound of one.
text: It is recommended to emit a warning on freezing a component
declared in the same record.
text: It is recommended that an implementation checks if a
record type contains a tuple with a identifier
separator that is the same as one of its
components. While this is not an error (parsing for
values is greedy) it is still highly confusing.
text: A `.check` file is simply a set of check blocks.
bnf: check_file ::= file_preamble
{ check_block }
text: It is an error to indicate a package that has not been
declared in an `.rsl` file.
text: A check file may not import any packages.
text: Check files are a deprecated feature that allows you to
specify checks separate from type
declarations. While this seems like a good
idea at first, in a large project this can
create confusion: consider a global definition
for your requirement type stored in a central
place which everyone is supposed to use. In
this example some random user can create in
their sub-project a check file that applies to
the global definitions, causing hassle for
everyone. It is recommended that an
implementation suggests moving any check from
a `.check` file into the package `.rsl` file.
bnf: check_block ::= 'checks' IDENTIFIER_record_or_tuple_name
'{' { check_declaration } '}'
check_declaration ::= expression ','
[ severity ] STRING_message
[ ',' STRING_details ]
[ ',' IDENTIFIER_component_name ]
severity ::= 'warning'
| 'error'
| 'fatal'
text: It is an error to refer to a type that does not exist in the
current package, or is not a record or tuple type.
text: In a check declaration or expression, it is an error to
refer to a component name (or field name) that does not belong
to the record (or tuple respectively) indicated.
text: It is an error to include a newline in the message.
text: Note that it is never possible to add checks to a type from
a foreign package. In check files this is more
obvious as you cannot have an import clause,
but this is also true for checks declared in
`.rsl` files since it is not possible to
specify a qualified name.
text: Each check inside a check block is evaluated in the specified
order.
text: If multiple check blocks are declared for the same type,
then the order of evaluation of each check
block is unspecified.
text: For record extensions, all checks for the base record must
be evaluated before any check of the record
extension is evaluated.
text: If the evaluated expression is true, no action is taken. If
it is false, then a diagnostic is emitted with
the specified message and details. It is
implementation defined if and how the details
are shown, for example in a "brief" mode they
could be omitted.
text: The severity, if provided, controls how future errors are
treated, and how the TRLC implementation
terminates.
bullets: ['A warning has no effect other than emitting the message.', 'An error (the default, in case severity is not specified)\ncauses an implementation to eventually\nreturn a non-zero error code, but further\nchecks will be evaluated (potentially\ncreating more messages).', 'A fatal message is like an error, except that no further\nchecks from this check block shall be\nevaluated for this object.']
text: The intended meaning of the message is a short description
of what is wrong. The intended meaning for the
optional details is a longer explanation,
including perhaps on what to fix.
text: The component name (if specified) is a hint where the
message should be anchored. It is
implementation defined how or if this is taken
into account. A suitable anchoring for a
message without a component or field could be the
object declaration itself. If only a
single component or field is used in the expression
then the message could be anchored the same
way as if the component was indicated
explicitly in the check.
text: It is an important design goal to keep the type system sane
and following LSP. I.e. each subtype may only
narrow the values permitted in a type. This
means for a record R and its extension RE; any
valid instance of RE is always a valid
instance of R if a new binding of R is created
considering only the components that are in R. It
will never be possible to delete, suppress,
widen, or omit checks in record extensions.
bnf: expression ::= relation { 'and' relation }
| relation { 'or' relation }
| relation [ 'xor' relation ]
| relation [ 'implies' relation ]
bnf: relation ::= simple_expression [ comparison_operator simple_expression ]
| simple_expression [ 'not' ] 'in' membership_choice
| simple_expression [ 'not' ] 'in' simple_expression
membership_choice ::= simple_expression '..' simple_expression
bnf: simple_expression ::= [ adding_operator ]
term { adding_operator term }
bnf: term ::= factor { multiplying_operator factor }
bnf: factor ::= primary [ '**' primary ]
| 'not' primary
| 'abs' primary
bnf: primary ::= INTEGER
| DECIMAL
| STRING
| 'true'
| 'false'
| 'null'
| name
| '(' expression ')'
| '(' quantified_expression ')'
| '(' conditional_expression ')'
text: The parsing of unary minus can be confusing: `-a % b` is
actually `- (a % b)`. The semantics of `%` are
carefully specified so that this does not
matter. It does also mean that `-a / -b` is
not legal and needs to be written as either
`-a / (-b)` or even better `(-a) / (-b)`. It
is recommended that a linter warns whenever a
unary minus is encountered that has a
non-trivial `term` or `factor` as its operand.
text: A name can be one of 5 things:
bullets: ['A qualified name referring to either some object, a component\n(in a record) or field (in a tuple), or an enumeration type.', 'A tuple field or enumeration literal.', 'An index into an array.', 'A (builtin) function call.']
bnf: name ::= qualified_name
| name '.' IDENTIFIER
| name '[' expression ']'
| name '(' parameter_list ')'
parameter_list ::= expression { ',' expression }
text: The `qualified_name` must resolve to record component or
tuple field in scope, or a visible record
object, or a visible enumeration type.
text: If the prefix of a dot (`.`) access resolves to an
enumeration type, then the identifier must be
a valid literal of that type. If the prefix of
a dot (`.`) access resolves to a tuple, then
the identifier must be a valid field of the
type of that tuple. Any other prefix is an
error.
text: The type of the `name` in an array indexing prefix (i.e. `[`
.. `]`) must be an array type.
text: The `name` in a function call must resolve to one of the
builtin functions.
text: The builtin function `len` is of arity 1. Its parameter must
be of type `String` or an array type. Its
return type is `Integer`.
text: The builtin functions `startswith` and `endswith` are of
arity 2. All of their parameters must be of
type `String`. The return type of either
function is `Boolean`.
text: The builtin function `matches` is of arity 2. Its parameters
must be of type `String`. The return type is
`Boolean`. The second parameter must be a
valid regular expression. *(It is
implementation defined which regular
expression language is used, but it is highly,
_highly_ recommended to implement the standard
POSIX regular expressions.)*
text: In addition, the second parameter to the `matches` function
must be a static compile-time constant,
i.e. it must not depend on the value of a
record field or the value of a quantified
variable.
text: The numeric type conversion functions are of arity 1 and
polymorphic (they are defined for both integer and
rationals). They return the type indicated by the function
name. *(They are polymorphic in case we want to introduce
a true rational type, or a floating point type.)*
text: The `len` function computes the length of the given string
or array.
text: The `startswith` function returns true iff the first
parameter fully contains the second parameter
at its start.
text: The `endswith` function returns true iff the first parameter
fully contains the second parameter at its
end.
text: The `matches` function returns true iff the first parameter
is matched by the regular expression given in
the second parameter.
text: The `Integer` type conversion rounds to the nearest
integral, away from zero in the case of ties.
text: The `Decimal` type conversion simply converts
the given integer to the exact same rational.
text: Name resolution is case sensitive.
bnf: comparison_operator ::= '==' | '!=' | '<' | '<=' | '>' | '>='
adding_operator ::= '+' | '-'
multiplying_operator ::= '*' | '/' | '%'
text: For a chain of operators of the same category, the
association is left to right.
text: It is implementation defined if type checks (range or
length) are performed for intermediate values;
if type checks are not performed then the
resulting expression must not be an error.
text: This means you either type check all the time, or you
guarantee that any intermediate will not
create an error in the implementation, as long
as the final value fits in the relevant
type. For example if A and B are maximum
length Strings, then `"potato" in A + B` may
either create an error when attempting to
concatenate the strings, OR it must work
correctly. What you cannot do is cause
undefined behaviour in the evaluation.
text: There are five Boolean operators defined (not, and, or, xor,
implies) for expressions of Boolean type.
The type of the result is Boolean.
text: Operators with short-cut semantics are `and`, `or`, and
`implies`. They first evaluate their
left-hand side and proceed to evaluate the
right-hand side if it could influence the
final result. The semantics of these
operators are the usual ones.
text: Operators with standard semantics are `xor`, and `not`. They
do the usual thing.
text: The equality operators `==` and `!=` are defined for all
types, as long as the types are compatible,
i.e. they are the same types or one type is
a (transitive) record extension of the other. The
result is a Boolean.
text: The ordering relations `<`, `<=`, `>=`, and `>` are defined
for pairs of integers or pairs of rationals only. The result
is a Boolean.
text: Range membership tests `not in` and `in` are defined for
integers and rationals only. All operands
must have the same type, and the result is a
Boolean.
text: Substring tests `not in` and `in` are defined for
Strings only. The result is a Boolean.
text: Array membership tests `not in` and `in` are defined for all
arrays. The type of the left-hand side must
match the element type of the array. The
result is a Boolean.
text: Null is only equal to itself.
text: Two tuple instances are equal if they both
contain the same values for all their components.
text: Two record references are equal if they both
refer to the same record.
text: Two arrays are equal if they both have the same length
and items (in the same order).
text: The meaning of the relationship operators are the usual.
text: An inclusive range membership test `x in a .. b` has the
same meaning as the Boolean expression `x >=
a and x <= b`. *(This means if a is less
than b, it is not an error. Instead the
result of such a test is always false.)*
text: An exclusive range membership test `x not in a .. b` has the
same meaning as the Boolean expression `x <
a or x > b`.
text: The meaning of the substring test is the usual. Note that
the substring test is of course performed on
the value of the string, not the original
literal.
text: The array membership test `X in A` is equivalent to `(exists
item in A => item == X)`. The array
non-membership test `X not in A` is
equivalent to `(forall item in A => item !=
X)`.
text: The binary adding operator `+` is defined for integers,
rationals, and strings. The binary
subtraction operator `-` is defined for only
integers and rationals.
text: When `+` is used to concatenate strings, the result is
always a `String`, even if one or both
operands if of type `Markup_String`. *(This
means the only way you can create a markup
string is when you construct it at record
object declaration.)*
text: The unary adding operator `+`, and the unary subtraction
operator `-` is defined only for integers
and rationals.
text: For binary adding operators the types of the operands have
to match, and the result is the same type as
the two operands. For unary adding operators
the result is always the same as the
operand.
text: The definition of `+` and `-` for integers and rationals is
the usual one.
text: The definition of `+` for strings is string concatenation.
text: The multiplying operators `*`, `/` are defined for integers
and rational types. The remainder operator
`%` is defined for integers only. For any of
these the result is always the same type as
the operands.
text: The definition of `*` is the usual one.
text: The definition of `/` for integers is floor division. *(For
example `5 / 2` is `2` and `-5 / 2` is
`-3`.)*
text: The definition of `/` for rationals is the usual one.
text: The modulus division for `x % y` satisfies the relation `x =
y*N + (x % y)`, for some (signed) value of
`N`, with one of the following constraints
met:
bullets: ['`x % y` is `0`', '`x % y` has the same sign as `y` and an absolute\nvalue less than `y`']
text: Division by zero or modulo division by 0 is an error.
text: The exponentiation operator `**` is defined for integers and
rational bases, and returns the same type as
its base. The exponent type is always an
Integer.
text: The right-hand side parameter of `**` must be a static
compile-time constant, i.e. it must not
depend on the value of a record field or the
value of a quantified variable. It must not
be negative.
text: The absolute value prefix operator `abs` is defined for
integers and rationals, and returns a
(positive) integer or rational respectively.
text: The logical negation prefix operator `not` is defined for
Booleans only, and returns a Boolean.
text: The definition of exponentiation `**` is the usual one.
text: The definition of absolute value `abs` is the usual one.
text: The definition of logical negation `not` is the usual one.
bnf: quantified_expression ::=
quantifier IDENTIFIER_name 'in' IDENTIFIER_component_name '=>'
expression_PREDICATE
quantifier ::= 'forall' | 'exists'
text: The component name must be defined in the current record,
and must be an array type.
text: A quantified expression introduces a new name, that is valid
only inside the predicate. This new name must
not shadow any other. *(This means two
separate quantified expressions may use the
same name, but you may not nest and shadow,
and you may not shadow a component name
either.)*
text: The type of the predicate must be Boolean. The result of a
quantified expression is Boolean.
text: During evaluation of the quantified expression, each element
of the array is evaluated in sequence and its
value is bound to the declared name. The
predicated is then evaluated with this
binding.
text: For universal *(forall)* quantification the final value is
true iff all predicates evaluate to
true. *(This means universal quantification
over an empty array is vacuously true.)*
text: For existential *(exists)* quantification the final value is
true iff the predicate evaluate to true at
least once. *(This means existential
quantification over an empty array is
vacuously false.)*
text: In general Quantification is equivalent to a chain of `and`
or `or` expressions, however it is left
unspecified if quantification is using
short-circuit semantics or not. It is
recommended (for sanity) to do the following:
bullets: ['For actual evaluation, use short-circuit semantics, as speed\nis important.', 'For static analysis or linting, use non-short-circuit\nsemantics, as using a quantifier as a\ncomplex guard is not reasonable.']
bnf: conditional_expression ::=
'if' expression_CONDITION 'then' expression_DEPENDENT
{'elsif' expression_CONDITION 'then' expression_DEPENDENT }
'else' expression_DEPENDENT
text: The condition expressions must be of Boolean type. The
dependent expressions must all be of the same
type, and the type of the entire conditional
expression is also of that type.
text: Each condition is evaluated in sequence. Evaluation stops on
the first condition that evaluates to true;
after which the corresponding dependent
expression is evaluated and returned.
text: If all conditions are evaluated to false, then the else
dependent expression is evaluated and
returned.
text: A `.trlc` file is simply a set of record object declarations.
bnf: trlc_file ::= file_preamble
{ trlc_entry }
trlc_entry ::= section_declaration
| record_object_declaration
text: It is permitted to indicate a package that has
not been declared in an `.rsl` file, in which
case it is declared by the
package_indication in the `.trlc` file. Such a package is
declared late. If two `.trlc` files declare the same
package, then it is unspecified which file actually
declares it.
text: For TRLC files it is impossible fully parse a file
in isolation, since we must process at least the package
indication of every other trlc file to know which import
statements are valid.
text: A section has no semantic impact, and no impact on name
resolution. Section names do not have to be
unique. It may be exposed in an API, for
example to section a HTML view of
requirements.
bnf: section_declaration ::= 'section' STRING_section_name
'{' { trlc_entry } '}'
bnf: record_object_declaration ::=
qualified_name_RECORD_TYPE IDENTIFIER_object_name
'{' { component_association } '}'
component_association ::= IDENTIFIER_component_name '=' value
value ::= [ adding_operator ] INTEGER
| [ adding_operator ] DECIMAL
| STRING
| qualified_name_RECORD_OBJECT
| qualified_name_ENUM_TYPE '.' IDENTIFIER_enumeration_literal
| '[' [ value { ',' value } ] ']'
| tuple_aggregate
tuple_aggregate ::= '(' value { ',' value } ')'
| value { separator_symbol value }
text: The type of the declaration must be a valid, non-abstract
record type. If no qualified name is given, the record type
must be in the indicated package. *(Note that it is not
legal to declare a tuple object.)*
text: The name of the declaration must be a unique name and
sufficiently distinct in the current
package. *(See name resolution for a
definition of sufficiently distinct.)* The
name of the declaration must not shadow a type
name or package.
text: Each component name must be a valid, non-frozen component
of the record type. It is an error to attempt to assign to
a frozen component *(even if the value assigned would be the
same value as it's frozen value)*.
text: Each enumeration must be a valid enumeration type in the
indicated (or qualified) package. Each
enumeration literal must be a valid literal of
the indicated enumeration in the prefix.
text: The type of each value must match each component or array
element type. Records are specified through
references *(there is no way to specify an
inline anonymous instance)*.
text: It is an error to not provide a value for a non-optional
component.
text: The aggregate of a tuple must use the correct form. A
tuple without separators uses the bracket syntax
(e.g. `(1, 2, 3)`, and a tuple with separators must
use the separator syntax (e.g. `12345@42`).
text: A tuple value for a tuple without separators must
contain one value, in order, for each of its fields.
text: A tuple value for a tuple with separators must
contain the separator symbols as indicated in the type,
in order. Optional fields and their preceding separator
may be omitted. Once an optional field has been omitted,
all following (optional) fields must also be omitted.
text: A record object declaration creates a new binding for a
record. The value of any frozen components is the value
provided in the freezing declaration. After references
are resolved, all applicable checks on the object, including
any checks for tuples, are evaluated in the context
of this binding.
text: It is an error to refer to a record by name that does not
exist. It is legal to refer to an record that
has not been encountered yet, as references
are resolved after parsing.
text: A record reference must match in type, i.e. be of the
correct type or any record extension of that
type. *(For example if RE extends R, then a
list of R may contain references to instances
of R and RE. A list of RE may not contain any
references to R.)*
text: The checks for a tuple aggregate are immediately evaluated
after the last value is parsed.
text: When declaring record objects there are wider rules that
indicate name clashes. Specifically a record
may not be declared if its "simplified name"
clashes with any other "simplified name". A
"simplified name" is the name converted to
lowercase and all underscored removed.
text: For example the simplified name of `Foo_Bar` is `foobar`
and therefore it clashes with `Foobar`,
`F_Oobar`, or `Foo_B_A_R`. But only at record
declaration; when referring to other object
you still have to use a precise match.
text: The purpose of this rule is to avoid requirements that
have hard to distinguish names.
text: When exposing record instances through the API, it is
required to make the type of the instance
available. There are some alternatives,
none of which are required:
bullets: ['Provide an implicit String record field named `type` that\ncarries this information (it is safe to do\nthat, as it is impossible to specify a\nrecord type with a field named `type`).', 'Provide a function or attribute that, when called, provides\nthe type information.', 'Provide the type information through the type system of the\nAPI language (e.g. the Python type system).']
text: A `Markup_String` allows you to inline references to TRLC
record in a string. The format is limited and
simple: any name or comma-separated list of
names enclosed in double square brackets
(i.e. `[[` and `]]`) is considered a
reference.
text: Attempting to nest, or close a list that does not exist, or
not close an open list before the end of the
string is an error.
text: The fragment of BNF grammar applicable is
qualified_name. The type of each named object
must be a record type. *(This means you cannot
reference types or enumerations.)*
text: The references are resolved late, just like an ordinary
record reference. *(This means you can
forward-reference objects, or reference
yourself.)*
text: The name resolution rules for the qualified_name are exactly
the same as they are for any other
qualified_name. *(This means you need to
import all packages from which you reference
objects.)*
text: The literal null value is only permitted to appear in
an equality or inequality. Any other context (such as
`(if a then null else b)` is rejected statically.
text: The expression `null == null` is statically true.
text: The value of an optional component or field that is
not specified is `null`.
text: For any other operator or operation, the null value is
considered out of bounds and raises an
error. *(This means you can check if something
is null or not, but any other use will cause
an error.)*