Skip to content

Commit 6a248a8

Browse files
committed
ldc2.conf: Implement ~= assignment operator
Similarly to the D counterpart, ~= is used for concatenating arrays. The new behavior of the config file can be summarized by: - go through each section (in the order that they appear in the file) - if the section matches the target triple go through each setting (in the order that they appear in the section) and carry out the assignments. `=` or `:` override the previous value, `~=` appends to it (in the case of arrays). This change can break some setups, mainly setups in which a section contained the same key multiple times. For example: ``` default: { switches = [ "A" ] switches = [ "B" ] } ``` Previously, the above config would pick the flag `A` but this has been changed to picking the flag `B`. Given that the above is unintuitive and users should have easily realized that some of their settings were being ignored, this can be a justified change. Signed-off-by: Andrei Horodniceanu <[email protected]> Signed-off-by: Andrei Horodniceanu <[email protected]>
1 parent 3726a93 commit 6a248a8

File tree

6 files changed

+145
-57
lines changed

6 files changed

+145
-57
lines changed

driver/config.d

+79-34
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,25 @@ class ScalarSetting : Setting
6666

6767
class ArraySetting : Setting
6868
{
69-
this(string name, string[] vals)
69+
this(string name, string[] vals, bool isAppending)
7070
{
7171
super(name, Type.array);
7272
_vals = vals;
73+
_isAppending = isAppending;
7374
}
7475

7576
@property const(string)[] vals() const
7677
{
7778
return _vals;
7879
}
7980

81+
@property bool isAppending() const
82+
{
83+
return _isAppending;
84+
}
85+
8086
private string[] _vals;
87+
private bool _isAppending;
8188
}
8289

8390
class GroupSetting : Setting
@@ -133,7 +140,7 @@ EBNF grammar.
133140
It is a subset of the libconfig grammar (http://www.hyperrealm.com/libconfig).
134141
135142
config = { ows , setting } , ows ;
136-
setting = (name | string) , (":" | "=") , value , [";" | ","] ;
143+
setting = (name | string) , (":" | "=" | "~=") , value , [";" | ","] ;
137144
name = alpha , { alpha | digit | "_" | "-" } ;
138145
value = string | array | group ;
139146
array = "[" , ows ,
@@ -172,6 +179,7 @@ enum Token
172179
{
173180
name,
174181
assign, // ':' or '='
182+
appendAssign, // '~='
175183
str,
176184
lbrace, // '{'
177185
rbrace, // '}'
@@ -187,17 +195,18 @@ string humanReadableToken(in Token tok)
187195
{
188196
final switch(tok)
189197
{
190-
case Token.name: return `"name"`;
191-
case Token.assign: return `':' or '='`;
192-
case Token.str: return `"string"`;
193-
case Token.lbrace: return `'{'`;
194-
case Token.rbrace: return `'}'`;
195-
case Token.lbracket: return `'['`;
196-
case Token.rbracket: return `']'`;
197-
case Token.semicolon: return `';'`;
198-
case Token.comma: return `','`;
199-
case Token.unknown: return `"unknown token"`;
200-
case Token.eof: return `"end of file"`;
198+
case Token.name: return `"name"`;
199+
case Token.assign: return `':' or '='`;
200+
case Token.appendAssign: return `'~='`;
201+
case Token.str: return `"string"`;
202+
case Token.lbrace: return `'{'`;
203+
case Token.rbrace: return `'}'`;
204+
case Token.lbracket: return `'['`;
205+
case Token.rbracket: return `']'`;
206+
case Token.semicolon: return `';'`;
207+
case Token.comma: return `','`;
208+
case Token.unknown: return `"unknown token"`;
209+
case Token.eof: return `"end of file"`;
201210
}
202211
}
203212

@@ -226,10 +235,15 @@ struct Parser
226235

227236
void error(in string msg)
228237
{
229-
enum fmt = "Error while reading config file: %.*s\nline %d: %.*s";
238+
error(msg, lineNum);
239+
}
240+
241+
void error(in string msg, int lineNum)
242+
{
243+
enum fmt = "line %d: %.*s";
230244
char[1024] buf;
231-
auto len = snprintf(buf.ptr, buf.length, fmt, cast(int) filename.length,
232-
filename.ptr, lineNum, cast(int) msg.length, msg.ptr);
245+
auto len = snprintf(buf.ptr, buf.length, fmt,
246+
lineNum, cast(int) msg.length, msg.ptr);
233247
throw new Exception(buf[0 .. len].idup);
234248
}
235249

@@ -275,6 +289,19 @@ struct Parser
275289
return getTok(outStr);
276290
}
277291

292+
if (lastChar == '~')
293+
{
294+
lastChar = getChar();
295+
if (lastChar != '=')
296+
{
297+
outStr = "~";
298+
return Token.unknown;
299+
}
300+
301+
lastChar = getChar();
302+
return Token.appendAssign;
303+
}
304+
278305
if (isalpha(lastChar))
279306
{
280307
string name;
@@ -410,17 +437,6 @@ struct Parser
410437
". Got " ~ humanReadableToken(tok) ~ s ~ " instead.");
411438
}
412439

