lazarus-lang 1.0.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ladji Doucaré
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: lazarus-lang
3
+ Version: 1.0.0
4
+ Summary: LAZARUS — le langage de programmation de Ladji : accolades comme Java, simplicité de Python, mots-clés uniques (laz, fonk, kan, tanke...)
5
+ Author: Ladji Doucaré
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ladjidoucare994-boop/lazarus
8
+ Project-URL: Documentation, https://github.com/ladjidoucare994-boop/lazarus/blob/main/GUIDE_LAZARUS.md
9
+ Project-URL: Playground, https://ladjidoucare994-boop.github.io/lazarus/
10
+ Keywords: langage,programmation,interpreteur,lazarus,education,francais
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Software Development :: Interpreters
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Natural Language :: French
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Dynamic: license-file
21
+
22
+ # ⚡ LAZARUS
23
+
24
+ **Le langage de programmation créé par Ladji** — la structure de Java, la simplicité de Python, et des mots-clés uniques au monde.
25
+
26
+ *A programming language created by Ladji — Java's structure, Python's simplicity, and one-of-a-kind keywords. French-friendly error messages. See the [English section](#-english) below.*
27
+
28
+ ```lazarus
29
+ laz nom = "monde"
30
+
31
+ fonk saluer(qui) {
32
+ rend "Bonjour " + qui + " !"
33
+ }
34
+
35
+ pou i dan 1..3 {
36
+ vox(saluer(nom))
37
+ }
38
+ ```
39
+
40
+ ## 🚀 Essayer tout de suite
41
+
42
+ **Dans le navigateur, sans rien installer :** ouvrez le playground en ligne (dossier [`docs/`](docs/index.html) — hébergé avec GitHub Pages).
43
+
44
+ **Sur votre machine :**
45
+
46
+ ```bash
47
+ pip install lazarus-lang # puis :
48
+ lazarus mon_programme.laz # exécuter un fichier
49
+ lazarus # mode interactif
50
+ ```
51
+
52
+ Ou sans pip, avec juste Python 3 :
53
+
54
+ ```bash
55
+ python3 lazarus.py exemples/demo.laz
56
+ ```
57
+
58
+ ## 📖 Le langage en 30 secondes
59
+
60
+ | LAZARUS | Signification |
61
+ |---|---|
62
+ | `laz x = 5` | déclarer une variable |
63
+ | `vox("salut")` | afficher |
64
+ | `demand("Ton nom ? ")` | saisie clavier |
65
+ | `kan ... { } sinon { }` | si / sinon |
66
+ | `tanke ... { }` | tant que |
67
+ | `pou i dan 1..10 { }` | boucle pour |
68
+ | `fonk f(x) { rend x }` | fonction + retour |
69
+ | `kase` / `swiv` | break / continue |
70
+ | `vrai` / `faux` / `walu` | true / false / null |
71
+ | `et` / `ou` / `non` | and / or / not |
72
+
73
+ - Accolades `{ }` pour les blocs, **pas** de point-virgules
74
+ - Listes : `[1, 2, 3]`, intervalle : `1..10`
75
+ - 14 fonctions intégrées : `vox`, `demand`, `nombre`, `texte`, `taille`, `ajoute`, `retire`, `hasard`, `arondi`, `majus`, `minus`, `koupe`, `tri`, `tip`
76
+ - Messages d'erreur **en français**, clairs et pédagogiques
77
+
78
+ 📚 **[Guide complet du langage → GUIDE_LAZARUS.md](GUIDE_LAZARUS.md)**
79
+
80
+ ## 📂 Contenu du dépôt
81
+
82
+ ```
83
+ lazarus.py L'interpréteur officiel (Python, zéro dépendance)
84
+ GUIDE_LAZARUS.md Le manuel complet du langage
85
+ exemples/ Programmes d'exemple (.laz)
86
+ docs/index.html Le playground web (moteur JavaScript complet)
87
+ pyproject.toml Package pip « lazarus-lang »
88
+ ```
89
+
90
+ ## 🌍 English
91
+
92
+ LAZARUS is a programming language with invented keywords (`laz` = let, `fonk` = function, `kan` = if, `tanke` = while, `pou...dan` = for...in, `vox` = print). Curly braces like Java, no semicolons like Python. It ships with a zero-dependency Python interpreter, a full in-browser playground (pure JavaScript), and beginner-friendly error messages in French. Try it: `pip install lazarus-lang`, then `lazarus examples/demo.laz`.
93
+
94
+ ## Licence
95
+
96
+ MIT — libre et gratuit pour tout le monde, pour toujours.
97
+
98
+ *Créé avec ❤️ par Ladji Doucaré, 2026.*
@@ -0,0 +1,77 @@
1
+ # ⚡ LAZARUS
2
+
3
+ **Le langage de programmation créé par Ladji** — la structure de Java, la simplicité de Python, et des mots-clés uniques au monde.
4
+
5
+ *A programming language created by Ladji — Java's structure, Python's simplicity, and one-of-a-kind keywords. French-friendly error messages. See the [English section](#-english) below.*
6
+
7
+ ```lazarus
8
+ laz nom = "monde"
9
+
10
+ fonk saluer(qui) {
11
+ rend "Bonjour " + qui + " !"
12
+ }
13
+
14
+ pou i dan 1..3 {
15
+ vox(saluer(nom))
16
+ }
17
+ ```
18
+
19
+ ## 🚀 Essayer tout de suite
20
+
21
+ **Dans le navigateur, sans rien installer :** ouvrez le playground en ligne (dossier [`docs/`](docs/index.html) — hébergé avec GitHub Pages).
22
+
23
+ **Sur votre machine :**
24
+
25
+ ```bash
26
+ pip install lazarus-lang # puis :
27
+ lazarus mon_programme.laz # exécuter un fichier
28
+ lazarus # mode interactif
29
+ ```
30
+
31
+ Ou sans pip, avec juste Python 3 :
32
+
33
+ ```bash
34
+ python3 lazarus.py exemples/demo.laz
35
+ ```
36
+
37
+ ## 📖 Le langage en 30 secondes
38
+
39
+ | LAZARUS | Signification |
40
+ |---|---|
41
+ | `laz x = 5` | déclarer une variable |
42
+ | `vox("salut")` | afficher |
43
+ | `demand("Ton nom ? ")` | saisie clavier |
44
+ | `kan ... { } sinon { }` | si / sinon |
45
+ | `tanke ... { }` | tant que |
46
+ | `pou i dan 1..10 { }` | boucle pour |
47
+ | `fonk f(x) { rend x }` | fonction + retour |
48
+ | `kase` / `swiv` | break / continue |
49
+ | `vrai` / `faux` / `walu` | true / false / null |
50
+ | `et` / `ou` / `non` | and / or / not |
51
+
52
+ - Accolades `{ }` pour les blocs, **pas** de point-virgules
53
+ - Listes : `[1, 2, 3]`, intervalle : `1..10`
54
+ - 14 fonctions intégrées : `vox`, `demand`, `nombre`, `texte`, `taille`, `ajoute`, `retire`, `hasard`, `arondi`, `majus`, `minus`, `koupe`, `tri`, `tip`
55
+ - Messages d'erreur **en français**, clairs et pédagogiques
56
+
57
+ 📚 **[Guide complet du langage → GUIDE_LAZARUS.md](GUIDE_LAZARUS.md)**
58
+
59
+ ## 📂 Contenu du dépôt
60
+
61
+ ```
62
+ lazarus.py L'interpréteur officiel (Python, zéro dépendance)
63
+ GUIDE_LAZARUS.md Le manuel complet du langage
64
+ exemples/ Programmes d'exemple (.laz)
65
+ docs/index.html Le playground web (moteur JavaScript complet)
66
+ pyproject.toml Package pip « lazarus-lang »
67
+ ```
68
+
69
+ ## 🌍 English
70
+
71
+ LAZARUS is a programming language with invented keywords (`laz` = let, `fonk` = function, `kan` = if, `tanke` = while, `pou...dan` = for...in, `vox` = print). Curly braces like Java, no semicolons like Python. It ships with a zero-dependency Python interpreter, a full in-browser playground (pure JavaScript), and beginner-friendly error messages in French. Try it: `pip install lazarus-lang`, then `lazarus examples/demo.laz`.
72
+
73
+ ## Licence
74
+
75
+ MIT — libre et gratuit pour tout le monde, pour toujours.
76
+
77
+ *Créé avec ❤️ par Ladji Doucaré, 2026.*
@@ -0,0 +1,972 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ ██╗ █████╗ ███████╗ █████╗ ██████╗ ██╗ ██╗███████╗
5
+ ██║ ██╔══██╗╚══███╔╝██╔══██╗██╔══██╗██║ ██║██╔════╝
6
+ ██║ ███████║ ███╔╝ ███████║██████╔╝██║ ██║███████╗
7
+ ██║ ██╔══██║ ███╔╝ ██╔══██║██╔══██╗██║ ██║╚════██║
8
+ ███████╗██║ ██║███████╗██║ ██║██║ ██║╚██████╔╝███████║
9
+ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
10
+
11
+ LAZARUS — Le langage de programmation de Ladji
12
+ Version 1.0
13
+
14
+ Syntaxe hybride Java + Python : des accolades { } mais pas de point-virgules.
15
+ Mots-clés inventés :
16
+ laz -> déclarer une variable
17
+ fonk -> définir une fonction
18
+ rend -> retourner une valeur
19
+ kan -> si (if)
20
+ sinon -> sinon (else) / sinon kan (else if)
21
+ tanke -> tant que (while)
22
+ pou..dan -> pour chaque (for)
23
+ kase -> casser la boucle (break)
24
+ swiv -> passer au suivant (continue)
25
+ vrai / faux / walu -> true / false / null
26
+ et / ou / non -> and / or / not
27
+
28
+ Utilisation :
29
+ python3 lazarus.py programme.laz (exécuter un fichier)
30
+ python3 lazarus.py (mode interactif)
31
+ """
32
+
33
+ import sys
34
+ import random
35
+
36
+ # ============================================================
37
+ # ERREURS
38
+ # ============================================================
39
+
40
+ class LazError(Exception):
41
+ def __init__(self, message, line=None):
42
+ self.message = message
43
+ self.line = line
44
+ super().__init__(message)
45
+
46
+ def __str__(self):
47
+ if self.line:
48
+ return f"✘ Erreur LAZARUS (ligne {self.line}) : {self.message}"
49
+ return f"✘ Erreur LAZARUS : {self.message}"
50
+
51
+ class ReturnEx(Exception):
52
+ def __init__(self, value):
53
+ self.value = value
54
+
55
+ class BreakEx(Exception):
56
+ pass
57
+
58
+ class ContinueEx(Exception):
59
+ pass
60
+
61
+ # ============================================================
62
+ # LEXER (découpe le code en jetons)
63
+ # ============================================================
64
+
65
+ KEYWORDS = {
66
+ 'laz', 'fonk', 'rend', 'kan', 'sinon', 'tanke', 'pou', 'dan',
67
+ 'vrai', 'faux', 'walu', 'et', 'ou', 'non', 'kase', 'swiv',
68
+ }
69
+
70
+ TWO_CHAR_OPS = {'==', '!=', '<=', '>=', '&&', '||', '..'}
71
+ ONE_CHAR_OPS = {'+', '-', '*', '/', '%', '<', '>', '=', '(', ')',
72
+ '{', '}', '[', ']', ',', '!', ';'}
73
+
74
+ def tokenize(source):
75
+ tokens = []
76
+ i = 0
77
+ line = 1
78
+ n = len(source)
79
+ paren_depth = 0 # pas de NEWLINE à l'intérieur de ( ) ou [ ]
80
+
81
+ while i < n:
82
+ c = source[i]
83
+
84
+ # espaces
85
+ if c in ' \t\r':
86
+ i += 1
87
+ continue
88
+
89
+ # retour à la ligne = séparateur d'instructions
90
+ if c == '\n':
91
+ if paren_depth == 0:
92
+ tokens.append(('NEWLINE', None, line))
93
+ line += 1
94
+ i += 1
95
+ continue
96
+
97
+ # commentaires # ... ou // ...
98
+ if c == '#' or (c == '/' and i + 1 < n and source[i+1] == '/'):
99
+ while i < n and source[i] != '\n':
100
+ i += 1
101
+ continue
102
+
103
+ # chaînes de caractères "..."
104
+ if c == '"':
105
+ i += 1
106
+ start_line = line
107
+ buf = []
108
+ while i < n and source[i] != '"':
109
+ ch = source[i]
110
+ if ch == '\n':
111
+ raise LazError('chaîne de caractères non fermée (il manque un ")', start_line)
112
+ if ch == '\\' and i + 1 < n:
113
+ nxt = source[i+1]
114
+ esc = {'n': '\n', 't': '\t', '"': '"', '\\': '\\'}
115
+ buf.append(esc.get(nxt, nxt))
116
+ i += 2
117
+ else:
118
+ buf.append(ch)
119
+ i += 1
120
+ if i >= n:
121
+ raise LazError('chaîne de caractères non fermée (il manque un ")', start_line)
122
+ i += 1 # sauter le " final
123
+ tokens.append(('STRING', ''.join(buf), start_line))
124
+ continue
125
+
126
+ # nombres
127
+ if c.isdigit():
128
+ start = i
129
+ while i < n and source[i].isdigit():
130
+ i += 1
131
+ # partie décimale (attention à l'opérateur .. )
132
+ if i < n and source[i] == '.' and i + 1 < n and source[i+1].isdigit():
133
+ i += 1
134
+ while i < n and source[i].isdigit():
135
+ i += 1
136
+ num = source[start:i]
137
+ value = float(num) if '.' in num else int(num)
138
+ tokens.append(('NUMBER', value, line))
139
+ continue
140
+
141
+ # identifiants et mots-clés
142
+ if c.isalpha() or c == '_':
143
+ start = i
144
+ while i < n and (source[i].isalnum() or source[i] == '_'):
145
+ i += 1
146
+ word = source[start:i]
147
+ if word in KEYWORDS:
148
+ tokens.append((word.upper(), word, line))
149
+ else:
150
+ tokens.append(('IDENT', word, line))
151
+ continue
152
+
153
+ # opérateurs à deux caractères
154
+ if source[i:i+2] in TWO_CHAR_OPS:
155
+ tokens.append(('OP', source[i:i+2], line))
156
+ i += 2
157
+ continue
158
+
159
+ # opérateurs à un caractère
160
+ if c in ONE_CHAR_OPS:
161
+ if c in '([':
162
+ paren_depth += 1
163
+ elif c in ')]':
164
+ paren_depth = max(0, paren_depth - 1)
165
+ if c == ';':
166
+ tokens.append(('NEWLINE', None, line))
167
+ else:
168
+ tokens.append(('OP', c, line))
169
+ i += 1
170
+ continue
171
+
172
+ raise LazError(f"caractère inconnu : '{c}'", line)
173
+
174
+ tokens.append(('NEWLINE', None, line))
175
+ tokens.append(('EOF', None, line))
176
+ return tokens
177
+
178
+ # ============================================================
179
+ # PARSER (transforme les jetons en arbre de syntaxe)
180
+ # ============================================================
181
+
182
+ class Parser:
183
+ def __init__(self, tokens):
184
+ self.tokens = tokens
185
+ self.pos = 0
186
+
187
+ def peek(self):
188
+ return self.tokens[self.pos]
189
+
190
+ def next(self):
191
+ tok = self.tokens[self.pos]
192
+ self.pos += 1
193
+ return tok
194
+
195
+ def check(self, ttype, value=None):
196
+ tok = self.peek()
197
+ if tok[0] != ttype:
198
+ return False
199
+ if value is not None and tok[1] != value:
200
+ return False
201
+ return True
202
+
203
+ def accept(self, ttype, value=None):
204
+ if self.check(ttype, value):
205
+ return self.next()
206
+ return None
207
+
208
+ def expect(self, ttype, value=None, what=None):
209
+ tok = self.peek()
210
+ if not self.check(ttype, value):
211
+ attendu = what or value or ttype
212
+ trouve = tok[1] if tok[1] is not None else tok[0]
213
+ trouve = {'NEWLINE': 'une fin de ligne', 'EOF': 'la fin du fichier'}.get(trouve, trouve)
214
+ raise LazError(f"j'attendais « {attendu} » mais j'ai trouvé « {trouve} »", tok[2])
215
+ return self.next()
216
+
217
+ def skip_newlines(self):
218
+ while self.check('NEWLINE'):
219
+ self.next()
220
+
221
+ # ---------- programme ----------
222
+
223
+ def parse_program(self):
224
+ stmts = []
225
+ self.skip_newlines()
226
+ while not self.check('EOF'):
227
+ stmts.append(self.parse_statement())
228
+ self.skip_newlines()
229
+ return ('block', stmts)
230
+
231
+ def parse_block(self):
232
+ self.skip_newlines()
233
+ self.expect('OP', '{', '{')
234
+ stmts = []
235
+ self.skip_newlines()
236
+ while not self.check('OP', '}'):
237
+ if self.check('EOF'):
238
+ raise LazError("il manque une accolade fermante }", self.peek()[2])
239
+ stmts.append(self.parse_statement())
240
+ self.skip_newlines()
241
+ self.expect('OP', '}', '}')
242
+ return ('block', stmts)
243
+
244
+ # ---------- instructions ----------
245
+
246
+ def parse_statement(self):
247
+ tok = self.peek()
248
+ line = tok[2]
249
+
250
+ if self.accept('LAZ'):
251
+ name = self.expect('IDENT', what='un nom de variable')[1]
252
+ self.expect('OP', '=', '=')
253
+ value = self.parse_expression()
254
+ return ('declare', name, value, line)
255
+
256
+ if self.accept('FONK'):
257
+ name = self.expect('IDENT', what='un nom de fonction')[1]
258
+ self.expect('OP', '(', '(')
259
+ params = []
260
+ if not self.check('OP', ')'):
261
+ params.append(self.expect('IDENT', what='un paramètre')[1])
262
+ while self.accept('OP', ','):
263
+ params.append(self.expect('IDENT', what='un paramètre')[1])
264
+ self.expect('OP', ')', ')')
265
+ body = self.parse_block()
266
+ return ('fonk', name, params, body, line)
267
+
268
+ if self.accept('KAN'):
269
+ return self.parse_kan(line)
270
+
271
+ if self.accept('TANKE'):
272
+ cond = self.parse_expression()
273
+ body = self.parse_block()
274
+ return ('tanke', cond, body, line)
275
+
276
+ if self.accept('POU'):
277
+ var = self.expect('IDENT', what='un nom de variable')[1]
278
+ self.expect('DAN', what='dan')
279
+ iterable = self.parse_expression()
280
+ body = self.parse_block()
281
+ return ('pou', var, iterable, body, line)
282
+
283
+ if self.accept('REND'):
284
+ if self.check('NEWLINE') or self.check('OP', '}') or self.check('EOF'):
285
+ return ('rend', None, line)
286
+ value = self.parse_expression()
287
+ return ('rend', value, line)
288
+
289
+ if self.accept('KASE'):
290
+ return ('kase', line)
291
+
292
+ if self.accept('SWIV'):
293
+ return ('swiv', line)
294
+
295
+ # expression ou affectation
296
+ expr = self.parse_expression()
297
+ if self.check('OP', '='):
298
+ self.next()
299
+ value = self.parse_expression()
300
+ if expr[0] == 'var':
301
+ return ('assign', expr[1], value, line)
302
+ if expr[0] == 'index':
303
+ return ('assign_index', expr[1], expr[2], value, line)
304
+ raise LazError("on ne peut affecter une valeur qu'à une variable ou à un élément de liste", line)
305
+ return ('expr', expr, line)
306
+
307
+ def parse_kan(self, line):
308
+ cond = self.parse_expression()
309
+ body = self.parse_block()
310
+ else_branch = None
311
+ # autoriser un retour à la ligne avant « sinon »
312
+ save = self.pos
313
+ self.skip_newlines()
314
+ if self.accept('SINON'):
315
+ if self.accept('KAN'):
316
+ else_branch = ('block', [self.parse_kan(self.peek()[2])])
317
+ else:
318
+ else_branch = self.parse_block()
319
+ else:
320
+ self.pos = save
321
+ return ('kan', cond, body, else_branch, line)
322
+
323
+ # ---------- expressions ----------
324
+
325
+ def parse_expression(self):
326
+ return self.parse_or()
327
+
328
+ def parse_or(self):
329
+ left = self.parse_and()
330
+ while True:
331
+ if self.accept('OU') or (self.check('OP', '||') and self.next()):
332
+ right = self.parse_and()
333
+ left = ('or', left, right)
334
+ else:
335
+ break
336
+ return left
337
+
338
+ def parse_and(self):
339
+ left = self.parse_not()
340
+ while True:
341
+ if self.accept('ET') or (self.check('OP', '&&') and self.next()):
342
+ right = self.parse_not()
343
+ left = ('and', left, right)
344
+ else:
345
+ break
346
+ return left
347
+
348
+ def parse_not(self):
349
+ if self.accept('NON') or (self.check('OP', '!') and self.next()):
350
+ return ('not', self.parse_not())
351
+ return self.parse_comparison()
352
+
353
+ def parse_comparison(self):
354
+ left = self.parse_range()
355
+ tok = self.peek()
356
+ if tok[0] == 'OP' and tok[1] in ('==', '!=', '<', '>', '<=', '>='):
357
+ op = self.next()[1]
358
+ right = self.parse_range()
359
+ return ('cmp', op, left, right, tok[2])
360
+ return left
361
+
362
+ def parse_range(self):
363
+ left = self.parse_additive()
364
+ tok = self.peek()
365
+ if tok[0] == 'OP' and tok[1] == '..':
366
+ self.next()
367
+ right = self.parse_additive()
368
+ return ('range', left, right, tok[2])
369
+ return left
370
+
371
+ def parse_additive(self):
372
+ left = self.parse_term()
373
+ while True:
374
+ tok = self.peek()
375
+ if tok[0] == 'OP' and tok[1] in ('+', '-'):
376
+ op = self.next()[1]
377
+ right = self.parse_term()
378
+ left = ('binop', op, left, right, tok[2])
379
+ else:
380
+ break
381
+ return left
382
+
383
+ def parse_term(self):
384
+ left = self.parse_unary()
385
+ while True:
386
+ tok = self.peek()
387
+ if tok[0] == 'OP' and tok[1] in ('*', '/', '%'):
388
+ op = self.next()[1]
389
+ right = self.parse_unary()
390
+ left = ('binop', op, left, right, tok[2])
391
+ else:
392
+ break
393
+ return left
394
+
395
+ def parse_unary(self):
396
+ tok = self.peek()
397
+ if tok[0] == 'OP' and tok[1] == '-':
398
+ self.next()
399
+ return ('neg', self.parse_unary(), tok[2])
400
+ return self.parse_postfix()
401
+
402
+ def parse_postfix(self):
403
+ expr = self.parse_primary()
404
+ while True:
405
+ tok = self.peek()
406
+ if tok[0] == 'OP' and tok[1] == '(':
407
+ self.next()
408
+ args = []
409
+ if not self.check('OP', ')'):
410
+ args.append(self.parse_expression())
411
+ while self.accept('OP', ','):
412
+ args.append(self.parse_expression())
413
+ self.expect('OP', ')', ')')
414
+ expr = ('call', expr, args, tok[2])
415
+ elif tok[0] == 'OP' and tok[1] == '[':
416
+ self.next()
417
+ index = self.parse_expression()
418
+ self.expect('OP', ']', ']')
419
+ expr = ('index', expr, index, tok[2])
420
+ else:
421
+ break
422
+ return expr
423
+
424
+ def parse_primary(self):
425
+ tok = self.peek()
426
+
427
+ if tok[0] == 'NUMBER':
428
+ self.next()
429
+ return ('num', tok[1])
430
+ if tok[0] == 'STRING':
431
+ self.next()
432
+ return ('str', tok[1])
433
+ if tok[0] == 'VRAI':
434
+ self.next()
435
+ return ('bool', True)
436
+ if tok[0] == 'FAUX':
437
+ self.next()
438
+ return ('bool', False)
439
+ if tok[0] == 'WALU':
440
+ self.next()
441
+ return ('walu',)
442
+ if tok[0] == 'IDENT':
443
+ self.next()
444
+ return ('var', tok[1], tok[2])
445
+ if tok[0] == 'OP' and tok[1] == '(':
446
+ self.next()
447
+ expr = self.parse_expression()
448
+ self.expect('OP', ')', ')')
449
+ return expr
450
+ if tok[0] == 'OP' and tok[1] == '[':
451
+ self.next()
452
+ items = []
453
+ self.skip_newlines()
454
+ if not self.check('OP', ']'):
455
+ items.append(self.parse_expression())
456
+ while self.accept('OP', ','):
457
+ self.skip_newlines()
458
+ items.append(self.parse_expression())
459
+ self.skip_newlines()
460
+ self.expect('OP', ']', ']')
461
+ return ('list', items, tok[2])
462
+
463
+ trouve = tok[1] if tok[1] is not None else tok[0]
464
+ trouve = {'NEWLINE': 'une fin de ligne', 'EOF': 'la fin du fichier'}.get(trouve, trouve)
465
+ raise LazError(f"expression invalide, je ne comprends pas « {trouve} » (il manque peut-être une valeur)", tok[2])
466
+
467
+ # ============================================================
468
+ # ENVIRONNEMENT (portée des variables)
469
+ # ============================================================
470
+
471
+ class Env:
472
+ def __init__(self, parent=None):
473
+ self.vars = {}
474
+ self.parent = parent
475
+
476
+ def get(self, name, line=None):
477
+ env = self
478
+ while env is not None:
479
+ if name in env.vars:
480
+ return env.vars[name]
481
+ env = env.parent
482
+ raise LazError(f"la variable « {name} » n'existe pas (déclare-la avec : laz {name} = ...)", line)
483
+
484
+ def declare(self, name, value):
485
+ self.vars[name] = value
486
+
487
+ def assign(self, name, value, line=None):
488
+ env = self
489
+ while env is not None:
490
+ if name in env.vars:
491
+ env.vars[name] = value
492
+ return
493
+ env = env.parent
494
+ raise LazError(f"la variable « {name} » n'existe pas (déclare-la avec : laz {name} = ...)", line)
495
+
496
+ # ============================================================
497
+ # VALEURS ET FONCTIONS
498
+ # ============================================================
499
+
500
+ class LazFunction:
501
+ def __init__(self, name, params, body, env):
502
+ self.name = name
503
+ self.params = params
504
+ self.body = body
505
+ self.env = env
506
+
507
+ def to_text(value):
508
+ if value is None:
509
+ return 'walu'
510
+ if value is True:
511
+ return 'vrai'
512
+ if value is False:
513
+ return 'faux'
514
+ if isinstance(value, float) and value.is_integer():
515
+ return str(int(value))
516
+ if isinstance(value, list):
517
+ return '[' + ', '.join(
518
+ f'"{v}"' if isinstance(v, str) else to_text(v) for v in value
519
+ ) + ']'
520
+ if isinstance(value, LazFunction):
521
+ return f'<fonk {value.name}>'
522
+ return str(value)
523
+
524
+ def is_truthy(value):
525
+ if value is None or value is False:
526
+ return False
527
+ if value is True:
528
+ return True
529
+ if isinstance(value, (int, float)):
530
+ return value != 0
531
+ if isinstance(value, (str, list)):
532
+ return len(value) > 0
533
+ return True
534
+
535
+ def check_number(value, line, contexte='cette opération'):
536
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
537
+ raise LazError(f"{contexte} demande un nombre, pas « {to_text(value)} »", line)
538
+ return value
539
+
540
+ # ============================================================
541
+ # FONCTIONS INTÉGRÉES
542
+ # ============================================================
543
+
544
+ def make_builtins(env):
545
+
546
+ def b_vox(args, line):
547
+ print(' '.join(to_text(a) for a in args))
548
+ return None
549
+
550
+ def b_demand(args, line):
551
+ prompt = to_text(args[0]) if args else ''
552
+ try:
553
+ return input(prompt)
554
+ except EOFError:
555
+ return ''
556
+
557
+ def b_nombre(args, line):
558
+ _need(args, 1, 'nombre', line)
559
+ v = args[0]
560
+ if isinstance(v, bool):
561
+ return 1 if v else 0
562
+ if isinstance(v, (int, float)):
563
+ return v
564
+ try:
565
+ f = float(str(v).strip().replace(',', '.'))
566
+ return int(f) if f.is_integer() else f
567
+ except (ValueError, TypeError):
568
+ raise LazError(f"impossible de convertir « {to_text(v)} » en nombre", line)
569
+
570
+ def b_texte(args, line):
571
+ _need(args, 1, 'texte', line)
572
+ return to_text(args[0])
573
+
574
+ def b_taille(args, line):
575
+ _need(args, 1, 'taille', line)
576
+ v = args[0]
577
+ if isinstance(v, (str, list)):
578
+ return len(v)
579
+ raise LazError("taille() fonctionne avec un texte ou une liste", line)
580
+
581
+ def b_ajoute(args, line):
582
+ _need(args, 2, 'ajoute', line)
583
+ if not isinstance(args[0], list):
584
+ raise LazError("ajoute() demande une liste en premier argument", line)
585
+ args[0].append(args[1])
586
+ return args[0]
587
+
588
+ def b_retire(args, line):
589
+ _need(args, 2, 'retire', line)
590
+ lst, idx = args[0], args[1]
591
+ if not isinstance(lst, list):
592
+ raise LazError("retire() demande une liste en premier argument", line)
593
+ idx = int(check_number(idx, line, 'retire()'))
594
+ if idx < -len(lst) or idx >= len(lst):
595
+ raise LazError(f"position {idx} hors de la liste (taille {len(lst)})", line)
596
+ return lst.pop(idx)
597
+
598
+ def b_hasard(args, line):
599
+ _need(args, 2, 'hasard', line)
600
+ a = int(check_number(args[0], line, 'hasard()'))
601
+ b = int(check_number(args[1], line, 'hasard()'))
602
+ return random.randint(min(a, b), max(a, b))
603
+
604
+ def b_arondi(args, line):
605
+ if not args:
606
+ _need(args, 1, 'arondi', line)
607
+ v = check_number(args[0], line, 'arondi()')
608
+ nd = int(check_number(args[1], line, 'arondi()')) if len(args) > 1 else 0
609
+ r = round(v, nd)
610
+ return int(r) if nd == 0 else r
611
+
612
+ def b_majus(args, line):
613
+ _need(args, 1, 'majus', line)
614
+ return to_text(args[0]).upper()
615
+
616
+ def b_minus(args, line):
617
+ _need(args, 1, 'minus', line)
618
+ return to_text(args[0]).lower()
619
+
620
+ def b_koupe(args, line):
621
+ _need(args, 2, 'koupe', line)
622
+ return to_text(args[0]).split(to_text(args[1]))
623
+
624
+ def b_tri(args, line):
625
+ _need(args, 1, 'tri', line)
626
+ if not isinstance(args[0], list):
627
+ raise LazError("tri() demande une liste", line)
628
+ try:
629
+ return sorted(args[0])
630
+ except TypeError:
631
+ raise LazError("tri() ne peut pas trier des types mélangés", line)
632
+
633
+ def b_tip(args, line):
634
+ _need(args, 1, 'tip', line)
635
+ v = args[0]
636
+ if v is None: return 'walu'
637
+ if isinstance(v, bool): return 'buli'
638
+ if isinstance(v, (int, float)): return 'nombre'
639
+ if isinstance(v, str): return 'texte'
640
+ if isinstance(v, list): return 'liste'
641
+ if isinstance(v, LazFunction): return 'fonk'
642
+ return 'inconnu'
643
+
644
+ def _need(args, count, name, line):
645
+ if len(args) < count:
646
+ raise LazError(f"{name}() demande au moins {count} argument(s), reçu {len(args)}", line)
647
+
648
+ builtins = {
649
+ 'vox': b_vox, # afficher
650
+ 'demand': b_demand, # demander une saisie au clavier
651
+ 'nombre': b_nombre, # convertir en nombre
652
+ 'texte': b_texte, # convertir en texte
653
+ 'taille': b_taille, # longueur d'un texte ou d'une liste
654
+ 'ajoute': b_ajoute, # ajouter à une liste
655
+ 'retire': b_retire, # retirer d'une liste
656
+ 'hasard': b_hasard, # nombre aléatoire entre a et b
657
+ 'arondi': b_arondi, # arrondir
658
+ 'majus': b_majus, # MAJUSCULES
659
+ 'minus': b_minus, # minuscules
660
+ 'koupe': b_koupe, # découper un texte en liste
661
+ 'tri': b_tri, # trier une liste
662
+ 'tip': b_tip, # type d'une valeur
663
+ }
664
+ for name, fn in builtins.items():
665
+ env.declare(name, ('builtin', name, fn))
666
+
667
+ # ============================================================
668
+ # INTERPRÉTEUR
669
+ # ============================================================
670
+
671
+ class Interpreter:
672
+ def __init__(self):
673
+ self.globals = Env()
674
+ make_builtins(self.globals)
675
+
676
+ def run(self, source):
677
+ tokens = tokenize(source)
678
+ ast = Parser(tokens).parse_program()
679
+ return self.exec_block(ast, self.globals)
680
+
681
+ def exec_block(self, block, env):
682
+ result = None
683
+ for stmt in block[1]:
684
+ result = self.exec_stmt(stmt, env)
685
+ return result
686
+
687
+ def exec_stmt(self, stmt, env):
688
+ kind = stmt[0]
689
+
690
+ if kind == 'declare':
691
+ _, name, value_node, line = stmt
692
+ env.declare(name, self.eval(value_node, env))
693
+ return None
694
+
695
+ if kind == 'assign':
696
+ _, name, value_node, line = stmt
697
+ env.assign(name, self.eval(value_node, env), line)
698
+ return None
699
+
700
+ if kind == 'assign_index':
701
+ _, target_node, index_node, value_node, line = stmt
702
+ target = self.eval(target_node, env)
703
+ index = self.eval(index_node, env)
704
+ value = self.eval(value_node, env)
705
+ if not isinstance(target, list):
706
+ raise LazError("on ne peut modifier par position que les listes", line)
707
+ idx = int(check_number(index, line, "l'indexation"))
708
+ if idx < -len(target) or idx >= len(target):
709
+ raise LazError(f"position {idx} hors de la liste (taille {len(target)})", line)
710
+ target[idx] = value
711
+ return None
712
+
713
+ if kind == 'fonk':
714
+ _, name, params, body, line = stmt
715
+ env.declare(name, LazFunction(name, params, body, env))
716
+ return None
717
+
718
+ if kind == 'kan':
719
+ _, cond, body, else_branch, line = stmt
720
+ if is_truthy(self.eval(cond, env)):
721
+ self.exec_block(body, env)
722
+ elif else_branch is not None:
723
+ self.exec_block(else_branch, env)
724
+ return None
725
+
726
+ if kind == 'tanke':
727
+ _, cond, body, line = stmt
728
+ while is_truthy(self.eval(cond, env)):
729
+ try:
730
+ self.exec_block(body, env)
731
+ except BreakEx:
732
+ break
733
+ except ContinueEx:
734
+ continue
735
+ return None
736
+
737
+ if kind == 'pou':
738
+ _, var, iterable_node, body, line = stmt
739
+ iterable = self.eval(iterable_node, env)
740
+ if isinstance(iterable, str):
741
+ iterable = list(iterable)
742
+ if not isinstance(iterable, list):
743
+ raise LazError("« pou ... dan ... » demande une liste, un intervalle (1..10) ou un texte", line)
744
+ env.declare(var, None)
745
+ for item in iterable:
746
+ env.vars[var] = item
747
+ try:
748
+ self.exec_block(body, env)
749
+ except BreakEx:
750
+ break
751
+ except ContinueEx:
752
+ continue
753
+ return None
754
+
755
+ if kind == 'rend':
756
+ _, value_node, line = stmt
757
+ value = self.eval(value_node, env) if value_node is not None else None
758
+ raise ReturnEx(value)
759
+
760
+ if kind == 'kase':
761
+ raise BreakEx()
762
+
763
+ if kind == 'swiv':
764
+ raise ContinueEx()
765
+
766
+ if kind == 'expr':
767
+ return self.eval(stmt[1], env)
768
+
769
+ raise LazError(f"instruction inconnue : {kind}")
770
+
771
+ # ---------- évaluation des expressions ----------
772
+
773
+ def eval(self, node, env):
774
+ kind = node[0]
775
+
776
+ if kind == 'num':
777
+ return node[1]
778
+ if kind == 'str':
779
+ return node[1]
780
+ if kind == 'bool':
781
+ return node[1]
782
+ if kind == 'walu':
783
+ return None
784
+ if kind == 'var':
785
+ return env.get(node[1], node[2])
786
+ if kind == 'list':
787
+ return [self.eval(item, env) for item in node[1]]
788
+
789
+ if kind == 'range':
790
+ _, start_node, end_node, line = node
791
+ start = int(check_number(self.eval(start_node, env), line, "l'intervalle .."))
792
+ end = int(check_number(self.eval(end_node, env), line, "l'intervalle .."))
793
+ step = 1 if end >= start else -1
794
+ return list(range(start, end + step, step))
795
+
796
+ if kind == 'or':
797
+ left = self.eval(node[1], env)
798
+ if is_truthy(left):
799
+ return left
800
+ return self.eval(node[2], env)
801
+
802
+ if kind == 'and':
803
+ left = self.eval(node[1], env)
804
+ if not is_truthy(left):
805
+ return left
806
+ return self.eval(node[2], env)
807
+
808
+ if kind == 'not':
809
+ return not is_truthy(self.eval(node[1], env))
810
+
811
+ if kind == 'neg':
812
+ _, operand, line = node
813
+ return -check_number(self.eval(operand, env), line, 'le signe -')
814
+
815
+ if kind == 'cmp':
816
+ _, op, left_node, right_node, line = node
817
+ left = self.eval(left_node, env)
818
+ right = self.eval(right_node, env)
819
+ if op == '==':
820
+ return left == right
821
+ if op == '!=':
822
+ return left != right
823
+ # comparaisons d'ordre : nombres entre eux, textes entre eux
824
+ if isinstance(left, str) and isinstance(right, str):
825
+ pass
826
+ else:
827
+ check_number(left, line, f"la comparaison {op}")
828
+ check_number(right, line, f"la comparaison {op}")
829
+ if op == '<': return left < right
830
+ if op == '>': return left > right
831
+ if op == '<=': return left <= right
832
+ if op == '>=': return left >= right
833
+
834
+ if kind == 'binop':
835
+ _, op, left_node, right_node, line = node
836
+ left = self.eval(left_node, env)
837
+ right = self.eval(right_node, env)
838
+
839
+ if op == '+':
840
+ if isinstance(left, str) or isinstance(right, str):
841
+ return to_text(left) + to_text(right)
842
+ if isinstance(left, list) and isinstance(right, list):
843
+ return left + right
844
+ check_number(left, line, "l'addition +")
845
+ check_number(right, line, "l'addition +")
846
+ return left + right
847
+
848
+ check_number(left, line, f"l'opération {op}")
849
+ check_number(right, line, f"l'opération {op}")
850
+ if op == '-':
851
+ return left - right
852
+ if op == '*':
853
+ return left * right
854
+ if op == '/':
855
+ if right == 0:
856
+ raise LazError("division par zéro impossible", line)
857
+ result = left / right
858
+ return int(result) if isinstance(result, float) and result.is_integer() else result
859
+ if op == '%':
860
+ if right == 0:
861
+ raise LazError("modulo par zéro impossible", line)
862
+ return left % right
863
+
864
+ if kind == 'index':
865
+ _, target_node, index_node, line = node
866
+ target = self.eval(target_node, env)
867
+ index = self.eval(index_node, env)
868
+ if not isinstance(target, (list, str)):
869
+ raise LazError("on ne peut indexer que les listes et les textes", line)
870
+ idx = int(check_number(index, line, "l'indexation"))
871
+ if idx < -len(target) or idx >= len(target):
872
+ raise LazError(f"position {idx} hors limites (taille {len(target)})", line)
873
+ return target[idx]
874
+
875
+ if kind == 'call':
876
+ _, callee_node, arg_nodes, line = node
877
+ callee = self.eval(callee_node, env)
878
+ args = [self.eval(a, env) for a in arg_nodes]
879
+
880
+ if isinstance(callee, tuple) and callee[0] == 'builtin':
881
+ return callee[2](args, line)
882
+
883
+ if isinstance(callee, LazFunction):
884
+ if len(args) != len(callee.params):
885
+ raise LazError(
886
+ f"la fonction « {callee.name} » attend {len(callee.params)} argument(s), reçu {len(args)}",
887
+ line)
888
+ call_env = Env(parent=callee.env)
889
+ for param, arg in zip(callee.params, args):
890
+ call_env.declare(param, arg)
891
+ try:
892
+ self.exec_block(callee.body, call_env)
893
+ except ReturnEx as ret:
894
+ return ret.value
895
+ return None
896
+
897
+ raise LazError(f"« {to_text(callee)} » n'est pas une fonction", line)
898
+
899
+ raise LazError(f"expression inconnue : {kind}")
900
+
901
+ # ============================================================
902
+ # POINT D'ENTRÉE
903
+ # ============================================================
904
+
905
+ BANNER = """
906
+ ╔═══════════════════════════════════════════╗
907
+ ║ LAZARUS v1.0 — le langage de Ladji ║
908
+ ║ Tape ton code, ou « sortir » pour quitter ║
909
+ ╚═══════════════════════════════════════════╝
910
+ """
911
+
912
+ def repl():
913
+ print(BANNER)
914
+ interp = Interpreter()
915
+ buffer = []
916
+ while True:
917
+ try:
918
+ prompt = '....> ' if buffer else 'LAZ> '
919
+ line = input(prompt)
920
+ except (EOFError, KeyboardInterrupt):
921
+ print('\nAu revoir !')
922
+ break
923
+
924
+ if not buffer and line.strip() in ('sortir', 'quit', 'exit'):
925
+ print('Au revoir !')
926
+ break
927
+
928
+ buffer.append(line)
929
+ code = '\n'.join(buffer)
930
+
931
+ # attendre la fin des blocs { }
932
+ if code.count('{') > code.count('}'):
933
+ continue
934
+
935
+ buffer = []
936
+ if not code.strip():
937
+ continue
938
+ try:
939
+ result = interp.run(code)
940
+ if result is not None:
941
+ print(to_text(result))
942
+ except LazError as e:
943
+ print(e)
944
+ except RecursionError:
945
+ print('✘ Erreur LAZARUS : récursion trop profonde (boucle infinie ?)')
946
+
947
+ def run_file(path):
948
+ try:
949
+ with open(path, 'r', encoding='utf-8') as f:
950
+ source = f.read()
951
+ except FileNotFoundError:
952
+ print(f"✘ Fichier introuvable : {path}")
953
+ sys.exit(1)
954
+
955
+ interp = Interpreter()
956
+ try:
957
+ interp.run(source)
958
+ except LazError as e:
959
+ print(e)
960
+ sys.exit(1)
961
+ except RecursionError:
962
+ print('✘ Erreur LAZARUS : récursion trop profonde (boucle infinie ?)')
963
+ sys.exit(1)
964
+
965
+ def main():
966
+ if len(sys.argv) > 1:
967
+ run_file(sys.argv[1])
968
+ else:
969
+ repl()
970
+
971
+ if __name__ == '__main__':
972
+ main()
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: lazarus-lang
3
+ Version: 1.0.0
4
+ Summary: LAZARUS — le langage de programmation de Ladji : accolades comme Java, simplicité de Python, mots-clés uniques (laz, fonk, kan, tanke...)
5
+ Author: Ladji Doucaré
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ladjidoucare994-boop/lazarus
8
+ Project-URL: Documentation, https://github.com/ladjidoucare994-boop/lazarus/blob/main/GUIDE_LAZARUS.md
9
+ Project-URL: Playground, https://ladjidoucare994-boop.github.io/lazarus/
10
+ Keywords: langage,programmation,interpreteur,lazarus,education,francais
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Software Development :: Interpreters
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Natural Language :: French
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Dynamic: license-file
21
+
22
+ # ⚡ LAZARUS
23
+
24
+ **Le langage de programmation créé par Ladji** — la structure de Java, la simplicité de Python, et des mots-clés uniques au monde.
25
+
26
+ *A programming language created by Ladji — Java's structure, Python's simplicity, and one-of-a-kind keywords. French-friendly error messages. See the [English section](#-english) below.*
27
+
28
+ ```lazarus
29
+ laz nom = "monde"
30
+
31
+ fonk saluer(qui) {
32
+ rend "Bonjour " + qui + " !"
33
+ }
34
+
35
+ pou i dan 1..3 {
36
+ vox(saluer(nom))
37
+ }
38
+ ```
39
+
40
+ ## 🚀 Essayer tout de suite
41
+
42
+ **Dans le navigateur, sans rien installer :** ouvrez le playground en ligne (dossier [`docs/`](docs/index.html) — hébergé avec GitHub Pages).
43
+
44
+ **Sur votre machine :**
45
+
46
+ ```bash
47
+ pip install lazarus-lang # puis :
48
+ lazarus mon_programme.laz # exécuter un fichier
49
+ lazarus # mode interactif
50
+ ```
51
+
52
+ Ou sans pip, avec juste Python 3 :
53
+
54
+ ```bash
55
+ python3 lazarus.py exemples/demo.laz
56
+ ```
57
+
58
+ ## 📖 Le langage en 30 secondes
59
+
60
+ | LAZARUS | Signification |
61
+ |---|---|
62
+ | `laz x = 5` | déclarer une variable |
63
+ | `vox("salut")` | afficher |
64
+ | `demand("Ton nom ? ")` | saisie clavier |
65
+ | `kan ... { } sinon { }` | si / sinon |
66
+ | `tanke ... { }` | tant que |
67
+ | `pou i dan 1..10 { }` | boucle pour |
68
+ | `fonk f(x) { rend x }` | fonction + retour |
69
+ | `kase` / `swiv` | break / continue |
70
+ | `vrai` / `faux` / `walu` | true / false / null |
71
+ | `et` / `ou` / `non` | and / or / not |
72
+
73
+ - Accolades `{ }` pour les blocs, **pas** de point-virgules
74
+ - Listes : `[1, 2, 3]`, intervalle : `1..10`
75
+ - 14 fonctions intégrées : `vox`, `demand`, `nombre`, `texte`, `taille`, `ajoute`, `retire`, `hasard`, `arondi`, `majus`, `minus`, `koupe`, `tri`, `tip`
76
+ - Messages d'erreur **en français**, clairs et pédagogiques
77
+
78
+ 📚 **[Guide complet du langage → GUIDE_LAZARUS.md](GUIDE_LAZARUS.md)**
79
+
80
+ ## 📂 Contenu du dépôt
81
+
82
+ ```
83
+ lazarus.py L'interpréteur officiel (Python, zéro dépendance)
84
+ GUIDE_LAZARUS.md Le manuel complet du langage
85
+ exemples/ Programmes d'exemple (.laz)
86
+ docs/index.html Le playground web (moteur JavaScript complet)
87
+ pyproject.toml Package pip « lazarus-lang »
88
+ ```
89
+
90
+ ## 🌍 English
91
+
92
+ LAZARUS is a programming language with invented keywords (`laz` = let, `fonk` = function, `kan` = if, `tanke` = while, `pou...dan` = for...in, `vox` = print). Curly braces like Java, no semicolons like Python. It ships with a zero-dependency Python interpreter, a full in-browser playground (pure JavaScript), and beginner-friendly error messages in French. Try it: `pip install lazarus-lang`, then `lazarus examples/demo.laz`.
93
+
94
+ ## Licence
95
+
96
+ MIT — libre et gratuit pour tout le monde, pour toujours.
97
+
98
+ *Créé avec ❤️ par Ladji Doucaré, 2026.*
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ lazarus.py
4
+ pyproject.toml
5
+ lazarus_lang.egg-info/PKG-INFO
6
+ lazarus_lang.egg-info/SOURCES.txt
7
+ lazarus_lang.egg-info/dependency_links.txt
8
+ lazarus_lang.egg-info/entry_points.txt
9
+ lazarus_lang.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lazarus = lazarus:main
@@ -0,0 +1 @@
1
+ lazarus
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lazarus-lang"
7
+ version = "1.0.0"
8
+ description = "LAZARUS — le langage de programmation de Ladji : accolades comme Java, simplicité de Python, mots-clés uniques (laz, fonk, kan, tanke...)"
9
+ readme = "README.md"
10
+ authors = [{ name = "Ladji Doucaré" }]
11
+ license = { text = "MIT" }
12
+ requires-python = ">=3.8"
13
+ keywords = ["langage", "programmation", "interpreteur", "lazarus", "education", "francais"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Software Development :: Interpreters",
19
+ "Intended Audience :: Education",
20
+ "Natural Language :: French",
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/ladjidoucare994-boop/lazarus"
25
+ Documentation = "https://github.com/ladjidoucare994-boop/lazarus/blob/main/GUIDE_LAZARUS.md"
26
+ Playground = "https://ladjidoucare994-boop.github.io/lazarus/"
27
+
28
+ [project.scripts]
29
+ lazarus = "lazarus:main"
30
+
31
+ [tool.setuptools]
32
+ py-modules = ["lazarus"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+