Core Language

All parsers must support the core language, which defines a minimal set of concepts, features, and value types. This chapter introduces these essential elements. Here, you’ll learn about fundamental topics such as file encoding, comments, sections, names, and values in a concise format.

File Encoding and Line Breaks

Let’s begin with the basics. All ELCL configuration files must be encoded in UTF-8. ELCL supports nearly all valid UTF-8 Unicode characters, except for certain control characters.

ELCL is a line-based configuration language, meaning that line breaks are syntactically significant. Line breaks terminate comments, separate statements, and influence overall structure. The following line-ending styles are supported, and can be mixed within the same document:

  • A single newline character (\n).

  • A Windows-style carriage return followed by a newline (\r and \n).

Document Structure

Every configuration document in ELCL is made up of a sequence of lines, separated by line breaks. Lines that are empty or contain only comments are ignored by the parser. Configuration data is organized into one or more sections, with each section containing any number of name-value pairs. Each pair assigns a value to a given name within its section, effectively configuring the settings for that section.

# A comment

[first_section]
Name1: "A text value"       # Comment
Name2: 1'000
last_name: 1                # Another comment.

[another_section.subsection]  # Comment
Name: "more keys"
../_images/intro_main_structure_simplified.svg

Comments

In ELCL, a comment begins with the # character and continues until the end of the line. Comments are ignored entirely during parsing and can contain any text, except for some control characters that are disallowed in ELCL documents.

# A comment at the start of the line.
    # A comment preceded by some spacing.
[section]   # A comment after a section.
Name: "Value"  # A comment after a name-value pair.

Comments can only appear outside of a value. For example, a # character within a text or code value does not initiate a comment.

Names

In ELCL, names are used to identify both sections and name-value pairs. Names are always case-insensitive, meaning variations like example, EXAMPLE, and eXaMpLe are treated as the same name.

Name Rules

Names in ELCL follow specific rules to ensure consistency and readability. They can include letters (az), digits (09), underscores (_), and spaces ( ). However, there are a few important constraints:

  1. Names must start with a letter: Names cannot begin with a digit or an underscore. For example, 100days or _example are invalid names.

  2. Spaces and underscores are interchangeable: Spaces in names are automatically converted to underscores. This means that Example Name is treated as example_name.

  3. No consecutive underscores: Names cannot contain consecutive underscores or multiple spaces in a row. This prevents names like this__name or this  name from being valid.

  4. No trailing underscores: Names must not end with an underscore. For instance, example_ is not considered valid.

These rules ensure that names in ELCL are consistent, readable, and unambiguous.

[ Example Section ]     # Interpreted as: example_section
DNS Host: "127.0.0.1"   # Interpreted as: dns_host

Sections

In ELCL, a section begins with a name enclosed in square brackets [ and ]. After the section name, you can define any number of name-value pairs. Empty sections—those without any name-value pairs—are also valid.

[section]
Name1: "Value"
Name2: "Value"

[empty_section]  # Empty sections are allowed.

