-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLanguage.razor
More file actions
379 lines (330 loc) · 18.7 KB
/
Copy pathLanguage.razor
File metadata and controls
379 lines (330 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
@page "/language"
@inherits LocalizedComponentBase
<PageTitle>@(Fr ? "Guide du langage — Settex" : "Language guide — Settex")</PageTitle>
<h1>@(Fr ? "Guide du langage" : "Language guide")</h1>
<p class="lead">
@(Fr
? "Un tour de chaque construction du langage Settex. Chaque exemple est écrit dans la syntaxe exacte acceptée par le compilateur."
: "A tour of every construct in the Settex language. Each example is written in the exact syntax the compiler accepts.")
</p>
<div class="onpage-toc">
<span>@(Fr ? "Sur cette page" : "On this page")</span>
<a href="language#comments">@(Fr ? "Commentaires" : "Comments")</a>
<a href="language#settings">@(Fr ? "Réglages" : "Settings")</a>
<a href="language#nested">@(Fr ? "Objets imbriqués" : "Nested objects")</a>
<a href="language#maps">@(Fr ? "Maps nommées" : "Named maps")</a>
<a href="language#arrays">@(Fr ? "Tableaux" : "Arrays")</a>
<a href="language#objects">@(Fr ? "Objets littéraux" : "Object literals")</a>
<a href="language#dotpath">@(Fr ? "Chemins pointés" : "Dot paths")</a>
<a href="language#env">@(Fr ? "Overlays d'environnement" : "Environment overlays")</a>
<a href="language#merging">@(Fr ? "Fusion" : "Merging")</a>
<a href="language#includes">Includes</a>
<a href="language#let">@(Fr ? "Variables" : "Variables")</a>
<a href="language#expressions">Expressions</a>
<a href="language#interpolation">Interpolation</a>
<a href="language#conditionals">@(Fr ? "Conditionnelles" : "Conditionals")</a>
<a href="language#setifmissing">Set-if-missing</a>
<a href="language#for">@(Fr ? "Boucles for" : "For loops")</a>
<a href="language#types">@(Fr ? "Types de valeurs" : "Value types")</a>
</div>
<h2 id="comments">@(Fr ? "Commentaires" : "Comments")</h2>
<CodeBlock Code="@("# Hash-style comment\n// Double-slash comment")" />
<h2 id="settings">@(Fr ? "Bloc settings" : "Settings block")</h2>
<p>@(Fr ? "Le bloc " : "The top-level ")<code>settings</code>@(Fr ? " de premier niveau contient votre configuration de base." : " block holds your base configuration.")</p>
<CodeBlock Code="@Settings" />
<h2 id="nested">@(Fr ? "Objets imbriqués" : "Nested objects")</h2>
<p>@(Fr ? "Un bloc nommé devient une clé d'objet JSON. Imbriquez-les pour créer une hiérarchie." : "A named block becomes a JSON object key. Nest them for hierarchy.")</p>
<CodeBlock Code="@Nested" />
<h2 id="maps">@(Fr ? "Maps nommées" : "Named maps")</h2>
<p>@(Fr ? "Comme chaque bloc nommé est une clé, on construit une map d'objets nommés en imbriquant des blocs nommés :" : "Because each named block is a key, you build a map of named objects by nesting named blocks:")</p>
<CodeBlock Code="@Maps" />
<h2 id="arrays">@(Fr ? "Tableaux" : "Arrays")</h2>
<p>@(Fr ? "Valeurs séparées par des virgules. Un retour à la ligne sépare aussi les éléments, donc la virgule peut être omise d'une ligne à l'autre — mais deux éléments sur la même ligne en exigent une." : "Comma-separated values. A newline separates items too, so a comma may be omitted across lines — but two items on the same line still need one.")</p>
<CodeBlock Code="@Arrays" />
<h2 id="objects">@(Fr ? "Objets littéraux dans les tableaux" : "Object literals in arrays")</h2>
<p>
@(Fr
? "Préfixez chaque objet d'un tableau par un tag — un identifiant suivi d'un bloc. Le tag est une étiquette syntaxique obligatoire et n'apparaît pas dans le JSON. Un simple { … } sans tag n'est pas valide."
: "Prefix each object in an array with a tag — any identifier followed by a block. The tag is a required syntactic label and is not emitted in the JSON. A bare { … } without a tag is not valid.")
</p>
<CodeBlock Code="@Objects" />
<p>@(Fr ? "La même forme s'utilise comme valeur : " : "The same form works as a plain value: ")<code>Database = connection { Host = "localhost" }</code>@(Fr ? " produit " : " produces ")<code>"Database": { "Host": "localhost" }</code>@(Fr ? " (le tag connection est ignoré)." : " (the connection tag is discarded).")</p>
<h2 id="dotpath">@(Fr ? "Affectations par chemin pointé" : "Dot-path assignments")</h2>
<CodeBlock Code="@DotPath" />
<h2 id="env">@(Fr ? "Overlays d'environnement" : "Environment overlays")</h2>
<p>
@(Fr
? "Chaque bloc env prend un nom entre guillemets et contient un bloc settings interne. Il génère un fichier appsettings.{Nom}.json contenant, par défaut, uniquement ses surcharges (un delta) que .NET superpose à la base à l'exécution. Settex avertit si une clé n'est définie que dans certains environnements."
: "Each env block takes a quoted name and contains an inner settings block. It generates a matching appsettings.{Name}.json holding only its overrides (a delta) by default, which .NET layers onto the base at runtime. Settex warns when a key is set in only some environments.")
</p>
<CodeBlock Code="@Env" />
<h2 id="merging">@(Fr ? "Comportement de fusion" : "Merging behaviour")</h2>
@if (Fr)
{
<ul>
<li><strong>Objets</strong> : fusion profonde (propriétés combinées).</li>
<li><strong>Tableaux</strong> : remplacés entièrement (pas de fusion élément par élément).</li>
<li><strong>Primitives</strong> : remplacées.</li>
</ul>
}
else
{
<ul>
<li><strong>Objects</strong> merge deeply (properties combined).</li>
<li><strong>Arrays</strong> are replaced entirely (no element merge).</li>
<li><strong>Primitives</strong> are replaced.</li>
</ul>
}
<CodeBlock Code="@Merging" />
<div class="callout callout-note">
@(Fr
? "⚠️ Piège des tableaux (limite .NET). Les règles ci-dessus décrivent la fusion vers la config effective. À l'exécution, .NET charge appsettings.json et appsettings.{Env}.json en couches : il aplatit chaque fichier en paires clé/valeur et les superpose, au lieu de remplacer le tableau. Deux fuites en découlent, et Settex avertit à la compilation dans les deux cas. (1) Tableau plus court : base [\"a\",\"b\",\"c\"] + env [\"x\"] donne [\"x\",\"b\",\"c\"]. (2) Éléments objets : à un index partagé, les deux objets fusionnent champ par champ, donc tout champ défini par la base et omis par l'override survit — quelles que soient les longueurs. [{ \"Name\": \"a\", \"Port\": 1 }] surchargé par [{ \"Name\": \"b\" }] donne Port = 1 à l'exécution. Évitez-les en gardant le tableau au moins aussi long et en répétant chaque champ d'un élément objet, ou en ne définissant le tableau que par environnement (pas dans la base). Les clés de configuration .NET étant insensibles à la casse, ces contrôles les comparent de la même façon : une base Hosts et un env hosts sont une seule clé."
: "⚠️ Array-layering caveat (a .NET limitation). The rules above describe merging into the effective config. At runtime, .NET loads appsettings.json and appsettings.{Env}.json as layers: it flattens every file into key/value pairs and layers those, rather than replacing the array. Two leaks follow, and Settex warns at compile time about both. (1) Shorter array: base [\"a\",\"b\",\"c\"] + env [\"x\"] yields [\"x\",\"b\",\"c\"]. (2) Object elements: at a shared index the two objects merge field by field, so any field the base defines and the override omits survives — whatever the lengths. [{ \"Name\": \"a\", \"Port\": 1 }] overridden by [{ \"Name\": \"b\" }] yields Port = 1 at runtime. Avoid both by keeping the array at least as long and repeating every field of an object element, or by defining the array only per environment (not in the base). Because .NET configuration keys are case-insensitive, these checks compare them that way too: a base Hosts and an env hosts are one key.")
</div>
<h2 id="includes">@(Fr ? "Includes de fichiers" : "File includes")</h2>
<p>
@(Fr
? "Répartissez la configuration sur plusieurs fichiers avec include. Un fichier inclus peut apporter des variables let, des blocs settings et des blocs env — tous fusionnés dans l'ordre du document, si bien que le fichier incluant l'emporte en cas de conflit."
: "Split configuration across files with include. An included file can contribute let variables, settings blocks and env blocks — all deep-merged in document order, so the including file wins on conflicts.")
</p>
<CodeBlock Title="common.settex" Code="@IncludeCommon" />
<CodeBlock Title="appsettings.settex" Code="@IncludeMain" />
<div class="callout callout-note">
@(Fr
? "Une variable let ne peut être définie qu'une fois par portée — évitez de redéfinir la même variable globale à la fois dans un fichier inclus et dans le fichier incluant."
: "A let variable may only be defined once per scope — avoid redefining the same global variable in both an included file and the including file.")
</div>
<h2 id="let">@(Fr ? "Variables avec " : "Variables with ")<code>let</code></h2>
<CodeBlock Code="@Let" />
<p>
@(Fr
? "Portée : un let au niveau fichier est global ; un let dans un bloc env masque le global pour cet environnement ; un itérateur de boucle n'existe que dans le corps de la boucle."
: "Scoping: file-level let is global; a let inside an env block shadows the global for that environment; a loop iterator only exists in the loop body.")
</p>
<h2 id="expressions">Expressions</h2>
<CodeBlock Code="@Expressions" />
<p><strong>@(Fr ? "Opérateurs :" : "Operators:")</strong> @(Fr ? "arithmétiques" : "arithmetic") <code>+ - * /</code>, @(Fr ? "comparaison" : "comparison") <code>== != < <= > >=</code>, @(Fr ? "logiques" : "logical") <code>and or not</code>, @(Fr ? "coalescence de null" : "null-coalescing") <code>??</code>. @(Fr ? "Les parenthèses groupent les sous-expressions, et " : "Parentheses group sub-expressions, and ")<code>+</code>@(Fr ? " concatène dès qu'un opérande est une chaîne (les nombres et booléens sont convertis)." : " concatenates when either side is a string (coercing numbers and booleans).")</p>
<h2 id="interpolation">@(Fr ? "Interpolation de chaînes" : "String interpolation")</h2>
<CodeBlock Code="@Interpolation" />
<div class="callout callout-warn">
<code>${…}</code> @(Fr
? "est résolu à la compilation à partir des variables et expressions Settex — ce n'est pas un emplacement pour des variables d'environnement runtime. Injectez les secrets au déploiement via les fournisseurs de configuration .NET (variables d'environnement ou user-secrets)."
: "is resolved at compile time from Settex variables and expressions — it is not a placeholder for runtime environment variables. Inject secrets at deployment time through .NET's environment-variable or user-secrets providers instead.")
</div>
<p>
@(Fr
? "Pour écrire un ${ littéral — un template shell, une regex, un emplacement destiné à un autre outil — échappez-le avec $${ : "
: "To write a literal ${ — a shell template, a regex, a placeholder meant for another tool — escape it with $${ : ")
</p>
<CodeBlock Code="@EscapedInterpolation" />
<div class="callout callout-note">
@(Fr
? "Un $ littéral juste avant une interpolation. $$ n'est spécial que suivi d'une accolade, donc \"$${price}\" est lu comme l'échappement et produit le littéral ${price} — pas un $ suivi de la valeur de price. Il n'existe pas d'échappement pour cette combinaison : concaténez, \"$\" + \"${price}\". Le cas est étroit (un symbole monétaire collé à un emplacement) mais silencieux."
: "A literal $ directly before an interpolation. $$ is only special when a brace follows, so \"$${price}\" is read as the escape and produces the literal ${price} — not $ followed by the value of price. There is no escape for that combination: concatenate instead, \"$\" + \"${price}\". The case is narrow (a currency symbol against a placeholder) but silent.")
</div>
<h2 id="conditionals">@(Fr ? "Affectations conditionnelles" : "Conditional assignments")</h2>
<p>@(Fr ? "Affecte une valeur seulement si une condition booléenne est vraie. Écrivez le repli d'abord, la surcharge conditionnelle après." : "Assign a value only when a boolean condition holds. Write the fallback first, the conditional override after.")</p>
<CodeBlock Code="@Conditionals" />
<h2 id="setifmissing">Set-if-missing (<code>:=</code>)</h2>
<p>@(Fr ? "N'affecte une valeur que si le chemin n'est pas déjà défini — y compris en vérifiant les réglages de base depuis un overlay. Idéal pour des valeurs par défaut." : "Sets a value only if the path is not already defined — including checking the base settings from within an overlay. Great for defaults.")</p>
<CodeBlock Code="@SetIfMissing" />
<h2 id="for">@(Fr ? "Boucles for" : "For loops")</h2>
<p>@(Fr ? "Génèrent dynamiquement des éléments de tableau. Chaque itération produit exactement un objet tagué." : "Generate array elements dynamically. Each iteration produces exactly one tagged object.")</p>
<CodeBlock Code="@ForLoops" />
<h2 id="types">@(Fr ? "Types de valeurs" : "Value types")</h2>
@if (Fr)
{
<ul>
<li><strong>Chaînes</strong> — <code>"guillemets doubles uniquement"</code></li>
<li><strong>Nombres</strong> — <code>42</code>, <code>3.14</code>, <code>-10</code></li>
<li><strong>Booléens</strong> — <code>true</code>, <code>false</code></li>
<li><strong>Null</strong> — <code>null</code></li>
<li><strong>Tableaux</strong> — <code>[1, 2, 3]</code> ou multiligne</li>
<li><strong>Objets</strong> — blocs imbriqués, ou objets littéraux tagués (<code>tag { … }</code>)</li>
</ul>
}
else
{
<ul>
<li><strong>Strings</strong> — <code>"double quotes only"</code></li>
<li><strong>Numbers</strong> — <code>42</code>, <code>3.14</code>, <code>-10</code></li>
<li><strong>Booleans</strong> — <code>true</code>, <code>false</code></li>
<li><strong>Null</strong> — <code>null</code></li>
<li><strong>Arrays</strong> — <code>[1, 2, 3]</code> or multiline</li>
<li><strong>Objects</strong> — nested blocks, or tagged object literals (<code>tag { … }</code>)</li>
</ul>
}
@code {
private const string Settings = """
settings {
AppName = "MyApp"
Version = "1.0.0"
EnableFeatureX = true
}
""";
private const string Nested = """
settings {
Database {
Host = "localhost"
Port = 5432
Options {
Timeout = 30
Retry = true
}
}
}
""";
private const string Maps = """
settings {
Providers {
AzureAd {
ClientId = "abc-123"
Authority = "https://login.microsoftonline.com"
}
Google {
ClientId = "xyz-456"
}
}
}
""";
private const string Arrays = """
settings {
AllowedHosts = [
"localhost"
"*.example.com"
]
Ports = [8080, 8081, 8082]
}
""";
private const string Objects = """
settings {
Services = [
service { Name = "auth" Port = 5001 }
service { Name = "data" Port = 5002 }
]
}
""";
private const string DotPath = """
settings {
Logging.LogLevel.Default = "Information"
Logging.LogLevel.Microsoft = "Warning"
}
""";
private const string Env = """
settings {
ApiUrl = "https://api.example.com"
LogLevel = "Information"
}
env "Development" {
settings {
ApiUrl = "http://localhost:5000"
LogLevel = "Debug"
}
}
env "Production" {
settings {
LogLevel = "Warning"
}
}
""";
private const string Merging = """
settings {
Database { Host = "localhost" Port = 5432 }
Tags = ["dev", "test"]
}
env "Production" {
settings {
Database.Host = "prod-server" // only Host changes
Tags = ["prod"] // array replaced entirely
}
}
""";
private const string IncludeCommon = """
let host = "localhost"
let defaultPort = 8000
settings {
Server { Host = host Port = defaultPort }
}
""";
private const string IncludeMain = """
include "./common.settex"
settings {
ApplicationName = "MyApp"
Server { Port = 9090 } // overrides the included default
}
""";
private const string Let = """
let basePort = 8000
let host = "localhost"
settings {
BaseUrl = "http://${host}:${basePort}"
}
env "Development" {
let basePort = 5000 // shadows the global within this env
settings {
DevUrl = "http://${host}:${basePort}"
}
}
""";
private const string Expressions = """
let timeout = 30
let enabled = true
settings {
Total = (10 + 5) * 2 // 30 — parentheses group
ShouldCache = enabled and timeout > 10
LogLevel = null ?? "Information"
Label = "v" + 2 + ".0" // "v2.0" — + concatenates with a string
}
""";
private const string EscapedInterpolation = """
let port = 8080
settings {
HomePath = "$${HOME}/bin" // -> "${HOME}/bin", left alone
Mixed = "$${NOT_INTERP} and ${port}" // -> "${NOT_INTERP} and 8080"
}
""";
private const string Interpolation = """
let host = "localhost"
let port = 8000
settings {
BaseUrl = "https://${host}:${port}"
Message = "Port is ${port + 100}"
}
""";
private const string Conditionals = """
env "Development" {
let verbose = true
settings {
LogLevel = "Warning" // fallback, always assigned
LogLevel = "Debug" if verbose // overrides only when verbose is true
}
}
""";
private const string SetIfMissing = """
settings {
Server { Port := 8080 } // default
}
env "Production" {
settings {
Server { Port := 443 } // won't set — already defined in base
}
}
""";
private const string ForLoops = """
let services = [
svc { Name = "auth" Port = 8001 }
svc { Name = "api" Port = 8002 }
]
let host = "localhost"
settings {
Endpoints = [
for s in services {
item {
Name = s.Name
Url = "http://${host}:${s.Port}"
}
}
]
}
""";
}