Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ generated in the following situations:

### 3.0.0-dev

* [TRLC] * Add support for nested (dotted) packages, e.g. `package com.bigcorp.safety`.

* [TRLC_RST] Render soft-wrapped description lines as a single RST
paragraph instead of splitting each line into its own paragraph.
Insert a blank line to start a new paragraph.
Expand Down
4 changes: 4 additions & 0 deletions documentation/TUTORIAL-ADVANCED.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ Finally note that a reference is always a reference. You cannot
specify an entirely new record "inline". We plan to support something
like this in a future release, but it will be distinct from records.

For grouping packages into a hierarchy (`com.bigcorp.safety`) and
importing whole subtrees at once, see
[Subpackages](TUTORIAL-PACKAGE.md#subpackages).

## In-line references

It is also possible to refer to other TRLC records directly in
Expand Down
78 changes: 78 additions & 0 deletions documentation/TUTORIAL-PACKAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,84 @@ the same package. For example instead of having one massive file
`car.trlc`, you could have many: `engine_control.trlc`,
`cruise_control.trlc`, `radar.trlc`, etc.

## Subpackages

In very large projects a single flat list of package names becomes hard
to manage. Package names may instead be *dotted*, forming a hierarchy of
subpackages:

```
package com.bigcorp.safety
```

Before you can declare a nested package, every prefix must already be a
declared package. So to declare `com.bigcorp.safety` you must also have
declared `com` and `com.bigcorp` somewhere (each in its own `.rsl`
file).

For example, you would need three separate `.rsl` files:

```
// com.rsl
package com
```

```
// com.bigcorp.rsl
package com.bigcorp
```

```
// com.bigcorp.safety.rsl
package com.bigcorp.safety

enum ASIL_Level { QM A B C D }
```

To reference a type or object from a nested package, use its full dotted
prefix:

```
package com.bigcorp.brakes
import com.bigcorp.safety

type Brake_Requirement {
level com.bigcorp.safety.ASIL_Level
}
```

### Importing subpackages

Importing is *not* transitive: `import com.bigcorp.safety` gives you
access to the types in `com.bigcorp.safety` only — not to its parent
`com.bigcorp`, and not to any of its children.

If you want a whole subtree at once, use a *wildcard import*:

```
package com.bigcorp.brakes
import com.bigcorp.safety.*

type Brake_Requirement {
level com.bigcorp.safety.ASIL_Level // from com.bigcorp.safety
unit com.bigcorp.safety.units.Pressure // from a child package
}
```

`import com.bigcorp.safety.*` makes `com.bigcorp.safety` **and every
package below it** visible. The linter will warn you about a wildcard
import that ends up unused, and about an explicit import that is already
covered by a wildcard.

A small note on the `.` separator: it means different things depending
on what is to its left. After a *package* it descends the namespace
(`com.bigcorp.safety.Type`); after a *value* it accesses a tuple or
record field (`my_record.field`). TRLC resolves the package prefix
first, against the imported packages, before any field access — so there
is no ambiguity. To keep it that way, a subpackage's leaf name must be
distinct from any type or object in its parent package (TRLC rejects the
clash).

## Sections

Even if you split the requirements of your project over many files,
Expand Down
77 changes: 73 additions & 4 deletions language-reference-manual/lrm.trlc
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,14 @@ section "Preamble" {
file_preamble ::= package_indication
{ import_clause }

package_indication ::= 'package' IDENTIFIER_name
package_name ::= IDENTIFIER { '.' IDENTIFIER }

import_clause ::= 'import' IDENTIFIER_name
package_indication ::= 'package' package_name

import_clause ::= 'import' package_name
| 'import' wildcard_import

wildcard_import ::= package_name '.' '*'
'''
}

Expand All @@ -300,6 +305,68 @@ import_clause ::= 'import' IDENTIFIER_name
text = '''A package may not import itself.'''
}

Static_Semantics Nested_Package_Names {
text = '''A package name may consist of multiple dot-separated
IDENTIFIER segments, forming a nested package hierarchy.
Each segment must be a valid IDENTIFIER. The full
dotted name is subject to the Sufficiently_Distinct rule.'''
}

Note Nested_Package_Name_Similarity {
text = '''When applying the Sufficiently_Distinct rule to nested package
names, dots are preserved in the simplified form. For example
`foo.bar` and `Foo.Bar` both simplify to `foo.bar` and
therefore clash. However `foobar` and `foo.bar` have different
simplified forms (`foobar` vs `foo.bar`) and do not clash.'''
}

Static_Semantics Parent_Package_Required {
text = '''A package `foo.bar` may only be declared if a package `foo`
has already been declared. More generally every prefix of a
nested package name must itself be a declared package.'''
}

Static_Semantics Nested_Visibility {
text = '''Importing a nested package `foo.bar` grants access to the
types declared in `foo.bar`. Importing the parent package
`foo` does not grant access to types in `foo.bar`.'''
}

Static_Semantics Wildcard_Import {
text = '''An import clause of the form `foo.*` is a wildcard import. It
grants access to the package `foo` and to every package that
is a descendant of `foo` in the nested package hierarchy. It
is equivalent to importing each of those packages
individually. A wildcard import `foo.*` is only legal if a
package `foo` has been declared; if `foo` has no descendants
the wildcard import is equivalent to importing `foo`.'''
}

Recommendation Wildcard_Trivial {
text = '''If a wildcard import `foo.*` is used but `foo` has no
sub-packages, the wildcard is equivalent to a plain
`import foo`. Prefer the explicit form to make the
import intent clear.'''
}

Static_Semantics Wildcard_Self_Cover {
text = '''A wildcard import whose root covers the current package (i.e.
the current package is the root or a descendant of it) is
permitted. The current package remains implicitly visible and
is not considered imported by the wildcard. An explicit
(non-wildcard) import of the current package remains illegal
(see Self_Imports).'''
}

Static_Semantics Subpackage_Member_Distinct {
text = '''Within a package, the leaf name of a direct sub-package must be
sufficiently distinct (see Sufficiently_Distinct) from the
name of any type or object declared in that package. This
avoids ambiguity in qualified-name resolution, which consults
sub-packages before members. The rule applies to all type
kinds: record types, union types, and enumeration types.'''
}

}

/***********************************************************************/
Expand Down Expand Up @@ -431,13 +498,15 @@ section "Qualified names" {

Grammar Qualified_Name {
bnf = '''
qualified_name ::= [ IDENTIFIER_package_name '.' ] IDENTIFIER_name
qualified_name ::= [ package_name '.' ] IDENTIFIER_name
'''
}

Static_Semantics Valid_Qualifier {
text = '''The package name of a qualified name must be the current
package or an imported package.'''
package or an imported package. For nested packages, the
full dotted package name must be imported; importing a
parent package does not give access to sub-packages.'''
}

Static_Semantics Valid_Name {
Expand Down
3 changes: 2 additions & 1 deletion tests-system/cyclic-packages/output
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork
cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork
| Bar is declared in bar.rsl
| Baz is declared in baz.rsl
| Bork is declared in bork.rsl
Processed 4 models and 0 requirement files and found 1 error
2 changes: 1 addition & 1 deletion tests-system/cyclic-packages/output.brief
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cyclic-packages/bar.rsl: trlc error: circular inheritence between Baz | Bork
cyclic-packages/bar.rsl: trlc error: circular inheritance between Bar | Baz | Bork
Processed 4 models and 0 requirement files and found 1 error
3 changes: 2 additions & 1 deletion tests-system/cyclic-packages/output.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork
cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork
| Bar is declared in bar.rsl
| Baz is declared in baz.rsl
| Bork is declared in bork.rsl
Processed 4 models and 0 requirement files and found 1 error
3 changes: 2 additions & 1 deletion tests-system/cyclic-packages/output.smtlib
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork
cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork
| Bar is declared in bar.rsl
| Baz is declared in baz.rsl
| Bork is declared in bork.rsl
Processed 4 models and 0 requirement files and found 1 error
5 changes: 4 additions & 1 deletion tests-system/duplicate-imports/output
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Foo
^^^ duplicate-imports/bar.trlc:4: warning: duplicate import of package Foo
Processed 1 model and 1 requirement file and found 1 warning
import Foo
^^^ duplicate-imports/bar.trlc:3: issue: unused import Foo [unused_imports]
| Consider deleting this import statement if not needed.
Processed 1 model and 1 requirement file and found 2 warnings
5 changes: 4 additions & 1 deletion tests-system/duplicate-imports/output.smtlib
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Foo
^^^ duplicate-imports/bar.trlc:4: warning: duplicate import of package Foo
Processed 1 model and 1 requirement file and found 1 warning
import Foo
^^^ duplicate-imports/bar.trlc:3: issue: unused import Foo [unused_imports]
| Consider deleting this import statement if not needed.
Processed 1 model and 1 requirement file and found 2 warnings
6 changes: 6 additions & 0 deletions tests-system/lint-nested-unused/app.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app
import ns.pkg

type R {
v Integer
}
1 change: 1 addition & 0 deletions tests-system/lint-nested-unused/ns.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package ns
5 changes: 5 additions & 0 deletions tests-system/lint-nested-unused/nspkg.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ns.pkg

type T {
v Integer
}
Empty file.
4 changes: 4 additions & 0 deletions tests-system/lint-nested-unused/output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ns.pkg
^^ lint-nested-unused/app.rsl:2: issue: unused import ns.pkg [unused_imports]
| Consider deleting this import statement if not needed.
Processed 3 models and 0 requirement files and found 1 warning
1 change: 1 addition & 0 deletions tests-system/lint-nested-unused/output.brief
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 3 models and 0 requirement files and found no issues
2 changes: 2 additions & 0 deletions tests-system/lint-nested-unused/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{}
Processed 3 models and 0 requirement files and found no issues
4 changes: 4 additions & 0 deletions tests-system/lint-nested-unused/output.smtlib
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ns.pkg
^^ lint-nested-unused/app.rsl:2: issue: unused import ns.pkg [unused_imports]
| Consider deleting this import statement if not needed.
Processed 3 models and 0 requirement files and found 1 warning
2 changes: 2 additions & 0 deletions tests-system/lint-nested-unused/tracing
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
LRM.Nested_Visibility
LRM.Import_Visibility
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-redundant/foo.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package foo
7 changes: 7 additions & 0 deletions tests-system/lint-wildcard-redundant/foobar.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package foo.bar
import sub.*
import sub.deep

type R {
x sub.deep.U
}
Empty file.
4 changes: 4 additions & 0 deletions tests-system/lint-wildcard-redundant/output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import sub.deep
^^^ lint-wildcard-redundant/foobar.rsl:3: issue: redundant import sub.deep, already covered by wildcard import sub.* [unused_imports]
| Consider deleting this import statement.
Processed 4 models and 0 requirement files and found 1 warning
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-redundant/output.brief
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 4 models and 0 requirement files and found no issues
2 changes: 2 additions & 0 deletions tests-system/lint-wildcard-redundant/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{}
Processed 4 models and 0 requirement files and found no issues
4 changes: 4 additions & 0 deletions tests-system/lint-wildcard-redundant/output.smtlib
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import sub.deep
^^^ lint-wildcard-redundant/foobar.rsl:3: issue: redundant import sub.deep, already covered by wildcard import sub.* [unused_imports]
| Consider deleting this import statement.
Processed 4 models and 0 requirement files and found 1 warning
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-redundant/sub.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package sub
5 changes: 5 additions & 0 deletions tests-system/lint-wildcard-redundant/subdeep.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package sub.deep

type U {
v Integer
}
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-redundant/tracing
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LRM.Wildcard_Import
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-unused/foo.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package foo
6 changes: 6 additions & 0 deletions tests-system/lint-wildcard-unused/foobar.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package foo.bar
import sub.*

type R {
v Integer
}
Empty file.
4 changes: 4 additions & 0 deletions tests-system/lint-wildcard-unused/output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import sub.*
^^^ lint-wildcard-unused/foobar.rsl:2: issue: unused wildcard import sub.* [unused_imports]
| Consider deleting this import statement if not needed.
Processed 3 models and 0 requirement files and found 1 warning
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-unused/output.brief
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 3 models and 0 requirement files and found no issues
2 changes: 2 additions & 0 deletions tests-system/lint-wildcard-unused/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{}
Processed 3 models and 0 requirement files and found no issues
4 changes: 4 additions & 0 deletions tests-system/lint-wildcard-unused/output.smtlib
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import sub.*
^^^ lint-wildcard-unused/foobar.rsl:2: issue: unused wildcard import sub.* [unused_imports]
| Consider deleting this import statement if not needed.
Processed 3 models and 0 requirement files and found 1 warning
5 changes: 5 additions & 0 deletions tests-system/lint-wildcard-unused/sub.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package sub

type T {
v Integer
}
1 change: 1 addition & 0 deletions tests-system/lint-wildcard-unused/tracing
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LRM.Wildcard_Import
3 changes: 3 additions & 0 deletions tests-system/nested-packages-basic/foo.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package foo

type Holder {}
5 changes: 5 additions & 0 deletions tests-system/nested-packages-basic/foo_bar.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package foo.bar

type Req {
title String
}
6 changes: 6 additions & 0 deletions tests-system/nested-packages-basic/mydata.trlc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package mydata
import foo.bar

foo.bar.Req REQ_001 {
title = "first requirement"
}
1 change: 1 addition & 0 deletions tests-system/nested-packages-basic/output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 2 models and 1 requirement file and found no issues
1 change: 1 addition & 0 deletions tests-system/nested-packages-basic/output.brief
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 2 models and 1 requirement file and found no issues
6 changes: 6 additions & 0 deletions tests-system/nested-packages-basic/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"REQ_001": {
"title": "first requirement"
}
}
Processed 2 models and 1 requirement file and found no issues
1 change: 1 addition & 0 deletions tests-system/nested-packages-basic/output.smtlib
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Processed 2 models and 1 requirement file and found no issues
3 changes: 3 additions & 0 deletions tests-system/nested-packages-basic/tracing
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
LRM.Nested_Package_Names
LRM.Parent_Package_Required
LRM.Qualified_Name
3 changes: 3 additions & 0 deletions tests-system/nested-packages-collision/FooBar.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package Foo.Bar

type Widget {}
3 changes: 3 additions & 0 deletions tests-system/nested-packages-collision/foo.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package foo

type Dummy {}
3 changes: 3 additions & 0 deletions tests-system/nested-packages-collision/foo_bar.rsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package foo.bar

type Item {}
3 changes: 3 additions & 0 deletions tests-system/nested-packages-collision/output
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package foo.bar
^^^ nested-packages-collision/foo_bar.rsl:1: error: foo.bar is too similar to Foo.Bar, declared at FooBar.rsl:1
Processed 3 models and 0 requirement files and found 1 error
Loading
Loading