Addressing Section List Values without an Index
This is a special behavior in ELCL that applies only in configuration documents — not in parser APIs. If a name path refers to a section list, and the next segment in the path is a regular name rather than an index, the path automatically points to the last entry in the list.
In other words:
In a configuration file, when no index is given,
the next name in the path refers to a child value or section inside the last added entry of the section list.
Example: Single Section List
The configuration below defines a section list named server.connection
with two entries:
[main]
user: "example"
[server]
threads: 4
startup delay: 20 s
*[server.connection]
port: 8080
interface: "web"
*[server.connection]
port: 9000
interface: "api"
[server.backend.filter]
reject: "bad"
accept: "good"
The resulting value tree shows how the name path server.connection.port
resolves:
Path: server.connection.port
● (root) <== Document ( size=2 )
┠── [main] <== SectionWithNames ( size=1 )
┃ └── user <== Text ( "example" )
┗━━ [server] <== SectionWithNames ( size=4 )
┠── [backend] <== IntermediateSection ( )
┃ └── [filter] <== SectionWithNames ( size=2 )
┃ ├── accept <== Text ( "good" )
┃ └── reject <== Text ( "bad" )
┡━━ *[connection] <== SectionList ( size=2 )
│ ┠── [0] <== SectionWithNames ( size=2 )
│ ┃ ├── interface <== Text ( "web" )
│ ┃ └── port <== Integer ( 8080 )
│ ┗━━ [1] <== SectionWithNames ( size=2 )
│ ┠── interface <== Text ( "api" )
│ ┗━━ port <== Integer ( 9000 )
├── startup_delay <== TimeDelta ( Not supported )
└── threads <== Integer ( 4 )
Here, the path server.connection.port
points to the port
value of the last entry in the connection
list — the one that holds the value 9000
.
Why This Rule Exists
At first glance, this rule may seem unusual — especially when working with a single-level list. However, it becomes intuitive and very useful in nested list structures, such as when building hierarchies dynamically.
Example: Nested Section Lists
Here is a configuration with nested section lists. Each place
may optionally define a list of tree
sections:
---*[place]*---
name: "example01"
---*[place]*---
name: "example02"
*[place.tree]
fruit: "apple"
*[place.tree]
fruit: "pear"
---*[place]*---
name: "example03"
*[place.tree]
fruit: "cherry"
*[place.tree]
fruit: "apricot"
The configuration expands into the following value tree:
● (root) <== Document ( size=1 )
└── *[place] <== SectionList ( size=3 )
├── [0] <== SectionWithNames ( size=1 )
│ └── name <== Text ( "example01" )
├── [1] <== SectionWithNames ( size=2 )
│ ├── *[tree] <== SectionList ( size=2 )
│ │ ├── [0] <== SectionWithNames ( size=1 )
│ │ │ └── fruit <== Text ( "apple" )
│ │ └── [1] <== SectionWithNames ( size=1 )
│ │ └── fruit <== Text ( "pear" )
│ └── name <== Text ( "example02" )
└── [2] <== SectionWithNames ( size=2 )
├── *[tree] <== SectionList ( size=2 )
│ ├── [0] <== SectionWithNames ( size=1 )
│ │ └── fruit <== Text ( "cherry" )
│ └── [1] <== SectionWithNames ( size=1 )
│ └── fruit <== Text ( "apricot" )
└── name <== Text ( "example03" )
The tree shows how entries are added to the place
list and how tree
sections belong to the last added place
.
Let’s highlight the relevant part:
13---*[place]*---
14name: "example03"
15
16*[place.tree]
17fruit: "cherry"
Even though place
is a list, we didn’t provide an index in place.tree
. Therefore, the configuration implicitly attaches the tree
entry to the last place — in this case, the one with name example03
.
Note
In this example, the full path place.tree
is used for clarity. In real-world configurations, it’s typically better to use relative name paths like .tree
instead.
Clarifying Name Path Semantics
In configuration files, name paths like
place.tree
automatically refer to the last entry in a list when no index is given.In parser APIs, this automatic behavior may or may not apply. Instead, you get explicit control over which element in a list you want to access.