fazer-lang 0.1.0 → 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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # [Logo](logo.png)
2
+
3
+ Langage de programmation.
4
+
5
+ ## Features
6
+ - `stash` vars, `broadcast` outputs
7
+ - `risk` pour probabilités
8
+ - `uhquali event` pour random culture boosts
9
+ - `cooldown` et `wait` pour timers
10
+ - `save/load` pour persistence
11
+ - `broadcast to discord` pour bots
12
+ - Et plus : intersects, flows, parallels, territories
13
+
14
+ ## Installation
15
+ 1. Clone le repo : `git clone https://github.com/tonusername/fazer-lang.git`
16
+ 2. `cd fazer-lang`
17
+ 3. `npm install`
18
+ 4. Build standalone : `npm run build` → get `bin/fazer.exe`
19
+ 5. Run : `fazer run monfichier.fz`
20
+
21
+ Ou via npm (si publié) : `npm install -g fazer-lang` puis `fazer run ...`
22
+
23
+ ## Exemple (heist.fz)
24
+ `stash rep as 50
25
+ uhquali event "street cred boost"
26
+ cooldown heist 30m
27
+ risk 70% chance of broadcast "Win!" ; stash cash as 1000
28
+ save empire`
29
+
30
+
31
+ ## Logo
32
+ ![Fazer Logo](logo.png)
package/fazer.js CHANGED
@@ -1,175 +1,234 @@
1
+ #!/usr/bin/env node
2
+
1
3
  const fs = require('fs');
2
4
  const path = require('path');