A section must start at the beginning of a line, with no spacing before the opening square bracket [. However, spacing around the section name inside the brackets is allowed.

Additionally, sections can be visually separated using any number of minus - characters surrounding the section name. These characters are ignored by the parser but can help with visual organization in configuration files.

[   section1    ]   # Spacing around section names is allowed.
Name: "Value"

-----------------[ section2 ]--------------------  # Surrounding minus signs are allowed.
Name: "Value"

[ section3 ]-------------------------------------
Name: "Value"

-------------------------------------[ section4 ]
Name: "Value"

Subsections

In ELCL, subsections are created by joining two or more section names with a . character, forming a name path. This allows you to define nested configurations within a document’s structure.

[section.subsection.one]
Name: "Value"

[  section . subsection . another_one ]  # Spacing around the '.' is also allowed.

Absolute and Relative Sections

If a section name begins with a . character, it is considered a relative section. Otherwise, it is treated as an absolute section.

  • An absolute section starts from the document root. Each additional name separated by a dot represents a deeper level in the hierarchy.

  • A relative section refers to a subsection of the most recently defined absolute section. Each relative section resets the hierarchy relative to the last absolute section, rather than nesting further under previous subsections. An ELCL document must never begin with a relative section.

[root]             # => root (absolute)
[.section1.sub]    # => root.section1.sub (relative to root)
[.section2]        # => root.section2 (relative to root)
[.section3.x]      # => root.section3.x (relative to root)
[another.sub]      # => another.sub (absolute)
[.section1.sub]    # => another.sub.section1.sub (relative to another.sub)
[.section2]        # => another.sub.section2 (relative to another.sub)
[.section3.x]      # => another.sub.section3.x (relative to another.sub)

Name-Value Pairs

Name-value pairs in ELCL configure individual settings within a section. Each pair must begin with a name at the start of a line, followed by a value separator, which can be either a colon (:) or an equal sign (=). After the separator comes the actual value, which is assigned to the name.

[server]
dns name: "ecl.example.com"   # Assigns "ecl.example.com" to `server.dns_name`
port: 9080                    # Assigns 9080 to `server.port`

Spacing and Formatting

You are free to add spaces around the value separator for readability:

[server]
dns name      :    "ecl.example.com"    # Spacing around the name and value is allowed.
port          :                         # The value can begin on the next line,
    9080                                # but indentation is required.

If the value starts on the next line, it must be indented with at least one space or tab. Also, there should be no empty line between the name and the start of the value.

../_images/intro_section_name_value.svg

Values

A value must follow a name and the value separator. You can write the value either on the same line or start it on the following line. There are two key rules to remember:

  • A value must not start or continue at the beginning of a line.

  • There must be no empty line between the name and its value.

The example below shows two valid placements of values. The first value is placed on the same line as the name, while the second value begins on the next line. If you place the value on a new line, you must indent it with at least one space ( ) or tab (\t) character.

[value_placement]
same_line: 7000
next_line:
    7000

Core Value Types

The core language defines three primary value types, which are fundamental to configuring settings in ELCL:

  1. Integer: Represents whole numbers, such as 42 or -1000. Integers are commonly used for numeric settings like port numbers or limits.

  2. Boolean: Represents logical values. Common literals include true and false, but ELCL supports multiple synonyms, such as yes and no, or on and off. Booleans are ideal for toggling features on or off, such as enabling or disabling a module.

  3. Text: Represents strings of characters enclosed in quotes, such as "example text". Text values are used for settings that require descriptive information, like file paths or descriptions.

While these are the core value types, ELCL also supports additional types that extend its capabilities beyond the core language. These extended types will be discussed in detail in later sections.

Value Formats

Each value type supports various formats, offering flexibility in how values can be represented in configuration files. For example, an integer can be written as a decimal, hexadecimal, or even using digit grouping, depending on the allowed formats.

These formats provide multiple ways to represent the same value type, making ELCL adaptable to different scenarios and user preferences.

Integer Values

An integer value in ELCL represents a whole positive or negative number. It can be written in decimal, hexadecimal, or binary format. Consider the following examples:

[integer_values]
decimal     : -12'000'000
hexadecimal : 0xAC12'08CD
binary      : 0b10100100'00010101

Decimal Format

Numbers without any prefix are interpreted as decimal values. For example, 120 is a regular decimal number. Note that ELCL does not support an octal number format, so avoid leading zeros (e.g., 0120) to prevent confusion, as they might be misinterpreted as octal in other contexts.

[decimal_numbers]
value a: 0                # Zero value
value b: 12'000           # Positive decimal value with separators
value c: -987654321       # Negative decimal value

Hexadecimal Format

Numbers prefixed with 0x are treated as hexadecimal values. In this format, you can use both lower-case and upper-case letters af interchangeably, as ELCL is case-insensitive. Leading zeros are allowed, but the total number of digits must not exceed the parser’s limit, typically 16 digits.

[hexadecimal_numbers]
value a: 0x0                 # Zero value
value b: 0x2ee0              # Positive hexadecimal value
value c: -0x3ADE'68B1        # Negative hexadecimal value with separators

Binary Format

Numbers prefixed with 0b are interpreted as binary values, consisting only of the digits 0 and 1. Leading zeros are also permitted. Binary numbers are right-aligned, meaning the last digit always represents the least significant bit.

[binary_numbers]
value a: 0b0                   # Zero value
value b: 0b00101110'11100000   # Positive binary value with separators
#        ↓ Negative binary value with separators
value c: -0b111010'11011110'01101000'10110001

Negative and Positive Numbers

Any of these formats can represent negative numbers by prefixing the value with a minus sign (-). For example, -0x1A and -42 are valid negative hexadecimal and decimal values, respectively. Note that setting the most significant bit (the leftmost bit) in binary format results in a negative value when interpreted as a signed integer. Also, it is possible to explicitly prefix any number with a plus sign (+).

The four values in the following example represent the same negative integer using different formats:

[negative_numbers]
value a: -987'654'321                          # Decimal format
value b: -0x3ADE68B1                           # Hexadecimal format
value c: -0b111010'11011110'01101000'10110001  # Binary format
#        ↓ Long binary format (negative 64-bit value)
value d: 0b11111111'11111111'11111111'11111111'11000101'00100001'10010111'01001111

Digit Separators

To enhance readability, you can use a digit separator ' between digits in any number format. For instance, 12'345'678 or 0xAC12'08CD are valid uses. However, there are some restrictions:

  • Separators must be placed between digits only, not at the beginning or end of a number.

  • Consecutive separators (e.g., 12''345) are not allowed.

By following these rules, you can format numbers in a way that is both flexible and clear, making your configurations easier to read and understand.

Boolean Values

Boolean values represent two possible states, typically true or false. They are ideal for toggling features, enabling or disabling functionalities, or providing affirmative or negative responses to configuration options. To improve readability, ELCL offers several synonyms for “true” and “false” that can be used interchangeably.

ELCL is also case-insensitive, meaning there is no difference between true, True, TRUE, or even tRuE—all are interpreted as the same value.

[boolean_values]
ecl_is_nice: true        # True using lowercase
light: Off               # False using a synonym
motor: ENABLED           # True using uppercase synonym
stop_now: yes            # True using another synonym

Boolean Synonyms

The table below lists all the boolean literals recognized by ELCL. You can use any of these for a true or false value, based on your preference.

True

False

Yes

No

On

Off

Enabled

Disabled

Text Values

The third core value type in ELCL is text. Text values are enclosed in double quotes ("). They are used for storing strings of characters, which can include almost any Unicode character, making them ideal for representing descriptive or freeform information.

[text_values]
value a: ""                        # An empty text
value b: "Text can be anything"    # A basic text value
value c: "    Spaces    are    preserved   "  # Text with preserved spaces

The core language supports only single-line text values, as shown above. Multi-line text values are also available in ELCL and will be covered in a later section.

Text Content Restrictions

You can use almost any Unicode character in text values, except for the following:

To include these characters in your text values, you must use escape sequences.

Text Escape Sequences

Escape sequences allow you to include special characters in text values by using a backslash (\) followed by a specific sequence of characters. The following table provides an overview of all supported escape sequences in ELCL:

Sequence

Description

\\

Inserts a backslash (\)

\"

Inserts a double quote (")

\$

Inserts a dollar sign ($)

\n

Inserts a newline control code (\n)

\r

Inserts a carriage-return control code (\r)

\t

Inserts a tab character (\t)

\uXXXX

Inserts a Unicode character represented by the four-digit hexadecimal code XXXX. The code must be between 0001 and FFFF. Surrogate code points between D800 and DFFF are not allowed.

\u{YYYY}

Inserts a Unicode character represented by the hexadecimal code YYYY. This form allows for codes with one to eight digits, between 1 and 10FFFF.

The escape sequence \uXXXX requires a four-digit hexadecimal code, while \u{YYYY} provides more flexibility, allowing one to eight digits. This flexibility helps when you need to represent characters beyond the Basic Multilingual Plane or when a fixed digit length is inconvenient.

ELCL intentionally supports only a limited set of escape sequences to keep the language simple and easy to read. For instance, rarely used escape sequences like “form feed” or “vertical tab” are not included.

Note

The escape sequence for the dollar sign ($) is included in the core language to establish a foundation for a unified placeholder system or text interpolation. Although the specification includes a recommended placeholder syntax, support for text replacements during configuration parsing is an optional feature. Full-featured parsers must implement this functionality, allowing dynamic substitution of placeholder values as the configuration is processed.

All three text values in the example below are equivalent, despite being represented with different escape sequences:

[escape_sequences]
text a: \"ありがとう\"😄"  # Direct Unicode characters, except for the double quotes
text b: "\u{3c8}\u{22}\u3042\u308a\u304c\u3068\u3046\u{22}\u{1f604}"  # Lowercase escape sequences
text c: "\U{3C8}\U{22}\U3042\U308A\U304C\U3068\U3046\U{22}\U{1F604}"  # Uppercase escape sequences

Meta Values

Meta values are special values that are part of the configuration language itself and its parser, not the actual configuration data. These values have a name that is starting with the “at”-character @ and they must always be defined at the beginning of a document, in the document root.

The core language requires all meta values to be placed before the first section in the document, and like name-value pairs, each meta value cannot be defined more than once. There are also meta commands, like @include, which will be discussed later. Meta commands can be defined later in the document, between section blocks.

The core language supports the following three meta values:

@signature: "..."
@version: "1.0"
@features: "regex, timedelta"

It’s important to note that all meta values are completely optional.

The Version Value

The version meta value specifies the version of ELCL for which the configuration was written. This value takes a text with the version number in the format <major>.<minor>. Currently, ELCL is at version 1.0, which is the only valid value at this time.

@version: "1.0"

[first_section]
name: "value"

When a parser reads the version meta value, it must compare it to its own supported version of the language. For example, if a future version “1.2” of ELCL introduces new features, and a parser that only supports version “1.0” encounters @version: "1.2", it must stop parsing. This is to prevent the parser from misinterpreting the configuration due to unsupported language features.

This value also helps ensure backward compatibility. If a future version “2.0” introduces breaking changes, a parser reading @version: "1.0" can switch to a legacy mode to handle the older syntax correctly.

The Features Value

The features meta value specifies a list of features that the document requires. This value takes a space-separated list of feature identifiers as text. If a parser does not support a specified feature, it must stop parsing the configuration.

@features: "regex timedelta"

[first_section]
name: "value"

This meta value ensures that a configuration document is compatible with different parsers. If a parser encounters an unsupported feature, it can provide a clear error message, preventing confusion that may arise from encountering unexpected character sequences in the document.

The Signature Value

The signature value, if present, must appear in the first line of the document. This value is used to verify the integrity of the document. If a parser or application does not support signature verification, it must stop with an error when it encounters this value.

@signature: "..."

[first_section]
name: "value"

This meta value is part of the core language as a security measure. If a document has been signed, the signature must be validated to ensure the document’s integrity. If a parser cannot verify the signature, it should not ignore it. Instead, it should require the signature to be manually removed before processing the document. This prevents potentially unauthorized changes to the configuration from going unnoticed.

Parser Meta Values

Parser implementations are allowed to provide additional meta values, that must start with @parser_.... Like meta values of the ELCL, parser meta values must be optional.

Name Paths and Name Conflicts

ELCL is designed to keep configurations simple and easy to understand. To maintain this simplicity, each value can be defined only once. Overwriting existing values is not allowed, and ELCL does not support “inheritance” or “templates.” These features are often sources of unintended complexity and can lead to configuration errors, particularly in large and complex systems.

The rules are intentionally straightforward: every section and value is assigned a unique name path starting from the document root. Once a name path has been assigned through a written definition in the configuration, it cannot be redefined or overwritten later in the document.

Multiple Values with the Same Name

In a single configuration, two values cannot share the same name. Attempting to define a value more than once results in an error.

[block.one]      # Creates the value map "block.one"
name: "value a"  # Assigns the value "value a" to "block.one.name"
name: "value b"  # Error! "block.one.name" is already defined.

Multiple Sections with the Same Name

The same rule applies to sections. Two sections in the same configuration cannot share the same name.

[block.one]      # Creates the section "block.one"
name: "value a"

[block.one]      # Error! The section "block.one" is already defined.
name: "value b"

This rule also prevents sections from being “continued” later in the document.

[block one]
value a: 123
value b: 123
value c: 123

[another block]
# ... other definitions ...

[block one]      # Error! The section "block one" was already defined.
value d: 123
value e: 123
value f: 123

Conflicts between Value and Section Names

A section defines a unique instance with its name, behaving similarly to any other value. Consequently, a section cannot share the same name path with an existing value.

[block]          # Creates the section "block"
name: "value a"  # Assigns the value "value a" to "block.name"

[block.name]     # Error! A value already exists for "block.name".

Likewise, no value can share the same name path as a section.

[block.name]     # Creates the section "block.name"

[block]
name: "value b"  # Error! The name path "block.name" is already in use.

The order in which sections or values are defined does not matter—the conflict will occur regardless. The only difference is whether the error is raised for the value or the section, depending on the order of definition.

No Conflicts with Intermediate Sections

Intermediate sections are implicitly created when defining a name path. These sections are not considered “defined” until explicitly declared, allowing them to be defined later in the same document.

[one.two.three]  # Creates the intermediate sections "one" and "one.two".
value: 123

[one.two]        # This is allowed! Defines the section "one.two".
value: 123

[one]            # This is allowed! Defines the section "one".
value: 123

This exception from the rule is only valid for intermediate sections being defined, the name path for intermediate sections cannot be used by values later in the same document.

[one.two.three] # Creates the intermediate sections "one" and "one.two".

[one]
two: 123        # Error! The name "one.two" is already in use.

Core Language: Key Takeaways and Beyond

In this chapter, we explored the essential components of the ELCL core language. We discussed how to structure configuration documents using sections, subsections, and name-value pairs, and how to effectively utilize comments for clarity. We also covered the fundamental value types—integers, booleans, and text—along with their formats, rules, and best practices.

Mastering these core concepts is crucial for working with ELCL, as they provide the foundational building blocks for all configurations. Whether you are defining simple settings or creating more complex structures, the core language ensures consistency, readability, and flexibility in your configurations.

While the core language is sufficient for basic configurations and essential for lightweight micro-parsers on microcontrollers, real-world scenarios on desktop applications often require more than just the basics. This is where ELCL’s Standard Features come into play. These features significantly extend the capabilities of the core language, enabling more sophisticated configurations.

With additional value types like floating-point numbers, dates, times, and byte-data, as well as powerful tools such as multi-line text and code blocks, value and section lists, text names, and the “include” meta command, ELCL provides the flexibility needed for complex and robust configurations.

In the next chapter, we’ll explore these standard features in detail, learning how they can help you tackle advanced configuration challenges with ease. Let’s move forward and discover the possibilities that await with ELCL’s extended capabilities.