thoth-markup-lang 10.5.0 → 12.0.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 +223 -23
- package/engine.php +287 -31
- package/package.json +20 -4
- package/thoth.js +367 -115
package/thoth.js
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* THOTH Engine
|
|
5
|
-
* Lead Developer: Engineer Abdelfatah Abdelhamed
|
|
4
|
+
* THOTH Engine v12.0.0 - The Sovereign Cloud (Intelligence Suite)
|
|
5
|
+
* Lead Developer & Architect: Engineer Abdelfatah Abdelhamed 🇪🇬
|
|
6
6
|
* ------------------------------------------------------------------
|
|
7
|
-
* الميزات
|
|
7
|
+
* الميزات المدمجة (النسخة النهائية):
|
|
8
8
|
* 1. Strict Identification: إلزامية وجود TYPE THOTH في السطر الأول.
|
|
9
9
|
* 2. Middleware Support: نظام Auth Guard المتقدم لتصفية العناصر.
|
|
10
10
|
* 3. Dynamic Meta Engine: توليد وسوم الميتا تلقائياً من سياق البيانات.
|
|
11
11
|
* 4. Dev Server & Live Reload: خادم محلي لمراقبة التغييرات لحظياً.
|
|
12
12
|
* 5. Recursive Data Engine: نظام الـ Loops والـ JSON والـ Variables.
|
|
13
|
+
* 6. Python-Style Traceback: متتبع أخطاء ذكي ودقيق جداً.
|
|
14
|
+
* 7. Ultra-Fast Parser: تسريع خوارزميات الـ Indentation.
|
|
15
|
+
* 8. Sovereign Components: القدرة على تعريف وإعادة استخدام المكونات.
|
|
16
|
+
* 9. State Management: حقن متغيرات الحالة لإنشاء واجهات تفاعلية.
|
|
17
|
+
* 10. Layout Engine: نظام شبكات مدمج (Row / Column) بخواص Flexbox.
|
|
18
|
+
* 11. Smart Routing: نظام تنقل داخلي مدمج لبناء تطبيقات SPA.
|
|
19
|
+
* 12. API Fetch Engine [NEW]: محرك لاتزامني (Async) لجلب بيانات الـ APIs.
|
|
13
20
|
*/
|
|
14
21
|
|
|
15
22
|
const fs = require('fs');
|
|
16
23
|
const path = require('path');
|
|
17
24
|
const http = require('http');
|
|
25
|
+
const https = require('https');
|
|
26
|
+
|
|
27
|
+
// --- كلاس تتبع الأخطاء الذكي (Python-Style Error Traceback) ---
|
|
28
|
+
class ThothSyntaxError extends Error {
|
|
29
|
+
constructor(message, lineNum, lineContent, filePath) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'ThothSyntaxError';
|
|
32
|
+
this.lineNum = lineNum;
|
|
33
|
+
this.lineContent = lineContent;
|
|
34
|
+
this.filePath = filePath;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
18
37
|
|
|
19
38
|
// --- القاموس المعماري الشامل ---
|
|
20
39
|
const TAG_MAP = {
|
|
@@ -23,17 +42,17 @@ const TAG_MAP = {
|
|
|
23
42
|
'Header': 'header', 'Footer': 'footer', 'Article': 'article', 'Aside': 'aside',
|
|
24
43
|
'Link': 'a', 'Image': 'img', 'Button': 'button', 'List': 'ul', 'Item': 'li',
|
|
25
44
|
'Input': 'input', 'Form': 'form', 'Label': 'label', 'Break': 'br', 'Line': 'hr',
|
|
26
|
-
'Main': 'main', 'Layout': 'section'
|
|
45
|
+
'Main': 'main', 'Layout': 'section',
|
|
46
|
+
'Row': 'div', 'Column': 'div'
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
const VOID_ELEMENTS = ['img', 'br', 'hr', 'input', 'meta', 'link'];
|
|
30
50
|
|
|
31
|
-
// --- سياق التشغيل السيادي ---
|
|
32
|
-
let
|
|
33
|
-
let config = {
|
|
51
|
+
// --- سياق التشغيل السيادي العام ---
|
|
52
|
+
let globalConfig = {
|
|
34
53
|
devMode: process.argv.includes('--dev') || process.argv.includes('serve'),
|
|
35
54
|
port: 3000,
|
|
36
|
-
currentUserRole: 'admin',
|
|
55
|
+
currentUserRole: 'admin',
|
|
37
56
|
strictMode: true
|
|
38
57
|
};
|
|
39
58
|
|
|
@@ -46,118 +65,243 @@ if (!command || (!target && command !== 'serve')) {
|
|
|
46
65
|
process.exit(1);
|
|
47
66
|
}
|
|
48
67
|
|
|
49
|
-
/**
|
|
50
|
-
* عرض إرشادات الاستخدام التقنية
|
|
51
|
-
*/
|
|
52
68
|
function displayUsage() {
|
|
53
69
|
const yellow = '\x1b[33m';
|
|
70
|
+
const cyan = '\x1b[36m';
|
|
54
71
|
const reset = '\x1b[0m';
|
|
55
72
|
console.log(yellow + '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + reset);
|
|
56
|
-
console.log('\x1b[1m\x1b[32m THOTH INTELLIGENCE SUITE
|
|
57
|
-
console.log(
|
|
73
|
+
console.log('\x1b[1m\x1b[32m THOTH INTELLIGENCE SUITE v12.0.0 (The Sovereign Cloud) \x1b[0m');
|
|
74
|
+
console.log(cyan + ' Lead Architect: Engineer Abdelfatah Abdelhamed' + reset);
|
|
58
75
|
console.log(yellow + '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + reset);
|
|
59
76
|
console.log(' Usage:');
|
|
60
77
|
console.log(' thoth build [file].thoth (Production Compilation)');
|
|
61
|
-
console.log(' thoth serve [file].thoth (Live
|
|
78
|
+
console.log(' thoth serve [file].thoth (Live SPA Server + Traceback)');
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
// --- محرك الاتصال الخارجي السيادي (متوافق مع كل نسخ Node) ---
|
|
82
|
+
function fetchJSON(url) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const client = url.startsWith('https') ? https : http;
|
|
85
|
+
client.get(url, { headers: { 'User-Agent': 'THOTH-Sovereign-Engine/12.0' } }, (res) => {
|
|
86
|
+
let data = '';
|
|
87
|
+
res.on('data', chunk => data += chunk);
|
|
88
|
+
res.on('end', () => {
|
|
89
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
90
|
+
try { resolve(JSON.parse(data)); }
|
|
91
|
+
catch (e) { reject(new Error('Invalid JSON response format.')); }
|
|
92
|
+
} else {
|
|
93
|
+
reject(new Error(`HTTP Error: ${res.statusCode}`));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}).on('error', reject);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const DATA_REGEX = /@\{\{([^}]+)\}\}/g;
|
|
67
101
|
function resolveData(text, context, variables = {}) {
|
|
68
|
-
// 1. استبدال المتغيرات @Var
|
|
69
102
|
let resolved = text;
|
|
70
103
|
for (const [key, val] of Object.entries(variables)) {
|
|
71
104
|
resolved = resolved.replace(new RegExp(key, 'g'), val);
|
|
72
105
|
}
|
|
73
|
-
|
|
74
|
-
return resolved.replace(/@\{\{([^}]+)\}\}/g, (match, key) => {
|
|
106
|
+
return resolved.replace(DATA_REGEX, (match, key) => {
|
|
75
107
|
const val = context[key.trim()];
|
|
76
108
|
return val !== undefined ? val : match;
|
|
77
109
|
});
|
|
78
110
|
}
|
|
79
111
|
|
|
112
|
+
function injectStateText(text, stateVars) {
|
|
113
|
+
let processedText = text;
|
|
114
|
+
for (const [key, val] of Object.entries(stateVars)) {
|
|
115
|
+
const token = `@{{${key}}}`;
|
|
116
|
+
if (processedText.includes(token)) {
|
|
117
|
+
processedText = processedText.replace(new RegExp(token, 'g'), `<span data-thoth-state="${key}">${val}</span>`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return processedText;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function injectStateBindings(attrs) {
|
|
124
|
+
const match = attrs.match(/action="([^"]+)"/);
|
|
125
|
+
if (match) {
|
|
126
|
+
const actionStr = match[1];
|
|
127
|
+
const jsAction = `thothState.dispatch('${actionStr}')`;
|
|
128
|
+
return attrs.replace(/action="[^"]+"/, `onclick="${jsAction}"`);
|
|
129
|
+
}
|
|
130
|
+
return attrs;
|
|
131
|
+
}
|
|
132
|
+
|
|
80
133
|
/**
|
|
81
|
-
*
|
|
134
|
+
* المحرك الأساسي (تحول إلى Async/Await لدعم السحابة السيادية)
|
|
82
135
|
*/
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const source = fs.readFileSync(inputPath, 'utf8');
|
|
87
|
-
const lines = source.split(/\r?\n/);
|
|
136
|
+
async function compileThothEngine(sourceContent, inputPath, isDev = false, isSubCall = false, inheritedContext = {}, inheritedComponents = {}, requestedRoute = '/') {
|
|
137
|
+
const lines = sourceContent.split(/\r?\n/);
|
|
88
138
|
|
|
89
|
-
// --- التحقق من شرط السيادة الأول ---
|
|
90
139
|
if (lines[0].trim() !== "TYPE THOTH") {
|
|
91
|
-
throw new
|
|
140
|
+
throw new ThothSyntaxError("Architectural Identity Error: Missing 'TYPE THOTH' protocol.", 1, lines[0], inputPath);
|
|
92
141
|
}
|
|
93
142
|
|
|
94
143
|
let htmlContent = "";
|
|
95
144
|
let stack = [];
|
|
96
|
-
|
|
145
|
+
|
|
146
|
+
let dataContext = { ...inheritedContext.dataContext };
|
|
147
|
+
let variables = { ...inheritedContext.variables };
|
|
148
|
+
let stateVars = { ...inheritedContext.stateVars };
|
|
149
|
+
let componentsContext = { ...inheritedComponents };
|
|
150
|
+
|
|
97
151
|
let rawScope = { active: false, type: '', indent: 0 };
|
|
98
152
|
let skipIndent = -1;
|
|
99
153
|
let repeatScope = { active: false, key: '', indent: 0, buffer: [] };
|
|
154
|
+
|
|
155
|
+
let componentScope = { active: false, name: '', indent: 0, buffer: [] };
|
|
100
156
|
|
|
101
|
-
for (let i = 1; i < lines.length; i++) {
|
|
157
|
+
for (let i = 1; i < lines.length; i++) {
|
|
158
|
+
const lineNum = i + 1;
|
|
102
159
|
let line = lines[i];
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
160
|
+
|
|
161
|
+
const trimmed = line.trimStart();
|
|
106
162
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
163
|
+
const indent = line.length - trimmed.length;
|
|
164
|
+
const fullyTrimmed = trimmed.trimEnd();
|
|
107
165
|
|
|
108
|
-
// --- 1. Middleware / Auth Guard ---
|
|
109
166
|
if (skipIndent !== -1) {
|
|
110
167
|
if (indent > skipIndent) continue;
|
|
111
168
|
else skipIndent = -1;
|
|
112
169
|
}
|
|
113
170
|
|
|
114
|
-
|
|
171
|
+
// --- 1. التنقل الذكي (Smart Routing) ---
|
|
172
|
+
if (fullyTrimmed.startsWith('Route "')) {
|
|
173
|
+
const match = fullyTrimmed.match(/Route "([^"]+)"/);
|
|
174
|
+
const routePath = match ? match[1] : '';
|
|
175
|
+
if (routePath !== requestedRoute) {
|
|
176
|
+
skipIndent = indent;
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- 2. Middleware / Auth Guard ---
|
|
182
|
+
const authMatch = fullyTrimmed.match(/Auth="([^"]+)"/);
|
|
115
183
|
if (authMatch) {
|
|
116
|
-
if (authMatch[1] !==
|
|
184
|
+
if (authMatch[1] !== globalConfig.currentUserRole && authMatch[1] !== 'guest') {
|
|
117
185
|
skipIndent = indent;
|
|
118
186
|
continue;
|
|
119
187
|
}
|
|
120
188
|
}
|
|
121
189
|
|
|
122
|
-
// ---
|
|
190
|
+
// --- 3. Sovereign Components (Define) ---
|
|
191
|
+
if (componentScope.active) {
|
|
192
|
+
if (indent > componentScope.indent) {
|
|
193
|
+
componentScope.buffer.push(line);
|
|
194
|
+
continue;
|
|
195
|
+
} else {
|
|
196
|
+
componentsContext[componentScope.name] = [...componentScope.buffer];
|
|
197
|
+
componentScope.active = false;
|
|
198
|
+
componentScope.buffer = [];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (fullyTrimmed.startsWith('Define ')) {
|
|
203
|
+
const colonIdx = fullyTrimmed.indexOf(':');
|
|
204
|
+
if (colonIdx === -1) throw new ThothSyntaxError("ComponentError: Missing ':' in Define statement.", lineNum, line, inputPath);
|
|
205
|
+
const compName = fullyTrimmed.substring(7, colonIdx).trim();
|
|
206
|
+
componentScope.name = compName;
|
|
207
|
+
componentScope.active = true;
|
|
208
|
+
componentScope.indent = indent;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// --- 4. State Management ---
|
|
213
|
+
if (fullyTrimmed.startsWith('State ')) {
|
|
214
|
+
const colonIdx = fullyTrimmed.indexOf(':');
|
|
215
|
+
if (colonIdx !== -1) {
|
|
216
|
+
const stateKey = fullyTrimmed.substring(6, colonIdx).trim();
|
|
217
|
+
const stateVal = fullyTrimmed.substring(colonIdx + 1).trim();
|
|
218
|
+
stateVars[stateKey] = stateVal;
|
|
219
|
+
variables[`@{{${stateKey}}}`] = stateVal;
|
|
220
|
+
} else {
|
|
221
|
+
throw new ThothSyntaxError(`StateError: Invalid State syntax. Expected 'State name: value'.`, lineNum, line, inputPath);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// --- 5. API Fetch Engine [NEW] ---
|
|
227
|
+
if (fullyTrimmed.startsWith('API ')) {
|
|
228
|
+
const match = fullyTrimmed.match(/API\s+([a-zA-Z0-9_]+)="([^"]+)"/);
|
|
229
|
+
if (match) {
|
|
230
|
+
const apiKey = match[1];
|
|
231
|
+
const apiUrl = match[2];
|
|
232
|
+
try {
|
|
233
|
+
dataContext[apiKey] = await fetchJSON(apiUrl);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
throw new ThothSyntaxError(`APIError: Failed to fetch external data from '${apiUrl}'. ${err.message}`, lineNum, line, inputPath);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
throw new ThothSyntaxError("APIError: Invalid API syntax. Expected 'API name=\"URL\"'.", lineNum, line, inputPath);
|
|
239
|
+
}
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// --- 6. Repeat Logic (Recursive & Async) ---
|
|
123
244
|
if (repeatScope.active) {
|
|
124
245
|
if (indent > repeatScope.indent) {
|
|
125
246
|
repeatScope.buffer.push(line);
|
|
126
247
|
continue;
|
|
127
248
|
} else {
|
|
128
249
|
const list = dataContext[repeatScope.key] || [];
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
250
|
+
if(!Array.isArray(list)) {
|
|
251
|
+
throw new ThothSyntaxError(`DataMappingError: '${repeatScope.key}' is not a valid Array in Context.`, lineNum, line, inputPath);
|
|
252
|
+
}
|
|
253
|
+
for (const item of list) {
|
|
254
|
+
const mergedContext = { dataContext: { ...dataContext, ...item }, variables, stateVars };
|
|
255
|
+
htmlContent += await processBuffer(repeatScope.buffer, mergedContext, componentsContext, inputPath, lineNum, requestedRoute);
|
|
256
|
+
}
|
|
132
257
|
repeatScope.active = false;
|
|
133
258
|
repeatScope.buffer = [];
|
|
134
259
|
}
|
|
135
260
|
}
|
|
136
261
|
|
|
137
|
-
if (
|
|
138
|
-
|
|
262
|
+
if (fullyTrimmed.startsWith('Repeat Over="')) {
|
|
263
|
+
const match = fullyTrimmed.match(/Over="([^"]+)"/);
|
|
264
|
+
if (!match) throw new ThothSyntaxError("LoopError: Invalid Repeat syntax.", lineNum, line, inputPath);
|
|
265
|
+
repeatScope.key = match[1];
|
|
139
266
|
repeatScope.active = true;
|
|
140
267
|
repeatScope.indent = indent;
|
|
141
268
|
continue;
|
|
142
269
|
}
|
|
143
270
|
|
|
144
|
-
// ---
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
147
|
-
|
|
271
|
+
// --- 7. DataSource & Variables ---
|
|
272
|
+
if (fullyTrimmed.startsWith('DataSource:')) {
|
|
273
|
+
const jsonFileName = fullyTrimmed.split(':')[1].trim();
|
|
274
|
+
const dPath = path.resolve(path.dirname(inputPath), jsonFileName);
|
|
275
|
+
if (!fs.existsSync(dPath)) throw new ThothSyntaxError(`MissingDataError: DataSource '${jsonFileName}' not found.`, lineNum, line, inputPath);
|
|
276
|
+
try {
|
|
277
|
+
const parsedData = JSON.parse(fs.readFileSync(dPath, 'utf8'));
|
|
278
|
+
dataContext = { ...dataContext, ...parsedData };
|
|
279
|
+
} catch(e) {
|
|
280
|
+
throw new ThothSyntaxError(`JSONParseError: Invalid JSON format in '${jsonFileName}'.`, lineNum, line, inputPath);
|
|
281
|
+
}
|
|
148
282
|
continue;
|
|
149
283
|
}
|
|
150
284
|
|
|
151
|
-
if (
|
|
152
|
-
const vParts =
|
|
285
|
+
if (fullyTrimmed.startsWith('Var ')) {
|
|
286
|
+
const vParts = fullyTrimmed.substring(4).split(':');
|
|
153
287
|
if (vParts.length === 2) variables['@' + vParts[0].trim()] = vParts[1].trim();
|
|
288
|
+
else throw new ThothSyntaxError(`VariableError: Invalid Var syntax.`, lineNum, line, inputPath);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (fullyTrimmed.startsWith('Import:')) {
|
|
293
|
+
const importFile = fullyTrimmed.substring(7).trim();
|
|
294
|
+
const importPath = path.resolve(path.dirname(inputPath), importFile);
|
|
295
|
+
if (!fs.existsSync(importPath)) throw new ThothSyntaxError(`ImportError: Module '${importFile}' not found.`, lineNum, line, inputPath);
|
|
296
|
+
const importContent = fs.readFileSync(importPath, 'utf8');
|
|
297
|
+
const subCall = await compileThothEngine(importContent, importPath, false, true, { dataContext, variables, stateVars }, componentsContext, requestedRoute);
|
|
298
|
+
htmlContent += subCall.html;
|
|
154
299
|
continue;
|
|
155
300
|
}
|
|
156
301
|
|
|
157
302
|
line = resolveData(line, dataContext, variables);
|
|
158
303
|
const processedTrimmed = line.trim();
|
|
159
304
|
|
|
160
|
-
// --- 4. Raw Code Injection (CSS/JS) ---
|
|
161
305
|
if (rawScope.active) {
|
|
162
306
|
if (indent <= rawScope.indent && processedTrimmed !== "") {
|
|
163
307
|
rawScope.active = false;
|
|
@@ -168,7 +312,6 @@ function compileThoth(inputPath, isDev = false) {
|
|
|
168
312
|
}
|
|
169
313
|
}
|
|
170
314
|
|
|
171
|
-
// --- خوارزمية تحليل الفصل الذكي ---
|
|
172
315
|
let colonIdx = -1;
|
|
173
316
|
let inQuotes = false;
|
|
174
317
|
for (let j = 0; j < processedTrimmed.length; j++) {
|
|
@@ -181,7 +324,7 @@ function compileThoth(inputPath, isDev = false) {
|
|
|
181
324
|
|
|
182
325
|
const firstSpace = instr.indexOf(' ');
|
|
183
326
|
const tagName = firstSpace !== -1 ? instr.substring(0, firstSpace) : instr;
|
|
184
|
-
|
|
327
|
+
let attrs = firstSpace !== -1 ? instr.substring(firstSpace).trim() : "";
|
|
185
328
|
|
|
186
329
|
if (tagName === "StyleSheet" || tagName === "Scripting") {
|
|
187
330
|
rawScope.active = true;
|
|
@@ -191,74 +334,134 @@ function compileThoth(inputPath, isDev = false) {
|
|
|
191
334
|
continue;
|
|
192
335
|
}
|
|
193
336
|
|
|
194
|
-
//
|
|
337
|
+
// --- 8. Component Instantiation ---
|
|
338
|
+
if (componentsContext[tagName]) {
|
|
339
|
+
let props = {};
|
|
340
|
+
const propMatches = attrs.matchAll(/([a-zA-Z0-9_]+)="([^"]+)"/g);
|
|
341
|
+
for (const match of propMatches) { props[`@${match[1]}`] = match[2]; }
|
|
342
|
+
|
|
343
|
+
let compSource = "TYPE THOTH\n";
|
|
344
|
+
componentsContext[tagName].forEach(l => { compSource += ' '.repeat(indent) + l + '\n'; });
|
|
345
|
+
|
|
346
|
+
const subMergedVars = { ...variables, ...props };
|
|
347
|
+
const subRes = await compileThothEngine(compSource, inputPath, false, true, { dataContext, variables: subMergedVars, stateVars }, componentsContext, requestedRoute);
|
|
348
|
+
htmlContent += subRes.html;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
|
|
195
352
|
while (stack.length > 0 && stack[stack.length - 1].indent >= indent) {
|
|
196
353
|
htmlContent += `</${stack.pop().name}>\n`;
|
|
197
354
|
}
|
|
198
355
|
|
|
199
356
|
const tag = TAG_MAP[tagName] || tagName.toLowerCase();
|
|
200
|
-
|
|
357
|
+
|
|
358
|
+
// --- 9. Layout Engine (Row & Column) ---
|
|
359
|
+
let injectedStyle = "";
|
|
360
|
+
if (tagName === 'Row') {
|
|
361
|
+
injectedStyle = "display:flex; flex-wrap:wrap;";
|
|
362
|
+
const gapMatch = attrs.match(/gap="([^"]+)"/);
|
|
363
|
+
if (gapMatch) injectedStyle += ` gap:${gapMatch[1]};`;
|
|
364
|
+
const alignMatch = attrs.match(/align="([^"]+)"/);
|
|
365
|
+
if (alignMatch) injectedStyle += ` align-items:${alignMatch[1]};`;
|
|
366
|
+
attrs = attrs.replace(/(gap|align)="[^"]+"/g, '');
|
|
367
|
+
} else if (tagName === 'Column') {
|
|
368
|
+
injectedStyle = "display:flex; flex-direction:column;";
|
|
369
|
+
const widthMatch = attrs.match(/width="([^"]+)"/);
|
|
370
|
+
if (widthMatch) injectedStyle += ` flex:0 0 ${widthMatch[1]}; max-width:${widthMatch[1]};`;
|
|
371
|
+
else injectedStyle += " flex:1;";
|
|
372
|
+
attrs = attrs.replace(/width="[^"]+"/g, '');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
let cleanAttrs = attrs.replace(/Auth="[^"]+"/, '').trim();
|
|
376
|
+
cleanAttrs = injectStateBindings(cleanAttrs);
|
|
377
|
+
|
|
378
|
+
if (injectedStyle !== "") {
|
|
379
|
+
if (cleanAttrs.includes('style="')) cleanAttrs = cleanAttrs.replace(/style="([^"]*)"/, `style="$1 ${injectedStyle.trim()}"`);
|
|
380
|
+
else cleanAttrs += ` style="${injectedStyle.trim()}"`;
|
|
381
|
+
}
|
|
382
|
+
|
|
201
383
|
const open = `<${tag}${cleanAttrs ? ' ' + cleanAttrs : ''}>`;
|
|
202
384
|
|
|
203
385
|
if (VOID_ELEMENTS.includes(tag)) {
|
|
204
|
-
htmlContent += open + '\n';
|
|
386
|
+
htmlContent += ' '.repeat(indent) + open + '\n';
|
|
205
387
|
} else if (val) {
|
|
206
|
-
|
|
388
|
+
const finalVal = injectStateText(val, stateVars);
|
|
389
|
+
htmlContent += ' '.repeat(indent) + `${open}${finalVal}</${tag}>\n`;
|
|
207
390
|
} else {
|
|
208
|
-
htmlContent += open + '\n';
|
|
391
|
+
htmlContent += ' '.repeat(indent) + open + '\n';
|
|
209
392
|
stack.push({ name: tag, indent: indent });
|
|
210
393
|
}
|
|
211
394
|
}
|
|
212
395
|
|
|
213
|
-
|
|
396
|
+
if (componentScope.active) componentsContext[componentScope.name] = [...componentScope.buffer];
|
|
214
397
|
if (repeatScope.active) {
|
|
215
398
|
const list = dataContext[repeatScope.key] || [];
|
|
216
|
-
|
|
399
|
+
for (const item of list) {
|
|
400
|
+
const mergedContext = { dataContext: { ...dataContext, ...item }, variables, stateVars };
|
|
401
|
+
htmlContent += await processBuffer(repeatScope.buffer, mergedContext, componentsContext, inputPath, lines.length, requestedRoute);
|
|
402
|
+
}
|
|
217
403
|
}
|
|
218
404
|
|
|
219
405
|
while (stack.length > 0) htmlContent += `</${stack.pop().name}>\n`;
|
|
220
406
|
|
|
221
|
-
return
|
|
407
|
+
if (isSubCall) return { html: htmlContent, dataContext, stateVars };
|
|
408
|
+
return generateBoilerplate(htmlContent, dataContext, stateVars, isDev);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* دالة التغليف العامة
|
|
413
|
+
*/
|
|
414
|
+
async function compileThoth(inputPath, isDev = false, requestedRoute = '/') {
|
|
415
|
+
if (!fs.existsSync(inputPath)) throw new ThothSyntaxError(`Source Integrity Error: File not found.`, 0, "", inputPath);
|
|
416
|
+
const source = fs.readFileSync(inputPath, 'utf8');
|
|
417
|
+
const result = await compileThothEngine(source, inputPath, isDev, false, {}, {}, requestedRoute);
|
|
418
|
+
return result.html;
|
|
222
419
|
}
|
|
223
420
|
|
|
224
421
|
/**
|
|
225
422
|
* معالجة الـ Buffer داخل الـ Loop
|
|
226
423
|
*/
|
|
227
|
-
function processBuffer(buffer,
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
let pLine = resolveData(l, item, variables);
|
|
233
|
-
const t = pLine.trim();
|
|
234
|
-
let cIdx = t.indexOf(':');
|
|
235
|
-
let cmdStr = cIdx !== -1 ? t.substring(0, cIdx).trim() : t;
|
|
236
|
-
let valStr = cIdx !== -1 ? t.substring(cIdx + 1).trim() : "";
|
|
237
|
-
|
|
238
|
-
const space = cmdStr.indexOf(' ');
|
|
239
|
-
const tag = TAG_MAP[cmdStr.substring(0, space === -1 ? cmdStr.length : space)] || cmdStr.toLowerCase();
|
|
240
|
-
|
|
241
|
-
while (bStack.length > 0 && bStack[bStack.length - 1].indent >= ind) res += `</${bStack.pop().name}>\n`;
|
|
242
|
-
|
|
243
|
-
const op = `<${tag}${space !== -1 ? ' ' + cmdStr.substring(space).trim() : ''}>`;
|
|
244
|
-
if (VOID_ELEMENTS.includes(tag)) res += op + '\n';
|
|
245
|
-
else if (valStr) res += `${op}${valStr}</${tag}>\n`;
|
|
246
|
-
else { res += op + '\n'; bStack.push({ name: tag, indent: ind }); }
|
|
247
|
-
});
|
|
248
|
-
while (bStack.length > 0) res += `</${bStack.pop().name}>\n`;
|
|
249
|
-
return res;
|
|
424
|
+
async function processBuffer(buffer, scopeContext, componentsContext, filePath, baseLineNum, requestedRoute) {
|
|
425
|
+
let tempSource = "TYPE THOTH\n";
|
|
426
|
+
buffer.forEach(l => tempSource += l + "\n");
|
|
427
|
+
const res = await compileThothEngine(tempSource, filePath, false, true, scopeContext, componentsContext, requestedRoute);
|
|
428
|
+
return res.html;
|
|
250
429
|
}
|
|
251
430
|
|
|
252
431
|
/**
|
|
253
|
-
* توليد هيكل HTML5 المعياري
|
|
432
|
+
* توليد هيكل HTML5 المعياري
|
|
254
433
|
*/
|
|
255
|
-
function generateBoilerplate(content, isDev) {
|
|
434
|
+
function generateBoilerplate(content, dataContext, stateVars, isDev) {
|
|
256
435
|
const title = dataContext.site_title || "THOTH Sovereign Architecture";
|
|
257
436
|
const desc = dataContext.site_desc || "Engineered by Abdelfatah Abdelhamed";
|
|
437
|
+
const stateJson = JSON.stringify(stateVars);
|
|
258
438
|
|
|
439
|
+
const stateEngineScript = `
|
|
440
|
+
<script>
|
|
441
|
+
const thothState = {
|
|
442
|
+
data: ${stateJson},
|
|
443
|
+
dispatch: function(actionStr) {
|
|
444
|
+
if(actionStr.startsWith('set:')) {
|
|
445
|
+
let logic = actionStr.replace('set:', '').split('=');
|
|
446
|
+
let key = logic[0]; let expr = logic[1];
|
|
447
|
+
if(expr.includes('+1')) this.data[key] = parseInt(this.data[key]) + 1;
|
|
448
|
+
else if(expr.includes('-1')) this.data[key] = parseInt(this.data[key]) - 1;
|
|
449
|
+
else this.data[key] = expr;
|
|
450
|
+
this.render();
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
render: function() {
|
|
454
|
+
document.querySelectorAll('[data-thoth-state]').forEach(el => {
|
|
455
|
+
let key = el.getAttribute('data-thoth-state');
|
|
456
|
+
if(this.data[key] !== undefined) el.innerText = this.data[key];
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
</script>`;
|
|
461
|
+
|
|
259
462
|
const liveReload = isDev ? `
|
|
260
463
|
<script>
|
|
261
|
-
console.log('%c THOTH DEV MODE: ACTIVE ', 'background:#
|
|
464
|
+
console.log('%c THOTH CLOUD DEV MODE: ACTIVE ', 'background:#06b6d4;color:#000;font-weight:bold;');
|
|
262
465
|
let lastMod = null;
|
|
263
466
|
setInterval(async () => {
|
|
264
467
|
try {
|
|
@@ -270,58 +473,107 @@ function generateBoilerplate(content, isDev) {
|
|
|
270
473
|
}, 1500);
|
|
271
474
|
</script>` : "";
|
|
272
475
|
|
|
273
|
-
return `<!DOCTYPE html>
|
|
476
|
+
return { html: `<!DOCTYPE html>
|
|
274
477
|
<html lang="ar" dir="rtl">
|
|
275
478
|
<head>
|
|
276
479
|
<meta charset="UTF-8">
|
|
277
480
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
278
481
|
<meta name="description" content="${desc}">
|
|
279
|
-
<meta name="generator" content="THOTH Sovereign Engine
|
|
280
|
-
<meta name="author" content="Abdelfatah Abdelhamed">
|
|
482
|
+
<meta name="generator" content="THOTH Sovereign Engine v12.0.0">
|
|
483
|
+
<meta name="author" content="Engineer Abdelfatah Abdelhamed">
|
|
281
484
|
<title>${title}</title>
|
|
282
485
|
</head>
|
|
283
|
-
<body style="margin:0; background:#
|
|
486
|
+
<body style="margin:0; background:#020617; color:#f8fafc; font-family:sans-serif;">
|
|
284
487
|
${content}
|
|
488
|
+
${stateEngineScript}
|
|
285
489
|
${liveReload}
|
|
286
490
|
</body>
|
|
287
|
-
</html
|
|
491
|
+
</html>`};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function getBrowserTracebackHTML(e) {
|
|
495
|
+
if (e.name === 'ThothSyntaxError') {
|
|
496
|
+
const escapedContent = e.lineContent.replace(/</g, '<').replace(/>/g, '>');
|
|
497
|
+
return `
|
|
498
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>THOTH Engine Error</title></head>
|
|
499
|
+
<body style="margin:0; background:#020617; font-family:'Fira Code', monospace; display:flex; align-items:center; justify-content:center; height:100vh;">
|
|
500
|
+
<div style="background:#0f172a; border:1px solid #ef4444; width:90%; max-width:800px; padding:30px; border-radius:12px; box-shadow:0 0 30px rgba(239, 68, 68, 0.2); color:#e2e8f0;">
|
|
501
|
+
<div style="border-bottom: 1px solid #334155; padding-bottom:15px; margin-bottom:20px;">
|
|
502
|
+
<h2 style="color:#ef4444; margin:0; font-size:1.5rem;">⚠️ THOTH Traceback (most recent call last):</h2>
|
|
503
|
+
</div>
|
|
504
|
+
<div style="padding: 10px 0;">
|
|
505
|
+
<p style="margin:0 0 10px 0; color:#94a3b8;">File <span style="color:#06b6d4;">"${e.filePath}"</span>, line <span style="color:#f59e0b; font-weight:bold;">${e.lineNum}</span>, in <module></p>
|
|
506
|
+
<div style="background:#000; padding:15px; border-radius:8px; border-left:4px solid #ef4444; overflow-x:auto;">
|
|
507
|
+
<div style="color:#f87171;">${e.lineNum} | <span style="color:#e2e8f0;">${escapedContent}</span></div>
|
|
508
|
+
<div style="color:#ef4444; margin-top:5px;"> ^</div>
|
|
509
|
+
</div>
|
|
510
|
+
<p style="margin:20px 0 0 0; color:#ef4444; font-weight:bold; font-size:1.1rem;">${e.message}</p>
|
|
511
|
+
</div>
|
|
512
|
+
<div style="border-top: 1px solid #334155; padding-top:15px; margin-top:20px; text-align:right;">
|
|
513
|
+
<button onclick="location.reload()" style="background:#ef4444; color:#fff; padding:8px 24px; border:none; border-radius:6px; cursor:pointer; font-weight:bold; font-family:inherit;">RESTART ENGINE</button>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
</body></html>`;
|
|
517
|
+
}
|
|
518
|
+
return `<body style="background:#000;color:#ff5555;padding:50px;font-family:monospace">
|
|
519
|
+
<h2 style="border-bottom:2px solid #ff5555;padding-bottom:10px">SOVEREIGN RUNTIME ERROR</h2>
|
|
520
|
+
<p style="font-size:18px">${e.message}</p>
|
|
521
|
+
<button onclick="location.reload()" style="background:#ff5555;color:#fff;border:none;padding:10px 20px;cursor:pointer">RETRY</button>
|
|
522
|
+
</body>`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function printTerminalError(e) {
|
|
526
|
+
if (e.name === 'ThothSyntaxError') {
|
|
527
|
+
console.error('\x1b[31m%s\x1b[0m', `\n⚠️ THOTH Traceback:`);
|
|
528
|
+
console.error('\x1b[90m%s\x1b[0m', ` File "${e.filePath}", line ${e.lineNum}, in <module>`);
|
|
529
|
+
console.error(` ${e.lineContent}`);
|
|
530
|
+
console.error('\x1b[31m%s\x1b[0m', ` ^`);
|
|
531
|
+
console.error('\x1b[1m\x1b[31m%s\x1b[0m', `${e.message}\n`);
|
|
532
|
+
} else {
|
|
533
|
+
console.error(`\x1b[31m [ERROR] ${e.message}\x1b[0m`);
|
|
534
|
+
}
|
|
288
535
|
}
|
|
289
536
|
|
|
290
537
|
// --- طبقة التنفيذ ---
|
|
291
538
|
if (command === 'serve') {
|
|
292
|
-
const server = http.createServer((req, res) => {
|
|
539
|
+
const server = http.createServer(async (req, res) => {
|
|
540
|
+
if (req.url === '/favicon.ico') { res.writeHead(204); return res.end(); }
|
|
541
|
+
|
|
293
542
|
try {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
543
|
+
const parsedUrl = new URL(req.url, `http://localhost:${globalConfig.port}`);
|
|
544
|
+
const requestedRoute = parsedUrl.pathname;
|
|
545
|
+
|
|
546
|
+
// استخدام await عشان المحرك يقدر يجلب بيانات الـ API
|
|
547
|
+
const html = await compileThoth(target, true, requestedRoute);
|
|
548
|
+
|
|
549
|
+
res.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-cache', 'ETag': Date.now().toString() });
|
|
300
550
|
res.end(html);
|
|
301
551
|
} catch (e) {
|
|
552
|
+
printTerminalError(e);
|
|
302
553
|
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
303
|
-
res.end(
|
|
304
|
-
<h2 style="border-bottom:2px solid #ff5555;padding-bottom:10px">SOVEREIGN COMPILATION ERROR</h2>
|
|
305
|
-
<p style="font-size:18px">${e.message}</p>
|
|
306
|
-
<button onclick="location.reload()" style="background:#ff5555;color:#fff;border:none;padding:10px 20px;cursor:pointer">RETRY</button>
|
|
307
|
-
</body>`);
|
|
554
|
+
res.end(getBrowserTracebackHTML(e));
|
|
308
555
|
}
|
|
309
556
|
});
|
|
310
557
|
|
|
311
|
-
server.listen(
|
|
312
|
-
console.log('\x1b[
|
|
313
|
-
console.log(` ✔ SERVER ACTIVE: http://localhost:${
|
|
558
|
+
server.listen(globalConfig.port, () => {
|
|
559
|
+
console.log('\x1b[36m%s\x1b[0m', `──────────────────────────────────────────`);
|
|
560
|
+
console.log(` ✔ SERVER ACTIVE: http://localhost:${globalConfig.port}`);
|
|
314
561
|
console.log(` ✔ ARCHITECTURE: ${target}`);
|
|
315
|
-
console.log(` ✔ MODE:
|
|
316
|
-
console.log('\x1b[
|
|
562
|
+
console.log(` ✔ MODE: The Sovereign Cloud v12.0.0`);
|
|
563
|
+
console.log('\x1b[36m%s\x1b[0m', `──────────────────────────────────────────`);
|
|
317
564
|
});
|
|
565
|
+
} else if (command === 'build') {
|
|
566
|
+
// التنفيذ غير المتزامن للبناء
|
|
567
|
+
(async () => {
|
|
568
|
+
try {
|
|
569
|
+
const out = await compileThoth(target, false);
|
|
570
|
+
fs.writeFileSync(target.replace('.thoth', '.html'), out);
|
|
571
|
+
console.log(`\x1b[32m [SUCCESS] Architecture materialized: ${target.replace('.thoth', '.html')}\x1b[0m`);
|
|
572
|
+
} catch (e) {
|
|
573
|
+
printTerminalError(e);
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
})();
|
|
318
577
|
} else {
|
|
319
|
-
|
|
320
|
-
const out = compileThoth(target, false);
|
|
321
|
-
fs.writeFileSync(target.replace('.thoth', '.html'), out);
|
|
322
|
-
console.log(`\x1b[32m [SUCCESS] Architecture materialized: ${target.replace('.thoth', '.html')}\x1b[0m`);
|
|
323
|
-
} catch (e) {
|
|
324
|
-
console.error(`\x1b[31m [ERROR] ${e.message}\x1b[0m`);
|
|
325
|
-
process.exit(1);
|
|
326
|
-
}
|
|
578
|
+
displayUsage();
|
|
327
579
|
}
|