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.treeautomatically 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.