humalis 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyse/accessibilite.js +13 -5
- package/dist/analyse/analyse.js +43 -3
- package/dist/analyse/langues.js +30 -10
- package/dist/analyse/medias.js +4 -1
- package/dist/analyse/navigation.js +35 -0
- package/dist/analyse/niveaux.js +33 -0
- package/dist/analyse/noms.js +1 -1
- package/dist/analyse/site.js +30 -8
- package/dist/cibles/commun/colorerCode.js +59 -0
- package/dist/cibles/commun/mouvement.js +110 -4
- package/dist/cibles/mobile/projet.js +7 -2
- package/dist/cibles/mobile/rendu.js +6 -3
- package/dist/cibles/statique/page.js +99 -6
- package/dist/cibles/statique/projet.js +31 -0
- package/dist/cibles/statique/valeurs.js +5 -1
- package/dist/cibles/web/css-ecran.js +22 -1
- package/dist/cibles/web/ecran.js +161 -16
- package/dist/cibles/web/interactions.js +50 -3
- package/dist/cibles/web/projet.js +29 -2
- package/dist/cibles/web/theme.js +11 -2
- package/dist/cibles/web/valeurs.js +17 -0
- package/dist/i18n/messages.js +2 -0
- package/dist/lexer/lexer.js +106 -3
- package/dist/navigateur.js +5274 -0
- package/dist/parser/interactions.js +6 -2
- package/dist/parser/langues.js +1 -1
- package/dist/parser/medias.js +6 -1
- package/dist/parser/parser.js +85 -3
- package/dist/parser/theme.js +10 -1
- package/dist/ri/construire.js +24 -1
- package/dist/vocabulaire/table-interactions.js +16 -1
- package/dist/vocabulaire/table.js +4 -4
- package/docs/guide.en.md +170 -2
- package/docs/guide.md +171 -1
- package/llms.txt +14 -1
- package/package.json +4 -2
package/dist/lexer/lexer.js
CHANGED
|
@@ -30,6 +30,23 @@ function motColle(ligne, debut, premier) {
|
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Le guillemet qui ferme un {code "…"} ou un {accent "…"} commencé à `debut` : le premier « "} »
|
|
35
|
+
* où les accolades du contenu sont équilibrées ({code "{accent "…"}"}), sinon le premier « "} »
|
|
36
|
+
* ({code "{"}). -1 s'il n'y en a pas.
|
|
37
|
+
*/
|
|
38
|
+
function finDuMorceau(texte, debut) {
|
|
39
|
+
let profondeur = 0;
|
|
40
|
+
for (let j = debut; j < texte.length; j++) {
|
|
41
|
+
if (texte.startsWith('"}', j) && profondeur <= 0)
|
|
42
|
+
return j;
|
|
43
|
+
if (texte[j] === '{')
|
|
44
|
+
profondeur++;
|
|
45
|
+
if (texte[j] === '}')
|
|
46
|
+
profondeur--;
|
|
47
|
+
}
|
|
48
|
+
return texte.indexOf('"}', debut);
|
|
49
|
+
}
|
|
33
50
|
function decouperChaine(valeur, ligne, colonne) {
|
|
34
51
|
const parties = [];
|
|
35
52
|
let tampon = '';
|
|
@@ -42,6 +59,13 @@ function decouperChaine(valeur, ligne, colonne) {
|
|
|
42
59
|
}
|
|
43
60
|
i++;
|
|
44
61
|
let nomVariable = '';
|
|
62
|
+
// {code "…"} et {accent "…"} : le morceau va jusqu'au premier « "} ».
|
|
63
|
+
const morceau = /^(code|accent)\s*"/.exec(valeur.slice(i));
|
|
64
|
+
const finMorceau = morceau ? finDuMorceau(valeur, i + morceau[0].length) : -1;
|
|
65
|
+
if (finMorceau >= 0) {
|
|
66
|
+
nomVariable = valeur.slice(i, finMorceau + 1);
|
|
67
|
+
i = finMorceau + 1;
|
|
68
|
+
}
|
|
45
69
|
while (i < valeur.length && valeur[i] !== '}') {
|
|
46
70
|
nomVariable += valeur[i];
|
|
47
71
|
i++;
|
|
@@ -50,7 +74,21 @@ function decouperChaine(valeur, ligne, colonne) {
|
|
|
50
74
|
throw new ErreurLexicale('accolade non fermée dans la chaîne : il manque }', ligne, colonne);
|
|
51
75
|
}
|
|
52
76
|
i++;
|
|
53
|
-
|
|
77
|
+
const accent = /^accent\b(.*)$/s.exec(nomVariable.trim());
|
|
78
|
+
if (accent) {
|
|
79
|
+
const contenu = /^\s*"([\s\S]*)"\s*$/.exec(accent[1]);
|
|
80
|
+
if (!contenu)
|
|
81
|
+
throw new ErreurLexicale('accent attend un texte entre guillemets : {accent "mot"}.', ligne, colonne);
|
|
82
|
+
parties.push({ type: 'accent', valeur: contenu[1] });
|
|
83
|
+
}
|
|
84
|
+
else if (/^code\s+\S/s.test(nomVariable.trim())) {
|
|
85
|
+
const contenu = /^code\s+"([\s\S]*)"\s*$/.exec(nomVariable.trim());
|
|
86
|
+
if (!contenu)
|
|
87
|
+
throw new ErreurLexicale('code attend un texte entre guillemets : {code "titre"}.', ligne, colonne);
|
|
88
|
+
parties.push({ type: 'code', valeur: contenu[1] });
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
parties.push({ type: 'variable', valeur: nomVariable.trim() });
|
|
54
92
|
continue;
|
|
55
93
|
}
|
|
56
94
|
tampon += valeur[i];
|
|
@@ -68,10 +106,52 @@ export function analyser(source) {
|
|
|
68
106
|
for (let numLigne = 0; numLigne < lignes.length; numLigne++) {
|
|
69
107
|
const ligne = lignes[numLigne];
|
|
70
108
|
let i = 0;
|
|
109
|
+
// « code humalis : », « code : », et l'animation : « code humalis avec animation frappe au defilement : ».
|
|
110
|
+
const codeBrut = /^( *)(code)(?: +(?!avec\b)([\p{L}][\p{L}0-9_]*))?( +avec +animation +frappe(?: +au +d[eé]filement)?)? *: *$/u.exec(ligne);
|
|
111
|
+
if (codeBrut) {
|
|
112
|
+
const contenu = [];
|
|
113
|
+
const indentation = codeBrut[1].length;
|
|
114
|
+
if (indentation % 4 !== 0)
|
|
115
|
+
throw new ErreurLexicale('indentation incohérente : un multiple de 4 est attendu', numLigne + 1, 1);
|
|
116
|
+
const niveau = indentation / 4;
|
|
117
|
+
const courant = pile[pile.length - 1];
|
|
118
|
+
if (niveau > courant + 1)
|
|
119
|
+
throw new ErreurLexicale('indentation incohérente : un seul niveau à la fois', numLigne + 1, 1);
|
|
120
|
+
if (niveau === courant + 1) {
|
|
121
|
+
pile.push(niveau);
|
|
122
|
+
tokens.push({ type: 'INDENT', valeur: '', ligne: numLigne + 1, colonne: 1 });
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
while (pile.length > 1 && pile[pile.length - 1] > niveau) {
|
|
126
|
+
pile.pop();
|
|
127
|
+
tokens.push({ type: 'DEDENT', valeur: '', ligne: numLigne + 1, colonne: 1 });
|
|
128
|
+
}
|
|
129
|
+
if (pile[pile.length - 1] !== niveau)
|
|
130
|
+
throw new ErreurLexicale('indentation incohérente : ce niveau ne correspond à aucun bloc ouvert', numLigne + 1, 1);
|
|
131
|
+
while (numLigne + 1 < lignes.length) {
|
|
132
|
+
const suivante = lignes[numLigne + 1];
|
|
133
|
+
const marge = suivante.length - suivante.trimStart().length;
|
|
134
|
+
if (suivante.trim() !== '' && marge <= indentation)
|
|
135
|
+
break;
|
|
136
|
+
contenu.push(lignes[++numLigne]);
|
|
137
|
+
}
|
|
138
|
+
while (contenu.length && contenu[contenu.length - 1].trim() === '')
|
|
139
|
+
contenu.pop();
|
|
140
|
+
const marge = Math.min(...contenu.filter((l) => l.trim() !== '').map((l) => l.length - l.trimStart().length));
|
|
141
|
+
const brut = contenu.map((l) => l.slice(Math.min(marge, l.length - l.trimStart().length))).join('\n');
|
|
142
|
+
const ligneDebut = numLigne - contenu.length + 1;
|
|
143
|
+
tokens.push({ type: 'MOT_CLE', valeur: 'code', ligne: ligneDebut, colonne: indentation + 1, reserve: false, original: 'code' }, ...(codeBrut[3] ? [{ type: 'IDENTIFIANT', valeur: codeBrut[3], ligne: ligneDebut, colonne: indentation + 6 }] : []), ...(codeBrut[4] ? codeBrut[4].trim().split(/ +/).map((mot) => ({ type: 'IDENTIFIANT', valeur: mot, ligne: ligneDebut, colonne: ligne.indexOf(codeBrut[4]) + 1 })) : []), { type: 'PONCTUATION', valeur: ':', ligne: ligneDebut, colonne: ligne.indexOf(':') + 1 }, { type: 'FIN_LIGNE', valeur: '', ligne: ligneDebut, colonne: ligne.length + 1 }, { type: 'CODE_BRUT', valeur: brut, ligne: ligneDebut + 1, colonne: 1 }, { type: 'FIN_LIGNE', valeur: '', ligne: numLigne + 1, colonne: 1 });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
71
146
|
// « style : » au début d'une ligne, hors de tout bloc : les lignes
|
|
72
147
|
// indentées qui suivent sont du CSS, recopié tel quel. Il contient des
|
|
73
148
|
// {, des : et des # qui ne doivent pas être lus comme du Humalis.
|
|
74
|
-
if (
|
|
149
|
+
if (/^style\s*:\s*$/i.test(ligne)) {
|
|
150
|
+
// Juste après un bloc de haut de fichier (« theme : »), ce bloc se ferme ici.
|
|
151
|
+
while (pile.length > 1) {
|
|
152
|
+
pile.pop();
|
|
153
|
+
tokens.push({ type: 'DEDENT', valeur: '', ligne: numLigne + 1, colonne: 1 });
|
|
154
|
+
}
|
|
75
155
|
const debut = numLigne;
|
|
76
156
|
const contenu = [];
|
|
77
157
|
while (numLigne + 1 < lignes.length && (lignes[numLigne + 1].trim() === '' || /^\s/.test(lignes[numLigne + 1]))) {
|
|
@@ -141,7 +221,30 @@ export function analyser(source) {
|
|
|
141
221
|
const debut = i;
|
|
142
222
|
i++;
|
|
143
223
|
let valeur = '';
|
|
144
|
-
|
|
224
|
+
let profondeur = 0;
|
|
225
|
+
while (i < ligne.length && (ligne[i] !== '"' || (profondeur > 0 && ligne.indexOf('}', i + 1) >= 0))) {
|
|
226
|
+
// {code "…"} et {accent "…"} : recopiés tels quels jusqu'au premier « "} »
|
|
227
|
+
// (accolades, guillemets et antislashs compris).
|
|
228
|
+
const morceau = /^\{(code|accent)\s*"/.exec(ligne.slice(i));
|
|
229
|
+
const fin = morceau ? finDuMorceau(ligne, i + morceau[0].length) : -1;
|
|
230
|
+
if (fin >= 0) {
|
|
231
|
+
valeur += ligne.slice(i, fin + 2);
|
|
232
|
+
i = fin + 2;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
// \" écrit un guillemet dans le texte ; tout autre antislash reste tel quel
|
|
236
|
+
// (« \\ » aussi, en entier : un texte peut finir par lui, comme avant).
|
|
237
|
+
if (ligne[i] === '\\' && ligne[i + 1] === '\\') {
|
|
238
|
+
valeur += '\\\\';
|
|
239
|
+
i += 2;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (ligne[i] === '\\' && ligne[i + 1] === '"')
|
|
243
|
+
i++;
|
|
244
|
+
if (ligne[i] === '{')
|
|
245
|
+
profondeur++;
|
|
246
|
+
if (ligne[i] === '}' && profondeur > 0)
|
|
247
|
+
profondeur--;
|
|
145
248
|
valeur += ligne[i];
|
|
146
249
|
i++;
|
|
147
250
|
}
|