413-
string accept(in Token expected)
414-
{
415-
string s;
416-
immutable tok = getTok(s);
417-
if (tok != expected)
418-
{
419-
unexpectedTokenError(tok, expected, s);
420-
}
421-
return s;
422-
}
423-
424440
Setting[] parseConfig()
425441
{
426442
Setting[] res;
@@ -450,11 +466,29 @@ struct Parser
450466
assert(false);
451467
}
452468

453-
accept(Token.assign);
469+
string s;
470+
t = getTok(s);
471+
if (t != Token.assign && t != Token.appendAssign)
472+
{
473+
auto msg = "Expected either"
474+
~ " token " ~ humanReadableToken(Token.assign)
475+
~ " or token " ~ humanReadableToken(Token.appendAssign)
476+
~ " but got: " ~ humanReadableToken(t)
477+
~ ' ' ~ (s.length ? '(' ~ s ~ ')' : s);
478+
error(msg);
479+
}
480+
// This is off by +1 if `t` is followed by \n
481+
const assignLineNum = lineNum;
454482

455-
Setting res = parseValue(name);
483+
Setting res = parseValue(name, t);
484+
if (t == Token.appendAssign)
485+
{
486+
if (res.type == Setting.Type.scalar)
487+
error(humanReadableToken(t) ~ " is not supported with scalar values", assignLineNum);
488+
if (res.type == Setting.Type.group)
489+
error(humanReadableToken(t) ~ " is not supported with groups", assignLineNum);
490+
}
456491

457-
string s;
458492
t = getTok(s);
459493
if (t != Token.semicolon && t != Token.comma)
460494
{
@@ -464,8 +498,10 @@ struct Parser
464498
return res;
465499
}
466500

