грамматика для файла .gitmodules

Как написать рекурсивный парсер?, Разработка на C# под linux
https://www.kernel.org/pub/software/scm/git/docs/git-config.html - определение формата

Syntax

The syntax is fairly flexible and permissive; whitespaces are mostly ignored. The # and ; characters begin comments to the end of line, blank lines are ignored.

The file consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next section begins. Section names are case-insensitive. Only alphanumeric characters, - and . are allowed in section names. Each variable must belong to some section, which means that there must be a section header before the first setting of a variable.

Sections can be further divided into subsections. To begin a subsection put its name in double quotes, separated by space from the section name, in the section header, like in the example below:

        [section "subsection"]

Subsection names are case sensitive and can contain any characters except newline (doublequote " and backslash can be included by escaping them as \" and \\, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have [section] if you have [section "subsection"], but you don’t need to.

There is also a deprecated [section.subsection] syntax. With this syntax, the subsection name is converted to lower-case and is also compared case sensitively. These subsection names follow the same restrictions as section names.

All the other lines (and the remainder of the line after the section header) are recognized as setting variables, in the form name = value (or just name, which is a short-hand to say that the variable is the boolean "true"). The variable names are case-insensitive, allow only alphanumeric characters and -, and must start with an alphabetic character.

A line that defines a value can be continued to the next line by ending it with a \; the backquote and the end-of-line are stripped. Leading whitespaces after name =, the remainder of the line after the first comment character # or ;, and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim.

Inside double quotes, double quote " and backslash \ characters must be escaped: use \" for " and \\ for \.

The following escape sequences (beside \" and \\) are recognized: \n for newline character (NL), \t for horizontal tabulation (HT, TAB) and \b for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid.

Includes

You can include one config file from another by setting the special include.path variable to the name of the file to be included. The included file is expanded immediately, as if its contents had been found at the location of the include directive. If the value of the include.path variable is a relative path, the path is considered to be relative to the configuration file in which the include directive was found. The value of include.path is subject to tilde expansion: ~/ is expanded to the value of $HOME, and ~user/ to the specified user’s home directory. See below for examples.

Моя попытка написать BNF

разделитель-строк : '\n\r' | '\n' | '\r' ;

Пробелов может быть несколько штук подряд
пробелы : ( ' ' | '\t' ) + ;

обобщенный-разделитель-строк : ( пробелы ? разделитель-строк пробелы ? ) + ;

здесь мы можем обнаружить недопустимые символы
символ : допустимый-символ | недопустимый-символ;

допустимый-символ : буква-русская | буква-английская | цифра | знаки-пунктуации | пробелы ;

знаки-пунктуации : '[' | ']' | '"' | '-' | '.' | ';' | '#';

в строках могут располагаться опциональные комментарии, текст комментария - это вполне себе семантическая конструкция, которую тоже можно попарсить (например в C# комментариях)

символ-комментария : '#' | ';' ;

комментарий : символ-комментария текст-комментария * ;

Текст комментария - это любой символ кроме разделителя строк
текст-комментария : . ;

разделитель : пробелы | обобщенный-разделитель-строк | комментарий ;

текстовая-строка : '"' ( допустимый-символ | пробелы | специальная-последовательность ) * '"' ;

специальная-последовательность : '\"' | '\\' | '\n' | '\t' | '\b' ;

хитрый-перенос : '\' разделитель строк ;

Файл состоит из секций, секции состоят из переменных:
полезное-содержимое : (секция разделитель*) * ;

У секций есть заголовки:
секция : заголовок-секции разделитель* тело-секции ;

У секций могут быть подсекции
тело-секции : (переменная разделитель*) * | (подсекция разделитель*) *;

не если секция не содержит переменных, а содержит только подсекции, то заголовок ей не нужен:
секция : (подсекция разделитель*) * ;

Заголовки заключены в квадратные скобки:
заголовок-секции : '[' пробелы* название-секции пробелы* ']' ;

Название секции состоит из букв, минусов и точек:
название-секции : (буква | '-' | '.' ) + ;

Имена секций не зависят от регистра:
буква : буква-английская-большая | буква--английская-маленькая;

буква-английская-большая : [A-Z] ;

буква-английская-маленькая : [a-z] ;

У подсекций не может быть своих подсекций:
подсекция : заголовок-подсекции разделитель* тело-подсекции ;

заголовок-секции : '[' пробелы* название-секции пробелы* '"' заголовок-подсекции '"' пробелы* ']' ;

тело-подсекции : (переменная разделитель*) * ;

переменная : имя-переменной ( разделитель* '=' разделитель* значение-переменной ) ?
|| include.path разделитель* '=' разделитель* путь-до-файла
;


заголовок-подсекции : ( '\"' | '\\' | допустимый-символ | пробелы ) + ;

имя-переменной : буква-английская ( буква-английская | '-' ) * ;

значение-переменной : строка-переносами-в-кавычках | строка-переносами-без-кавычек ;

строка-с-переносами-в-кавычках : '"' ( допустимый-символ | пробелы | специальная-последовательность | хитрый-перенос ) * '"' ;

строка-переносами-без-кавычек : ( ( допустимый-символ | специальная-последовательность ) * пробелы | хитрый-перенос ) * ( допустимый-символ | специальная-последовательность ) разделитель ?