3
- const { Client, IntentsBitField } = require('discord.js'); // Pour Discord
4
- const { Lexer, Parser, createToken } = require('chevrotain'); // Pour vrai parser
5
-
6
- // Parser Chevrotain pour remplacer regex (plus robuste, évite fragilités)
7
- const Stash = createToken({ name: 'Stash', pattern: /stash/ });
8
- const Broadcast = createToken({ name: 'Broadcast', pattern: /broadcast/ });
9
- const Intersect = createToken({ name: 'Intersect', pattern: /intersect/ });
10
- const Detour = createToken({ name: 'Detour', pattern: /detour/ });
11
- const EndIntersect = createToken({ name: 'EndIntersect', pattern: /endintersect/ });
12
- const Flow = createToken({ name: 'Flow', pattern: /flow/ });
13
- const EndFlow = createToken({ name: 'EndFlow', pattern: /endflow/ });
14
- const Trigger = createToken({ name: 'Trigger', pattern: /trigger/ });
15
- const Risk = createToken({ name: 'Risk', pattern: /risk/ });
16
- const Patrol = createToken({ name: 'Patrol', pattern: /patrol/ });
17
- const EndPatrol = createToken({ name: 'EndPatrol', pattern: /endpatrol/ });
18
- const Parallel = createToken({ name: 'Parallel', pattern: /parallel streets/ });
19
- const EndParallel = createToken({ name: 'EndParallel', pattern: /endparallel/ });
20
- const Uhquali = createToken({ name: 'Uhquali', pattern: /uhquali/ });
21
- const Cooldown = createToken({ name: 'Cooldown', pattern: /cooldown/ });
22
- const Wait = createToken({ name: 'Wait', pattern: /wait/ });
23
- const Save = createToken({ name: 'Save', pattern: /save/ });
24
- const Load = createToken({ name: 'Load', pattern: /load/ });
25
- const Territory = createToken({ name: 'Territory', pattern: /territory/ });
26
- const Whitespace = createToken({ name: 'Whitespace', pattern: /\s+/, group: Lexer.SKIPPED });
27
- const Identifier = createToken({ name: 'Identifier', pattern: /\w+/ });
28
- const NumberLiteral = createToken({ name: 'NumberLiteral', pattern: /\d+/ });
29
- const StringLiteral = createToken({ name: 'StringLiteral', pattern: /"[^"]*"/ });
30
- const Comment = createToken({ name: 'Comment', pattern: /#.*/, group: Lexer.SKIPPED });
31
-
32
- // Lexer
33
- const fazerLexer = new Lexer([
34
- Whitespace, Comment, Stash, Broadcast, Intersect, Detour, EndIntersect, Flow, EndFlow, Trigger, Risk,
35
- Patrol, EndPatrol, Parallel, EndParallel, Uhquali, Cooldown, Wait, Save, Load, Territory,
36
- Identifier, NumberLiteral, StringLiteral
5
+ const { Lexer, Parser, createToken, EmbeddedActionsParser } = require('chevrotain');
6
+
7
+ // Tokens (restent les mêmes, ajoutés Equals, NotEq, And, Or, Not pour expressions)
8
+ const Assign = createToken({ name: 'Assign', pattern: /:=/ });
9
+ const Arrow = createToken({ name: 'Arrow', pattern: /→/ });
10
+ const DoublePipe = createToken({ name: 'DoublePipe', pattern: /→>/ });
11
+ const Case = createToken({ name: 'Case', pattern: /case/ });
12
+ const Rescue = createToken({ name: 'Rescue', pattern: /rescue/ });
13
+ const Catch = createToken({ name: 'Catch', pattern: /catch/ });
14
+ const From = createToken({ name: 'From', pattern: /from/ });
15
+ const End = createToken({ name: 'End', pattern: /end/ });
16
+ const Await = createToken({ name: 'Await', pattern: /await/ });
17
+ const Mut = createToken({ name: 'Mut', pattern: /mut/ });
18
+ const Async = createToken({ name: 'Async', pattern: /async/ });
19
+ const Else = createToken({ name: 'Else', pattern: /else/ });
20
+ const In = createToken({ name: 'In', pattern: /in/ });
21
+ const Step = createToken({ name: 'Step', pattern: /step/ });
22
+
23
+ const Identifier = createToken({ name: 'Identifier', pattern: /[a-zA-Z_][a-zA-Z0-9_]*/ });
24
+ const Integer = createToken({ name: 'Integer', pattern: /-?\d+/ });
25
+ const Float = createToken({ name: 'Float', pattern: /-?\d+\.\d+/ });
26
+ const StringLiteral = createToken({ name: 'StringLiteral', pattern: /"([^"\\]|\\.)*"/ });
27
+ const True = createToken({ name: 'True', pattern: /true/ });
28
+ const False = createToken({ name: 'False', pattern: /false/ });
29
+ const Colon = createToken({ name: 'Colon', pattern: /:/ });
30
+ const LBracket = createToken({ name: 'LBracket', pattern: /\[/ });
31
+ const RBracket = createToken({ name: 'RBracket', pattern: /\]/ });
32
+ const LBrace = createToken({ name: 'LBrace', pattern: /{/ });
33
+ const RBrace = createToken({ name: 'RBrace', pattern: /}/ });
34
+ const Comma = createToken({ name: 'Comma', pattern: /,/ });
35
+ const Dot = createToken({ name: 'Dot', pattern: /\./ });
36
+ const Plus = createToken({ name: 'Plus', pattern: /\+/ });
37
+ const Minus = createToken({ name: 'Minus', pattern: /-/ });
38
+ const Star = createToken({ name: 'Star', pattern: /\*/ });
39
+ const Slash = createToken({ name: 'Slash', pattern: /\// });
40
+ const Percent = createToken({ name: 'Percent', pattern: /%/ });
41
+ const Greater = createToken({ name: 'Greater', pattern: />/ });
42
+ const Less = createToken({ name: 'Less', pattern: /</ });
43
+ const GreaterEq = createToken({ name: 'GreaterEq', pattern: />=/ });
44
+ const LessEq = createToken({ name: 'LessEq', pattern: /<=/ });
45
+ const Eq = createToken({ name: 'Eq', pattern: /==/ });
46
+ const NotEq = createToken({ name: 'NotEq', pattern: /!=/ });
47
+ const And = createToken({ name: 'And', pattern: /and/ });
48
+ const Or = createToken({ name: 'Or', pattern: /or/ });
49
+ const Not = createToken({ name: 'Not', pattern: /not/ });
50
+ const Comment = createToken({ name: 'Comment', pattern: /#.*/, group: Lexer.SKIPPED });
51
+ const WhiteSpace = createToken({ name: 'WhiteSpace', pattern: /\s+/, group: Lexer.SKIPPED });
52
+
53
+ const lexer = new Lexer([
54
+ Assign, Arrow, DoublePipe, Case, Rescue, Catch, From, End, Await, Mut, Async, Else, In, Step,
55
+ Identifier, Float, Integer, StringLiteral, True, False, Colon, LBracket, RBracket, LBrace, RBrace,
56
+ Comma, Dot, Plus, Minus, Star, Slash, Percent, Greater, Less, GreaterEq, LessEq, Eq, NotEq, And, Or, Not,
57
+ Comment, WhiteSpace
37
58
  ]);
38
59
 
39
- // Parser basique (extendable)
40
- class FazerParser extends Parser {
60
+ // ── Parser avec Embedded Actions (exécution inline) ──────────────────────────
61
+ class FazerParser extends EmbeddedActionsParser {
41
62
  constructor() {
42
- super(fazerLexer.allTokens);
63
+ super(lexer.allTokens);
43
64
  const $ = this;
44
- $.RULE('statement', () => {
45
- $.OR([
46
- { ALT: () => $.SUBRULE($.stashStmt) },
47
- { ALT: () => $.SUBRULE($.broadcastStmt) },
48
- // Ajoute les autres rules pour intersect, flow, etc.
49
- { ALT: () => $.SUBRULE($.uhqualiStmt) },
50
- { ALT: () => $.SUBRULE($.cooldownStmt) },
51
- { ALT: () => $.SUBRULE($.waitStmt) },
52
- { ALT: () => $.SUBRULE($.saveStmt) },
53
- { ALT: () => $.SUBRULE($.loadStmt) },
54
- { ALT: () => $.SUBRULE($.territoryStmt) },
55
- // ... (extend pour les autres)
56
- ]);
65
+
66
+ $.RULE('program', () => {
67
+ const stmts = $.MANY(() => $.SUBRULE($.statement));
68
+ return stmts;
69
+ });
70
+
71
+ $.RULE('statement', () => $.OR([
72
+ { ALT: () => $.SUBRULE($.assignStmt) },
73
+ { ALT: () => $.SUBRULE($.functionDef) },
74
+ { ALT: () => $.SUBRULE($.caseBlock) },
75
+ { ALT: () => $.SUBRULE($.rescueBlock) },
76
+ { ALT: () => $.SUBRULE($.expressionStmt) }
77
+ ]));
78
+
79
+ $.RULE('assignStmt', () => {
80
+ const isMut = $.OPTION(() => $.CONSUME(Mut));
81
+ const id = $.CONSUME(Identifier);
82
+ let type = null;
83
+ $.OPTION2(() => {
84
+ $.CONSUME(Colon);
85
+ type = $.CONSUME2(Identifier).image;
86
+ });
87
+ $.CONSUME(Assign);
88
+ const value = $.SUBRULE($.expression);
89
+ return { type: 'assign', mut: !!isMut, name: id.image, varType: type, value };
90
+ });
91
+
92
+ $.RULE('caseBlock', () => {
93
+ $.CONSUME(Case);
94
+ const expr = $.SUBRULE($.expression);
95
+ const cases = [];
96
+ $.MANY(() => {
97
+ let pattern = $.OR([
98
+ { ALT: () => $.SUBRULE($.pattern) },
99
+ { ALT: () => $.CONSUME(Else) ? 'else' : null }
100
+ ]);
101
+ $.CONSUME(Arrow);
102
+ const body = $.SUBRULE($.block);
103
+ cases.push({ pattern, body });
104
+ });
105
+ return { type: 'case', expr, cases };
106
+ });
107
+
108
+ $.RULE('block', () => {
109
+ const stmts = $.MANY(() => $.SUBRULE($.statement));
110
+ $.CONSUME(End);
111
+ return stmts;
112
+ });
113
+
114
+ $.RULE('expression', () => {
115
+ let left = $.SUBRULE($.primaryExpr);
116
+ $.MANY(() => {
117
+ const op = $.OR([
118
+ { ALT: () => $.CONSUME(Plus).image },
119
+ { ALT: () => $.CONSUME(Minus).image },
120
+ { ALT: () => $.CONSUME(Star).image },
121
+ { ALT: () => $.CONSUME(Slash).image }
122
+ ]);
123
+ const right = $.SUBRULE2($.primaryExpr);
124
+ left = { type: 'binop', op, left, right };
125
+ });
126
+ return left;
57
127
  });
58
- // Ajoute rules pour chaque stmt (ex: stashStmt: consume(Stash) then Identifier etc.)
128
+
129
+ $.RULE('primaryExpr', () => $.OR([
130
+ { ALT: () => $.CONSUME(Integer).image },
131
+ { ALT: () => $.CONSUME(StringLiteral).image.slice(1, -1) },
132
+ { ALT: () => $.CONSUME(Identifier).image },
133
+ // Ajouter list, map, call, etc.
134
+ ]));
135
+
136
+ // Ajouter rules pour functionDef, rescueBlock, pattern, etc.
137
+
59
138
  this.performSelfAnalysis();
60
139
  }
61
140
  }
62
- const parser = new FazerParser();
63
141
 
64
- class FazerInterpreter {
142
+ // ── Runtime (exécution du CST) ───────────────────────────────────────────────
143
+ class FazerRuntime {
65
144
  constructor() {
66
- this.variables = {};
67
- this.functions = {};
68
- this.cooldowns = {}; // { name: endTime }
69
- this.discordClient = null; // Init Discord lazy
70
- }
71
-
72
- // Parse avec Chevrotain
73
- parse(code) {
74
- const lexResult = fazerLexer.tokenize(code);
75
- parser.input = lexResult.tokens;
76
- const cst = parser.statement(); // Parse top-level
77
- if (parser.errors.length > 0) throw new Error(parser.errors[0].message);
78
- return cst; // Retourne CST pour exécution
145
+ this.vars = new Map(); // name → {value, mut, type}
146
+ this.fns = new Map();
79
147
  }
80
148
 
81
- // Exécute (simplifié, étend pour full CST)
82
- async executeBlock(lines, isAsync = false) {
83
- // Pour l'instant, mix regex + chevrotain ; full chevrotain later
84
- // ... (garde la logique précédente, mais parse chaque line avec lexer)
85
- let i = 0;
86
- const output = [];
87
- while (i < lines.length) {
88
- const line = lines[i].trim();
89
- if (!line || line.startsWith('#')) { i++; continue; }
90
-
91
- // Exemple pour uhquali
92
- if (line.startsWith('uhquali event ')) {
93
- const eventName = line.slice(13).trim().replace(/"/g, '');
94
- const events = ['Win culture boost +20 cultural_rep', 'Lose -10, rival diss', 'Neutral vibe'];
95
- const outcome = events[Math.floor(Math.random() * events.length)];
96
- this.variables['cultural_rep'] = (this.variables['cultural_rep'] || 0) + (outcome.includes('+') ? 20 : outcome.includes('-') ? -10 : 0);
97
- console.log(`Uhquali event "${eventName}": ${outcome}`);
98
- output.push(outcome);
99
- }
100
-
101
- // Cooldown
102
- if (line.startsWith('cooldown ')) {
103
- const [name, timeStr] = line.slice(9).trim().split(' ');
104
- const time = this.parseTime(timeStr);
105
- this.cooldowns[name] = Date.now() + time;
106
- console.log(`Cooldown set for ${name}: ${timeStr}`);
107
- }
108
-
109
- // Check cooldown in actions (ex: in heist, if (Date.now() < this.cooldowns['heist']) bust)
110
-
111
- // Wait
112
- if (line.startsWith('wait ')) {
113
- const timeStr = line.slice(5).trim();
114
- const time = this.parseTime(timeStr);
115
- await new Promise(resolve => setTimeout(resolve, time));
149
+ evalExpr(node) {
150
+ if (typeof node === 'string' || typeof node === 'number') return node;
151
+ if (node.type === 'binop') {
152
+ const left = this.evalExpr(node.left);
153
+ const right = this.evalExpr(node.right);
154
+ switch (node.op) {
155
+ case '+': return left + right;
156
+ case '-': return left - right;
157
+ case '*': return left * right;
158
+ case '/': return left / right;
116
159
  }
160
+ }
161
+ if (node === 'print') {
162
+ console.log(this.evalExpr(node.args));
163
+ return null;
164
+ }
165
+ return null; // Étendre
166
+ }
117
167
 
118
- // Save
119
- if (line.startsWith('save ')) {
120
- const name = line.slice(5).trim();
121
- fs.writeFileSync(`${name}.json`, JSON.stringify(this.variables));
122
- console.log(`State saved to ${name}.json`);
123
- }
168
+ execute(stmt) {
169
+ if (stmt.type === 'assign') {
170
+ const value = this.evalExpr(stmt.value);
171
+ this.vars.set(stmt.name, { value, mut: stmt.mut, type: stmt.varType });
172
+ return value;
173
+ }
124
174
 
125
- // Load
126
- if (line.startsWith('load ')) {
127
- const name = line.slice(5).trim();
128
- if (fs.existsSync(`${name}.json`)) {
129
- this.variables = JSON.parse(fs.readFileSync(`${name}.json`, 'utf8'));
130
- console.log(`State loaded from ${name}.json`);
175
+ if (stmt.type === 'case') {
176
+ const exprValue = this.evalExpr(stmt.expr);
177
+ for (const c of stmt.cases) {
178
+ if (c.pattern === 'else' || this.matchPattern(exprValue, c.pattern)) {
179
+ return this.executeBlock(c.body);
131
180
  }
132
181
  }
182
+ return null;
183
+ }
133
184
 
134
- // Broadcast to Discord
135
- if (line.startsWith('broadcast to discord ')) {
136
- const [channelId, ...msgParts] = line.slice(20).trim().split(' ');
137
- const message = msgParts.join(' ');
138
- await this.sendToDiscord(channelId, this.evaluate(message));
139
- }
140
-
141
- // Territory
142
- if (line.startsWith('territory ')) {
143
- const [name, action, key, value] = line.slice(10).trim().split(' ');
144
- if (!this.variables[name]) this.variables[name] = {};
145
- if (action === 'add') this.variables[name][key] = this.evaluate(value);
146
- console.log(`Territory ${name} updated`);
147
- }
185
+ // Étendre pour fn def, etc.
186
+ }
148
187
 
149
- // ... (intègre le reste de la logique précédente pour intersect, risk, etc.)
188
+ matchPattern(value, pattern) {
189
+ // Implémentation basique : pour conditions > < ==, ou patterns {key: val}
190
+ return value === pattern; // Étendre
191
+ }
150
192
 
151
- i++;
193
+ executeBlock(block) {
194
+ let last = null;
195
+ for (const stmt of block) {
196
+ last = this.execute(stmt);
152
197
  }
153
- return output;
198
+ return last;
154
199
  }
155
200
 
156
- parseTime(timeStr) {
157
- const val = parseInt(timeStr.slice(0, -1));
158
- const unit = timeStr.slice(-1);
159
- return val * (unit === 's' ? 1000 : unit === 'm' ? 60000 : unit === 'h' ? 3600000 : 86400000); // s/m/h/d
160
- }
201
+ run(code) {
202
+ const lexResult = lexer.tokenize(code);
203
+ if (lexResult.errors.length > 0) {
204
+ console.error('Lexer errors:', lexResult.errors);
205
+ return;
206
+ }
207
+
208
+ const parser = new FazerParser();
209
+ parser.input = lexResult.tokens;
210
+ const cst = parser.program();
161
211
 
162
- async sendToDiscord(channelId, message) {
163
- if (!this.discordClient) {
164
- this.discordClient = new Client({ intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages] });
165
- await this.discordClient.login(process.env.DISCORD_TOKEN);
212
+ if (parser.errors.length > 0) {
213
+ console.error('Parse errors:', parser.errors);
214
+ return;
166
215
  }
167
- const channel = await this.discordClient.channels.fetch(channelId);
168
- await channel.send(message);
169
- }
170
216
 
171
- // ... (reste du code : evaluate, run, CLI)
217
+ this.executeBlock(cst);
218
+ }
172
219
  }
173
220
 
174
- // CLI reste pareil
175
- // ...
221
+ // ── CLI ──────────────────────────────────────────────────────────────────────
222
+ const args = process.argv.slice(2);
223
+
224
+ if (args[0] === 'run' && args[1]) {
225
+ const filePath = path.resolve(args[1]);
226
+ if (!fs.existsSync(filePath)) {
227
+ console.error(`Fichier non trouvé : ${filePath}`);
228
+ process.exit(1);
229
+ }
230
+ const code = fs.readFileSync(filePath, 'utf8');
231
+ new FazerRuntime().run(code);
232
+ } else {
233
+ console.log('Usage: fazer run fichier.fz');
234
+ }
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "fazer-lang",
3
- "version": "0.1.0",
4
- "description": "Fazer Language Un langage de programmation.",
3
+ "version": "1.1.0",
4
+ "description": "Fazer – Langage d'expression fluide : verbes contextuels, pipes →>, assignation :=, pattern matching via case",
5
5
  "main": "fazer.js",
6
6
  "bin": {
7
7
  "fazer": "fazer.js"
8
8
  },
9
9
  "dependencies": {
10
- "discord.js": "^14.14.1",
11
10
  "chevrotain": "^11.0.3"
12
11
  },
13
12
  "devDependencies": {
@@ -20,25 +19,9 @@
20
19
  "keywords": [
21
20
  "fazer",
22
21
  "programming-language",
23
- "street-script",
24
- "discord-bot",
25
- "simulation",
26
- "risk-based"
22
+ "scripting",
23
+ "expression-language"
27
24
  ],
28
- "author": "hm (Fazer City)",
29
- "license": "MIT",
30
- "files": [
31
- "fazer.js",
32
- "bin/",
33
- "README.md",
34
- "logo.png"
35
- ],
36
- "repository": {
37
- "type": "git",
38
- "url": "git+https://github.com/hmj34/fazer-lang.git"
39
- },
40
- "bugs": {
41
- "url": "https://github.com/hmj34/fazer-lang/issues"
42
- },
43
- "homepage": "https://github.com/hmj34/fazer-lang#readme"
25
+ "author": "hm",
26
+ "license": "MIT"
44
27
  }
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "fazer-lang",
3
+ "version": "0.1.0",
4
+ "description": "Fazer Language – Un langage de programmation.",
5
+ "main": "fazer.js",
6
+ "bin": {
7
+ "fazer": "fazer.js"
8
+ },
9
+ "dependencies": {
10
+ "discord.js": "^14.14.1",
11
+ "chevrotain": "^11.0.3"
12
+ },
13
+ "devDependencies": {
14
+ "pkg": "^5.8.1"
15
+ },
16
+ "scripts": {
17
+ "start": "node fazer.js",
18
+ "build": "pkg fazer.js -t node18-win-x64 -o bin/fazer.exe"
19
+ },
20
+ "keywords": [
21
+ "fazer",
22
+ "programming-language",
23
+ "street-script",
24
+ "discord-bot",
25
+ "simulation",
26
+ "risk-based"
27
+ ],
28
+ "author": "hm (Fazer City)",
29
+ "license": "MIT",
30
+ "files": [
31
+ "fazer.js",
32
+ "bin/",
33
+ "README.md",
34
+ "logo.png"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/hmj34/fazer-lang.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/hmj34/fazer-lang/issues"
42
+ },
43
+ "homepage": "https://github.com/hmj34/fazer-lang#readme"
44
+ }