77#include < cctype>
88#include < fstream>
99#include < functional>
10- #include < optional>
1110#include < set>
1211
1312namespace {
@@ -16,19 +15,6 @@ bool is_option(const std::string & arg) {
1615 return !arg.empty () && arg[0 ] == ' -' ;
1716}
1817
19- std::string trim (const std::string & value) {
20- const auto is_space = [](unsigned char c) { return std::isspace (c) != 0 ; };
21- size_t start = 0 ;
22- while (start < value.size () && is_space (value[start])) {
23- ++start;
24- }
25- size_t end = value.size ();
26- while (end > start && is_space (value[end - 1 ])) {
27- --end;
28- }
29- return value.substr (start, end - start);
30- }
31-
3218bool is_implicit_value (const std::vector<std::string> & args, size_t index) {
3319 return index + 1 < args.size () && !is_option (args[index + 1 ]);
3420}
@@ -91,29 +77,44 @@ void server_config_manager::ensure_loaded() {
9177 std::string contents ((std::istreambuf_iterator<char >(file)), std::istreambuf_iterator<char >());
9278
9379 static const auto parser = build_peg_parser ([](auto & p) {
94- const auto ws = p.space ();
95- const auto new_line = p.choice ({p.literal (" \r\n " ), p.literal (" \n " ), p.literal (" \r " )});
80+ // newline ::= "\r\n" / "\n" / "\r"
81+ auto newline = p.rule (" newline" , p.literal (" \r\n " ) | p.literal (" \n " ) | p.literal (" \r " ));
82+
83+ // ws ::= [ \t]*
84+ auto ws = p.rule (" ws" , p.chars (" [ \t ]" , 0 , -1 ));
85+
86+ // comment ::= [;#] (!newline .)*
87+ auto comment = p.rule (" comment" , p.chars (" [;#]" , 1 , 1 ) + p.zero_or_more (p.negate (newline) + p.any ()));
88+
89+ // eol ::= ws comment? (newline / EOF)
90+ auto eol = p.rule (" eol" , ws + p.optional (comment) + (newline | p.end ()));
9691
97- const auto section_name = p. tag ( " section-name " , p. until ( " ] " ));
98- const auto section_line = ws + " [ " + section_name + " ] " + p. until_one_of ({ " \r " , " \n " } );
92+ // ident ::= [a-zA-Z_] [a-zA-Z0-9_.-]*
93+ auto ident = p. rule ( " ident " , p. chars ( " [a-zA-Z_] " , 1 , 1 ) + p. chars ( " [a-zA-Z0-9_.-] " , 0 , - 1 ) );
9994
100- const auto key = p. tag ( " key " , p. until ( " = " ));
101- const auto value = p.tag ( " value " , p. until_one_of ({ " \r " , " \n " } ));
102- const auto key_value_line = ws + key + ws + " = " + ws + value ;
95+ // value ::= (!eol-start .)*
96+ auto eol_start = p.rule ( " eol-start " , ws + (p. chars ( " [;#] " , 1 , 1 ) | newline | p. end () ));
97+ auto value = p. rule ( " value " , p. zero_or_more (p. negate (eol_start) + p. any ())) ;
10398
104- const auto comment = p. choice ({p. literal ( " ; " ), p. literal ( " # " )}) + p. until_one_of ({ " \r " , " \n " });
105- const auto comment_line = ws + comment ;
99+ // header-line ::= "[" ws ident ws "]" eol
100+ auto header_line = p. rule ( " header-line " , " [ " + ws + p. tag ( " section-name " , p. chars ( " [^]] " )) + ws + " ] " + eol) ;
106101
107- const auto blank_line = ws + new_line;
102+ // kv-line ::= ident ws "=" ws value eol
103+ auto kv_line = p.rule (" kv-line" , p.tag (" key" , ident) + ws + " =" + ws + p.tag (" value" , value) + eol);
108104
109- const auto line = p.choice ({
110- section_line + new_line,
111- key_value_line + new_line,
112- comment_line + new_line,
113- blank_line,
114- });
105+ // comment-line ::= ws comment (newline / EOF)
106+ auto comment_line = p.rule (" comment-line" , ws + comment + (newline | p.end ()));
115107
116- return p.rule (" ini" , p.zero_or_more (line) + p.optional (ws) + p.end ());
108+ // blank-line ::= ws (newline / EOF)
109+ auto blank_line = p.rule (" blank-line" , ws + (newline | p.end ()));
110+
111+ // line ::= header-line / kv-line / comment-line / blank-line
112+ auto line = p.rule (" line" , header_line | kv_line | comment_line | blank_line);
113+
114+ // ini ::= line* EOF
115+ auto ini = p.rule (" ini" , p.zero_or_more (line) + p.end ());
116+
117+ return ini;
117118 });
118119
119120 common_peg_parse_context ctx (contents);
@@ -123,57 +124,33 @@ void server_config_manager::ensure_loaded() {
123124 }
124125
125126 std::map<std::string, std::map<std::string, std::string>> parsed;
126- std::string current_section;
127- std::optional<std::string> pending_key;
128-
129- const auto flush_pending = [&](const std::string & value) {
130- if (current_section.empty () || !pending_key) {
131- return ;
132- }
133-
134- const auto & key = *pending_key;
135- if (key.rfind (" LLAMA_ARG_" , 0 ) != 0 ) {
136- return ;
137- }
138127
139- parsed[ current_section][key] = value ;
140- } ;
128+ std::string current_section;
129+ std::string current_key ;
141130
142- ctx.ast .visit (result, [&](const common_peg_ast_node & node) {
131+ ctx.ast .visit (result, [&](const auto & node) {
143132 if (node.tag == " section-name" ) {
144- if (pending_key) {
145- flush_pending (" " );
146- pending_key.reset ();
147- }
148-
149- current_section = trim (std::string (node.text ));
150- return ;
151- }
152-
153- if (node.tag == " key" ) {
154- if (pending_key) {
155- flush_pending (" " );
156- }
157-
158- pending_key = trim (std::string (node.text ));
159- return ;
160- }
161-
162- if (node.tag == " value" ) {
163- if (!pending_key) {
133+ const std::string section = std::string (node.text );
134+ if (section.rfind (" LLAMA_ARG_" , 0 ) == 0 ) {
135+ current_section.clear ();
164136 return ;
165137 }
166138
167- flush_pending (trim (std::string (node.text )));
168- pending_key.reset ();
169- return ;
139+ current_section = section;
140+ parsed[current_section] = {};
141+ } else if (node.tag == " key" ) {
142+ const std::string key = std::string (node.text );
143+ if (key.rfind (" LLAMA_ARG_" , 0 ) == 0 ) {
144+ current_key = key;
145+ } else {
146+ current_key.clear ();
147+ }
148+ } else if (node.tag == " value" && !current_key.empty () && !current_section.empty ()) {
149+ parsed[current_section][current_key] = std::string (node.text );
150+ current_key.clear ();
170151 }
171152 });
172153
173- if (pending_key) {
174- flush_pending (" " );
175- }
176-
177154 data = std::move (parsed);
178155}
179156
0 commit comments