467-
Setting parseValue(string name)
501+
Setting parseValue(string name, Token tAssign = Token.assign)
468502
{
503+
assert(tAssign == Token.assign || tAssign == Token.appendAssign);
504+
469505
string s;
470506
auto t = getTok(s);
471507
if (t == Token.str)
@@ -474,6 +510,7 @@ struct Parser
474510
}
475511
else if (t == Token.lbracket)
476512
{
513+
const isAppending = tAssign == Token.appendAssign;
477514
string[] arrVal;
478515
while (1)
479516
{
@@ -485,7 +522,7 @@ struct Parser
485522
arrVal ~= s;
486523
break;
487524
case Token.rbracket:
488-
return new ArraySetting(name, arrVal);
525+
return new ArraySetting(name, arrVal, isAppending);
489526
default:
490527
unexpectedTokenError(t, Token.str, s);
491528
assert(false);
@@ -498,7 +535,7 @@ struct Parser
498535
case Token.comma:
499536
break;
500537
case Token.rbracket:
501-
return new ArraySetting(name, arrVal);
538+
return new ArraySetting(name, arrVal, isAppending);
502539
default:
503540
unexpectedTokenError(t, Token.comma, s);
504541
assert(false);
@@ -578,6 +615,8 @@ group-1_2: {};
578615
scalar = "abc";
579616
// comment
580617
Array_1-2 = [ "a" ];
618+
619+
AppArray ~= [ "x" ]; // appending array
581620
};
582621
`;
583622

@@ -591,7 +630,7 @@ group-1_2: {};
591630
assert(settings[1].name == "86(_64)?-.*linux\\.?");
592631
assert(settings[1].type == Setting.Type.group);
593632
auto group2 = cast(GroupSetting) settings[1];
594-
assert(group2.children.length == 2);
633+
assert(group2.children.length == 3);
595634

596635
assert(group2.children[0].name == "scalar");
597636
assert(group2.children[0].type == Setting.Type.scalar);
@@ -600,4 +639,10 @@ group-1_2: {};
600639
assert(group2.children[1].name == "Array_1-2");
601640
assert(group2.children[1].type == Setting.Type.array);
602641
assert((cast(ArraySetting) group2.children[1]).vals == [ "a" ]);
642+
assert((cast(ArraySetting) group2.children[1]).isAppending == false);
643+
644+
assert(group2.children[2].name == "AppArray");
645+
assert(group2.children[2].type == Setting.Type.array);
646+
assert((cast(ArraySetting) group2.children[2]).vals == [ "x" ]);
647+
assert((cast(ArraySetting) group2.children[2]).isAppending == true);
603648
}

driver/configfile.d

+27-16
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,42 @@ string normalizeSlashes(const(char)* binDir)
3030
return cast(string)res; // assumeUnique
3131
}
3232

33-
T findSetting(T)(GroupSetting[] sections, Setting.Type type, string name)
33+
T findSetting(T, alias reducer)(GroupSetting[] sections, Setting.Type type, string name)
3434
{
35-
// lexically later sections dominate earlier ones
36-
foreach_reverse (section; sections)
35+
T result = null;
36+
foreach (section; sections)
3737
{
3838
foreach (c; section.children)
3939
{
4040
if (c.type == type && c.name == name)
41-
return cast(T) c;
41+
{
42+
if (result !is null)
43+
result = reducer(result, cast(T) c);
44+
else
45+
result = cast(T) c;
46+
}
4247
}
4348
}
44-
return null;
49+
return result;
4550
}
4651

4752
ArraySetting findArraySetting(GroupSetting[] sections, string name)
4853
{
49-
return findSetting!ArraySetting(sections, Setting.Type.array, name);
54+
auto merge(ArraySetting a, ArraySetting b)
55+
{
56+
string[] newVals;
57+
if (b.isAppending)
58+
newVals = [] ~ a.vals ~ b.vals;
59+
else
60+
newVals = [] ~ b.vals;
61+
return new ArraySetting(a.name, newVals, a.isAppending && b.isAppending);
62+
}
63+
return findSetting!(ArraySetting, merge)(sections, Setting.Type.array, name);
5064
}
5165

5266
ScalarSetting findScalarSetting(GroupSetting[] sections, string name)
5367
{
54-
return findSetting!ScalarSetting(sections, Setting.Type.scalar, name);
68+
return findSetting!(ScalarSetting, (o, n) => n)(sections, Setting.Type.scalar, name);
5569
}
5670

5771
string replace(string str, string pattern, string replacement)
@@ -137,8 +151,6 @@ private:
137151

138152
bool readConfig(const(char)* cfPath, const(char)* triple, const(char)* binDir)
139153
{
140-
switches.setDim(0);
141-
postSwitches.setDim(0);
142154
const cfgPaths = CfgPaths(cfPath, binDir);
143155

144156
try
@@ -156,24 +168,23 @@ private:
156168
if (sections.length == 0)
157169
{
158170
const dTriple = triple[0 .. strlen(triple)];
159-
const dCfPath = cfPath[0 .. strlen(cfPath)];
160171
throw new Exception("No matching section for triple '" ~ cast(string) dTriple
161-
~ "' in " ~ cast(string) dCfPath);
172+
~ "'");
162173
}
163174

164175
auto switches = findArraySetting(sections, "switches");
165176
auto postSwitches = findArraySetting(sections, "post-switches");
166177
if (!switches && !postSwitches)
167-
{
168-
const dCfPath = cfPath[0 .. strlen(cfPath)];
169-
throw new Exception("Could not look up switches in " ~ cast(string) dCfPath);
170-
}
178+
throw new Exception("Could not look up switches");
171179

172180
void applyArray(ref Array!(const(char)*) output, ArraySetting input)
173181
{
174182
if (!input)
175183
return;
176184

185+
if (!input.isAppending)
186+
output.setDim(0);
187+
177188
output.reserve(input.vals.length);
178189
foreach (sw; input.vals)
179190
{
@@ -195,7 +206,7 @@ private:
195206
}
196207
catch (Exception ex)
197208
{
198-
fprintf(stderr, "Error: %.*s\n", cast(int) ex.msg.length, ex.msg.ptr);
209+
fprintf(stderr, "Error while reading config file: %s\n%.*s\n", cfPath, cast(int) ex.msg.length, ex.msg.ptr);
199210
return false;
200211
}
201212
}

tests/driver/config_append_assign.d

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %ldc -o- -v -conf=%S/inputs/appending_assign.conf %s 2>&1
2+
3+
module object;
4+
5+
version(Section1_1)
6+
static assert(false);
7+
version(Section1_2) {}
8+
else static assert(false);
9+
10+
version(Section2) {}
11+
else static assert(false);

tests/driver/config_diag.d

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
// RUN: not %ldc -conf=%S/inputs/noswitches.conf %s 2>&1 | FileCheck %s --check-prefix=NOSWITCHES
2-
// NOSWITCHES: Could not look up switches in {{.*}}noswitches.conf
1+
// RUN: %ldc -o- -conf=%S/inputs/noswitches.conf %s 2>&1 | FileCheck %s --check-prefix=NOSWITCHES
2+
// NOSWITCHES: Error while reading config file: {{.*}}noswitches.conf
3+
// NOSWITCHES-NEXT: Could not look up switches
34

4-
// RUN: not %ldc -conf=%S/inputs/section_aaa.conf %s 2>&1 | FileCheck %s --check-prefix=NO_SEC
5-
// NO_SEC: No matching section for triple '{{.*}}' in {{.*}}section_aaa.conf
5+
// RUN: %ldc -o- -conf=%S/inputs/section_aaa.conf %s 2>&1 | FileCheck %s --check-prefix=NO_SEC
6+
// NO_SEC: Error while reading config file: {{.*}}section_aaa.conf
7+
// NO_SEC-NEXT: No matching section for triple '{{.*}}'
68

9+
// RUN: %ldc -o- -conf=%S/inputs/invalid_append.conf %s 2>&1 | FileCheck %s --check-prefix=APP
10+
// APP: Error while reading config file: {{.*}}invalid_append.conf
11+
// APP-NEXT: line 3: '~=' is not supported with scalar values
712

8-
void foo()
9-
{
10-
}
13+
module object;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
default: {
2+
switches = [ "-d-version=Section1_1" ]
3+
switches = [ "-d-version=Section1_2" ]
4+
}
5+
6+
".?": {
7+
switches ~= [ "-d-version=Section2" ]
8+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
default:
2+
{
3+
rpath ~= "/path";
4+
}
5+
6+
default:
7+
{
8+
switches = [];
9+
post-switches = [];
10+
}

0 commit comments

Comments
 (0)