raizcode-ofc 1.10.0 → 1.10.2
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 +648 -7
- package/compilador.js +773 -261
- package/package.json +2 -2
- package/exemplos/teste.js +0 -6
package/compilador.js
CHANGED
|
@@ -1,299 +1,727 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
2
3
|
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const os = require('os');
|
|
6
7
|
const http = require('http');
|
|
7
|
-
const
|
|
8
|
+
const Module = require('module');
|
|
9
|
+
const { spawn } = require('child_process');
|
|
8
10
|
|
|
11
|
+
const VERSAO_INTERNA = '1.10.1';
|
|
9
12
|
const args = process.argv.slice(2);
|
|
13
|
+
const flags = new Set(args.filter((arg) => arg.startsWith('--')));
|
|
14
|
+
const posicionais = args.filter((arg) => !arg.startsWith('--'));
|
|
10
15
|
const arquivoOuCmd = args[0];
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
class ErroRaizcode extends Error {
|
|
18
|
+
constructor(mensagem, arquivo, numeroLinha, codigoLinha = '') {
|
|
19
|
+
super(mensagem);
|
|
20
|
+
this.name = 'ErroRaizcode';
|
|
21
|
+
this.arquivo = arquivo;
|
|
22
|
+
this.numeroLinha = numeroLinha;
|
|
23
|
+
this.codigoLinha = codigoLinha;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function obterVersao() {
|
|
28
|
+
return VERSAO_INTERNA;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function estaNoTermux() {
|
|
32
|
+
return Boolean(
|
|
33
|
+
process.env.TERMUX_VERSION ||
|
|
34
|
+
process.env.PREFIX?.includes('com.termux') ||
|
|
35
|
+
fs.existsSync('/data/data/com.termux')
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function copiarRecursivo(origem, destino) {
|
|
40
|
+
if (typeof fs.cpSync === 'function') {
|
|
41
|
+
fs.cpSync(origem, destino, { recursive: true });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const status = fs.statSync(origem);
|
|
46
|
+
if (status.isDirectory()) {
|
|
47
|
+
fs.mkdirSync(destino, { recursive: true });
|
|
48
|
+
for (const item of fs.readdirSync(origem)) {
|
|
49
|
+
copiarRecursivo(path.join(origem, item), path.join(destino, item));
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fs.copyFileSync(origem, destino);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function encontrarPastaExtensoes() {
|
|
58
|
+
const home = os.homedir();
|
|
59
|
+
const candidatos = [
|
|
60
|
+
process.env.VSCODE_EXTENSIONS,
|
|
61
|
+
path.join(home, '.local', 'share', 'code-server', 'extensions'),
|
|
62
|
+
path.join(home, '.vscode-server', 'extensions'),
|
|
63
|
+
path.join(home, '.vscode', 'extensions')
|
|
64
|
+
].filter(Boolean);
|
|
65
|
+
|
|
66
|
+
const existente = candidatos.find((pasta) => fs.existsSync(path.dirname(pasta)) || fs.existsSync(pasta));
|
|
67
|
+
return existente || path.join(home, '.vscode', 'extensions');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function instalarExtensaoVSCode() {
|
|
71
|
+
const extDir = path.join(encontrarPastaExtensoes(), `riquefla.raizcode-${obterVersao()}`);
|
|
72
|
+
|
|
17
73
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
74
|
+
fs.mkdirSync(extDir, { recursive: true });
|
|
75
|
+
|
|
76
|
+
for (const pasta of ['syntaxes', 'icons']) {
|
|
77
|
+
const origem = path.join(__dirname, pasta);
|
|
78
|
+
const destino = path.join(extDir, pasta);
|
|
79
|
+
if (fs.existsSync(origem)) copiarRecursivo(origem, destino);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const gramaticaRC = './syntaxes/raizcode.tmLanguage.json';
|
|
83
|
+
const gramaticaRCX = './syntaxes/raizcode-estrutura.tmLanguage.json';
|
|
84
|
+
const gramaticaRCC = './syntaxes/raizcode-estilo.tmLanguage.json';
|
|
85
|
+
|
|
86
|
+
const grammars = [];
|
|
87
|
+
if (fs.existsSync(path.join(extDir, gramaticaRC))) {
|
|
88
|
+
grammars.push({ language: 'raizcode', scopeName: 'source.rc', path: gramaticaRC });
|
|
89
|
+
}
|
|
90
|
+
if (fs.existsSync(path.join(extDir, gramaticaRCX))) {
|
|
91
|
+
grammars.push({ language: 'raizcode-estrutura', scopeName: 'source.rcx', path: gramaticaRCX });
|
|
92
|
+
}
|
|
93
|
+
if (fs.existsSync(path.join(extDir, gramaticaRCC))) {
|
|
94
|
+
grammars.push({ language: 'raizcode-estilo', scopeName: 'source.rcc', path: gramaticaRCC });
|
|
95
|
+
}
|
|
28
96
|
|
|
29
|
-
// Gera o package.json da extensão VSCode com suporte a Temas de Ícones
|
|
30
97
|
const pkgExtensao = {
|
|
31
|
-
name:
|
|
32
|
-
displayName:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
98
|
+
name: 'raizcode-extension',
|
|
99
|
+
displayName: 'Raizcode',
|
|
100
|
+
description: 'Suporte aos arquivos .rc, .rcx e .rcc',
|
|
101
|
+
version: obterVersao(),
|
|
102
|
+
publisher: 'riquefla',
|
|
103
|
+
engines: { vscode: '^1.60.0' },
|
|
104
|
+
categories: ['Programming Languages'],
|
|
36
105
|
contributes: {
|
|
37
106
|
languages: [
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
extensions: [".rc"]
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
id: "raizcode-estrutura",
|
|
45
|
-
aliases: ["Raizcode Estrutura", "rcx"],
|
|
46
|
-
extensions: [".rcx"]
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: "raizcode-estilo",
|
|
50
|
-
aliases: ["Raizcode Estilo", "rcc"],
|
|
51
|
-
extensions: [".rcc"]
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
grammars: [
|
|
55
|
-
{
|
|
56
|
-
language: "raizcode",
|
|
57
|
-
scopeName: "source.rc",
|
|
58
|
-
path: "./syntaxes/raizcode.tmLanguage.json"
|
|
59
|
-
}
|
|
107
|
+
{ id: 'raizcode', aliases: ['Raizcode', 'rc'], extensions: ['.rc'] },
|
|
108
|
+
{ id: 'raizcode-estrutura', aliases: ['Raizcode Estrutura', 'rcx'], extensions: ['.rcx'] },
|
|
109
|
+
{ id: 'raizcode-estilo', aliases: ['Raizcode Estilo', 'rcc'], extensions: ['.rcc'] }
|
|
60
110
|
],
|
|
61
|
-
|
|
62
|
-
iconThemes:
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
label: "Raizcode Icons",
|
|
66
|
-
path: "./icons/raizcode-icon-theme.json"
|
|
67
|
-
}
|
|
68
|
-
]
|
|
111
|
+
grammars,
|
|
112
|
+
iconThemes: fs.existsSync(path.join(extDir, 'icons', 'raizcode-icon-theme.json'))
|
|
113
|
+
? [{ id: 'raizcode-icons', label: 'Raizcode Icons', path: './icons/raizcode-icon-theme.json' }]
|
|
114
|
+
: []
|
|
69
115
|
}
|
|
70
116
|
};
|
|
71
117
|
|
|
72
|
-
fs.writeFileSync(
|
|
73
|
-
path.join(extDir, 'package.json'),
|
|
74
|
-
JSON.stringify(pkgExtensao, null, 2)
|
|
75
|
-
);
|
|
118
|
+
fs.writeFileSync(path.join(extDir, 'package.json'), JSON.stringify(pkgExtensao, null, 2));
|
|
76
119
|
|
|
77
|
-
console.log(
|
|
78
|
-
console.log(
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
console.log('✅ Extensão Raizcode instalada.');
|
|
121
|
+
console.log(`📁 Local: ${extDir}`);
|
|
122
|
+
if (estaNoTermux()) {
|
|
123
|
+
console.log('💡 No Termux, use code-server ou um editor compatível com extensões do VS Code.');
|
|
124
|
+
} else {
|
|
125
|
+
console.log('💡 Reinicie o VS Code e selecione o tema "Raizcode Icons", caso ele esteja disponível.');
|
|
126
|
+
}
|
|
127
|
+
} catch (erro) {
|
|
128
|
+
console.error(`❌ Não foi possível instalar a extensão: ${erro.message}`);
|
|
129
|
+
process.exitCode = 1;
|
|
81
130
|
}
|
|
82
|
-
process.exit();
|
|
83
131
|
}
|
|
84
132
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
133
|
+
function mostrarAjuda() {
|
|
134
|
+
console.log(`🌱 Raizcode v${obterVersao()} — A linguagem brasileira\n`);
|
|
135
|
+
console.log('Uso:');
|
|
136
|
+
console.log(' raizcode arquivo.rc');
|
|
137
|
+
console.log(' raizcode arquivo.rc 3000');
|
|
138
|
+
console.log(' raizcode arquivo.rc --no-open');
|
|
139
|
+
console.log(' raizcode --setup');
|
|
140
|
+
console.log(' raizcode --version');
|
|
141
|
+
console.log(' raizcode --help');
|
|
142
|
+
console.log('\nOpções:');
|
|
143
|
+
console.log(' --no-open Não abre o navegador automaticamente.');
|
|
144
|
+
console.log(' --permitir-js Permite JavaScript puro sem o prefixo "js".');
|
|
145
|
+
console.log(' --estrito Recusa comandos desconhecidos.');
|
|
146
|
+
}
|
|
88
147
|
|
|
89
|
-
// Abre o navegador de forma correta em qualquer sistema
|
|
90
148
|
function abrirNavegador(url) {
|
|
91
149
|
const plataforma = os.platform();
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
150
|
+
let comandos;
|
|
151
|
+
|
|
152
|
+
if (plataforma === 'win32') {
|
|
153
|
+
comandos = [['cmd', ['/c', 'start', '', url]]];
|
|
154
|
+
} else if (plataforma === 'darwin') {
|
|
155
|
+
comandos = [['open', [url]]];
|
|
156
|
+
} else if (estaNoTermux()) {
|
|
157
|
+
comandos = [
|
|
158
|
+
['termux-open-url', [url]],
|
|
159
|
+
['termux-open', [url]]
|
|
160
|
+
];
|
|
161
|
+
} else {
|
|
162
|
+
comandos = [['xdg-open', [url]]];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const tentar = (indice) => {
|
|
166
|
+
if (indice >= comandos.length) {
|
|
167
|
+
console.log(`ℹ️ Abra manualmente no navegador: ${url}`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const [comando, argumentos] = comandos[indice];
|
|
172
|
+
let iniciou = false;
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const processo = spawn(comando, argumentos, {
|
|
176
|
+
detached: true,
|
|
177
|
+
stdio: 'ignore'
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
processo.once('spawn', () => {
|
|
181
|
+
iniciou = true;
|
|
182
|
+
processo.unref();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
processo.once('error', () => {
|
|
186
|
+
if (!iniciou) tentar(indice + 1);
|
|
187
|
+
});
|
|
188
|
+
} catch (_) {
|
|
189
|
+
tentar(indice + 1);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
tentar(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function criarErro(mensagem, arquivo, numeroLinha, codigoLinha = '') {
|
|
197
|
+
throw new ErroRaizcode(mensagem, arquivo, numeroLinha, codigoLinha);
|
|
96
198
|
}
|
|
97
199
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
200
|
+
function escaparHTML(valor) {
|
|
201
|
+
return String(valor)
|
|
202
|
+
.replace(/&/g, '&')
|
|
203
|
+
.replace(/</g, '<')
|
|
204
|
+
.replace(/>/g, '>')
|
|
205
|
+
.replace(/"/g, '"')
|
|
206
|
+
.replace(/'/g, ''');
|
|
103
207
|
}
|
|
104
208
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
209
|
+
function escaparAtributo(valor) {
|
|
210
|
+
return escaparHTML(valor).replace(/`/g, '`');
|
|
211
|
+
}
|
|
108
212
|
|
|
109
|
-
function
|
|
110
|
-
|
|
213
|
+
function substituirForaDeStrings(texto, transformador) {
|
|
214
|
+
let resultado = '';
|
|
215
|
+
let trecho = '';
|
|
216
|
+
let aspas = null;
|
|
217
|
+
let escapado = false;
|
|
111
218
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
219
|
+
const descarregar = () => {
|
|
220
|
+
if (!trecho) return;
|
|
221
|
+
resultado += aspas ? trecho : transformador(trecho);
|
|
222
|
+
trecho = '';
|
|
223
|
+
};
|
|
115
224
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
225
|
+
for (const caractere of texto) {
|
|
226
|
+
trecho += caractere;
|
|
227
|
+
|
|
228
|
+
if (escapado) {
|
|
229
|
+
escapado = false;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (caractere === '\\') {
|
|
234
|
+
escapado = true;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (aspas) {
|
|
239
|
+
if (caractere === aspas) {
|
|
240
|
+
descarregar();
|
|
241
|
+
aspas = null;
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
121
245
|
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
246
|
+
if (caractere === '"' || caractere === "'" || caractere === '`') {
|
|
247
|
+
trecho = trecho.slice(0, -1);
|
|
248
|
+
descarregar();
|
|
249
|
+
aspas = caractere;
|
|
250
|
+
trecho = caractere;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
descarregar();
|
|
255
|
+
return resultado;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function traduzirExpressao(expressao) {
|
|
259
|
+
return substituirForaDeStrings(expressao, (trecho) => trecho
|
|
260
|
+
.replace(/\bverdadeiro\b/g, 'true')
|
|
261
|
+
.replace(/\bfalso\b/g, 'false')
|
|
262
|
+
.replace(/\bnulo\b/g, 'null')
|
|
263
|
+
.replace(/\bnao\b/g, '!')
|
|
264
|
+
.replace(/\be\b/g, '&&')
|
|
265
|
+
.replace(/\bou\b/g, '||'));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function extrairTextoEntreAspas(linha, comando, arquivo, numeroLinha) {
|
|
269
|
+
const padrao = new RegExp(`^${comando}\\s+"([\\s\\S]*)"$`);
|
|
270
|
+
const resultado = linha.match(padrao);
|
|
271
|
+
if (!resultado) {
|
|
272
|
+
criarErro(`Sintaxe inválida. Use: ${comando} "texto"`, arquivo, numeroLinha, linha);
|
|
273
|
+
}
|
|
274
|
+
return resultado[1];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function traduzirEstrutura(conteudo, arquivo) {
|
|
278
|
+
if (!conteudo) return '';
|
|
279
|
+
|
|
280
|
+
const linhas = conteudo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
|
|
281
|
+
const pilha = [];
|
|
282
|
+
const saida = [];
|
|
283
|
+
|
|
284
|
+
linhas.forEach((linhaOriginal, indice) => {
|
|
285
|
+
const numeroLinha = indice + 1;
|
|
286
|
+
const linha = linhaOriginal.trim();
|
|
287
|
+
|
|
288
|
+
if (!linha || linha.startsWith('//')) {
|
|
289
|
+
saida.push('');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (linha.startsWith('titulo ')) {
|
|
294
|
+
saida.push(`<h1>${escaparHTML(extrairTextoEntreAspas(linha, 'titulo', arquivo, numeroLinha))}</h1>`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (linha.startsWith('subtitulo ')) {
|
|
299
|
+
saida.push(`<h2>${escaparHTML(extrairTextoEntreAspas(linha, 'subtitulo', arquivo, numeroLinha))}</h2>`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (linha.startsWith('texto ')) {
|
|
304
|
+
saida.push(`<p>${escaparHTML(extrairTextoEntreAspas(linha, 'texto', arquivo, numeroLinha))}</p>`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (linha.startsWith('link ')) {
|
|
309
|
+
const resultado = linha.match(/^link\s+"(.+?)"\s+url\s+"(.+?)"$/);
|
|
310
|
+
if (!resultado) criarErro('Use: link "Texto" url "https://exemplo.com"', arquivo, numeroLinha, linhaOriginal);
|
|
311
|
+
saida.push(`<a href="${escaparAtributo(resultado[2])}" target="_blank" rel="noopener noreferrer">${escaparHTML(resultado[1])}</a>`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (linha.startsWith('imagem ')) {
|
|
316
|
+
const endereco = extrairTextoEntreAspas(linha, 'imagem', arquivo, numeroLinha);
|
|
317
|
+
saida.push(`<img src="${escaparAtributo(endereco)}" alt="imagem" style="max-width:100%;">`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (linha.startsWith('botao ')) {
|
|
322
|
+
const comId = linha.match(/^botao\s+"(.+?)"\s+id\s+"([A-Za-z][\w:-]*)"$/);
|
|
323
|
+
const semId = linha.match(/^botao\s+"(.+?)"$/);
|
|
324
|
+
if (comId) {
|
|
325
|
+
saida.push(`<button id="${escaparAtributo(comId[2])}">${escaparHTML(comId[1])}</button>`);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (semId) {
|
|
329
|
+
saida.push(`<button>${escaparHTML(semId[1])}</button>`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
criarErro('Use: botao "Texto" id "meuBotao"', arquivo, numeroLinha, linhaOriginal);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (linha.startsWith('entrada ')) {
|
|
336
|
+
const resultado = linha.match(/^entrada\s+id\s+"([A-Za-z][\w:-]*)"\s+dica\s+"(.+?)"$/);
|
|
337
|
+
if (!resultado) criarErro('Use: entrada id "nome" dica "Digite seu nome"', arquivo, numeroLinha, linhaOriginal);
|
|
338
|
+
saida.push(`<input id="${escaparAtributo(resultado[1])}" placeholder="${escaparAtributo(resultado[2])}">`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (linha.startsWith('caixa ')) {
|
|
343
|
+
const classe = extrairTextoEntreAspas(linha, 'caixa', arquivo, numeroLinha);
|
|
344
|
+
pilha.push({ tipo: 'caixa', linha: numeroLinha });
|
|
345
|
+
saida.push(`<div class="${escaparAtributo(classe)}">`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (linha === 'fim') {
|
|
350
|
+
if (pilha.length === 0) criarErro('Existe um "fim" sem uma caixa aberta.', arquivo, numeroLinha, linhaOriginal);
|
|
351
|
+
pilha.pop();
|
|
352
|
+
saida.push('</div>');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
criarErro(`Comando de estrutura desconhecido: "${linha}"`, arquivo, numeroLinha, linhaOriginal);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (pilha.length > 0) {
|
|
360
|
+
const aberto = pilha[pilha.length - 1];
|
|
361
|
+
criarErro(`A caixa aberta na linha ${aberto.linha} não foi fechada com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return saida.join('\n');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const PROPRIEDADES_RCC = {
|
|
368
|
+
'fundo': 'background',
|
|
369
|
+
'cor-texto': 'color',
|
|
370
|
+
'tamanho-texto': 'font-size',
|
|
371
|
+
'familia-fonte': 'font-family',
|
|
372
|
+
'peso-fonte': 'font-weight',
|
|
373
|
+
'margem': 'margin',
|
|
374
|
+
'margem-interna': 'padding',
|
|
375
|
+
'borda': 'border',
|
|
376
|
+
'arredondado': 'border-radius',
|
|
377
|
+
'largura': 'width',
|
|
378
|
+
'altura': 'height',
|
|
379
|
+
'largura-maxima': 'max-width',
|
|
380
|
+
'altura-minima': 'min-height',
|
|
381
|
+
'exibir': 'display',
|
|
382
|
+
'alinhar-itens': 'align-items',
|
|
383
|
+
'justificar': 'justify-content',
|
|
384
|
+
'direcao': 'flex-direction',
|
|
385
|
+
'espaco': 'gap',
|
|
386
|
+
'sombra': 'box-shadow',
|
|
387
|
+
'cursor': 'cursor',
|
|
388
|
+
'opacidade': 'opacity',
|
|
389
|
+
'transicao': 'transition',
|
|
390
|
+
'posicao': 'position',
|
|
391
|
+
'topo': 'top',
|
|
392
|
+
'direita': 'right',
|
|
393
|
+
'baixo': 'bottom',
|
|
394
|
+
'esquerda': 'left'
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const VALORES_RCC = {
|
|
398
|
+
'preto': '#000000',
|
|
399
|
+
'branco': '#ffffff',
|
|
400
|
+
'cinza': '#808080',
|
|
401
|
+
'vermelho': '#ff0000',
|
|
402
|
+
'verde': '#00a650',
|
|
403
|
+
'azul': '#0066ff',
|
|
404
|
+
'transparente': 'transparent',
|
|
405
|
+
'verde-neon': '#00ff88',
|
|
406
|
+
'amarelo-neon': '#ffff00',
|
|
407
|
+
'azul-neon': '#00cfff',
|
|
408
|
+
'vermelho-neon': '#ff4444'
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
function traduzirSeletor(texto, arquivo, numeroLinha, linhaOriginal) {
|
|
412
|
+
const seletor = texto.trim();
|
|
413
|
+
if (!seletor) criarErro('Informe um seletor depois de "estilo".', arquivo, numeroLinha, linhaOriginal);
|
|
414
|
+
|
|
415
|
+
if (seletor === 'corpo') return 'body';
|
|
416
|
+
if (seletor === 'raiz') return '#raiz-app';
|
|
417
|
+
if (seletor.startsWith('classe ')) return `.${seletor.slice(7).trim()}`;
|
|
418
|
+
if (seletor.startsWith('id ')) return `#${seletor.slice(3).trim()}`;
|
|
419
|
+
if (seletor.startsWith('elemento ')) return seletor.slice(9).trim();
|
|
420
|
+
if (/^[.#[:*]/.test(seletor)) return seletor;
|
|
421
|
+
return `.${seletor}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function traduzirValorCSS(valor) {
|
|
425
|
+
let resultado = valor.trim();
|
|
426
|
+
for (const [portugues, css] of Object.entries(VALORES_RCC)) {
|
|
427
|
+
resultado = resultado.replace(new RegExp(`\\b${portugues}\\b`, 'g'), css);
|
|
428
|
+
}
|
|
429
|
+
return resultado;
|
|
146
430
|
}
|
|
147
431
|
|
|
148
|
-
function traduzirEstilo(conteudo) {
|
|
149
|
-
if (!conteudo) return
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
432
|
+
function traduzirEstilo(conteudo, arquivo) {
|
|
433
|
+
if (!conteudo) return '';
|
|
434
|
+
|
|
435
|
+
const linhas = conteudo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
|
|
436
|
+
const pilha = [];
|
|
437
|
+
const saida = [];
|
|
438
|
+
|
|
439
|
+
linhas.forEach((linhaOriginal, indice) => {
|
|
440
|
+
const numeroLinha = indice + 1;
|
|
441
|
+
const linha = linhaOriginal.trim();
|
|
442
|
+
|
|
443
|
+
if (!linha || linha.startsWith('//')) {
|
|
444
|
+
saida.push('');
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (linha.startsWith('estilo ')) {
|
|
449
|
+
const seletor = traduzirSeletor(linha.slice(7), arquivo, numeroLinha, linhaOriginal);
|
|
450
|
+
pilha.push({ seletor, linha: numeroLinha });
|
|
451
|
+
saida.push(`${seletor} {`);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (linha.startsWith('ao-passar-mouse ')) {
|
|
456
|
+
const seletor = traduzirSeletor(linha.slice('ao-passar-mouse '.length), arquivo, numeroLinha, linhaOriginal);
|
|
457
|
+
pilha.push({ seletor: `${seletor}:hover`, linha: numeroLinha });
|
|
458
|
+
saida.push(`${seletor}:hover {`);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (linha === 'fim') {
|
|
463
|
+
if (pilha.length === 0) criarErro('Existe um "fim" sem um estilo aberto.', arquivo, numeroLinha, linhaOriginal);
|
|
464
|
+
pilha.pop();
|
|
465
|
+
saida.push('}');
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (pilha.length === 0) {
|
|
470
|
+
criarErro('Propriedade encontrada fora de um bloco "estilo".', arquivo, numeroLinha, linhaOriginal);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const propriedade = linha.match(/^([\w-]+)\s*:\s*(.+?);?$/);
|
|
474
|
+
if (!propriedade) criarErro('Use o formato: propriedade: valor', arquivo, numeroLinha, linhaOriginal);
|
|
475
|
+
|
|
476
|
+
const nome = PROPRIEDADES_RCC[propriedade[1]] || propriedade[1];
|
|
477
|
+
const valor = traduzirValorCSS(propriedade[2].replace(/;$/, ''));
|
|
478
|
+
saida.push(` ${nome}: ${valor};`);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (pilha.length > 0) {
|
|
482
|
+
const aberto = pilha[pilha.length - 1];
|
|
483
|
+
criarErro(`O estilo aberto na linha ${aberto.linha} não foi fechado com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return saida.join('\n');
|
|
164
487
|
}
|
|
165
488
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
489
|
+
function validarBlocos(linhas, arquivo) {
|
|
490
|
+
const pilha = [];
|
|
491
|
+
|
|
492
|
+
linhas.forEach((linhaOriginal, indice) => {
|
|
493
|
+
const numeroLinha = indice + 1;
|
|
494
|
+
const linha = linhaOriginal.trim();
|
|
495
|
+
if (!linha || linha.startsWith('//')) return;
|
|
496
|
+
|
|
497
|
+
let tipo = null;
|
|
498
|
+
if (linha.startsWith('se ')) tipo = 'se';
|
|
499
|
+
else if (linha.startsWith('enquanto ')) tipo = 'enquanto';
|
|
500
|
+
else if (linha.startsWith('funcao ')) tipo = 'funcao';
|
|
501
|
+
else if (linha.startsWith('repetir ')) tipo = 'repetir';
|
|
502
|
+
else if (linha.startsWith('para cada ')) tipo = 'para';
|
|
503
|
+
else if (linha.startsWith('ao_clicar ')) tipo = 'evento';
|
|
504
|
+
|
|
505
|
+
if (tipo) {
|
|
506
|
+
pilha.push({ tipo, linha: numeroLinha });
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (linha === 'senao' || linha.startsWith('senaose ')) {
|
|
511
|
+
const atual = pilha[pilha.length - 1];
|
|
512
|
+
if (!atual || atual.tipo !== 'se') {
|
|
513
|
+
criarErro(`"${linha.split(' ')[0]}" só pode ser usado dentro de um bloco "se".`, arquivo, numeroLinha, linhaOriginal);
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (linha === 'fim') {
|
|
519
|
+
if (pilha.length === 0) criarErro('Existe um "fim" sem bloco aberto.', arquivo, numeroLinha, linhaOriginal);
|
|
520
|
+
pilha.pop();
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
if (pilha.length > 0) {
|
|
525
|
+
const aberto = pilha[pilha.length - 1];
|
|
526
|
+
criarErro(`O bloco "${aberto.tipo}" aberto na linha ${aberto.linha} não foi fechado com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
|
|
527
|
+
}
|
|
528
|
+
}
|
|
169
529
|
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
if (!
|
|
530
|
+
function traduzirLinha(linhaOriginal, numeroLinha, contexto) {
|
|
531
|
+
const linha = linhaOriginal.trim();
|
|
532
|
+
if (!linha || linha.startsWith('//')) return linhaOriginal;
|
|
173
533
|
|
|
174
|
-
|
|
175
|
-
if (t.startsWith('pagina ')) {
|
|
534
|
+
if (linha.startsWith('pagina ')) {
|
|
176
535
|
contexto.ehSite = true;
|
|
177
|
-
const
|
|
536
|
+
const valor = linha.slice(7).trim();
|
|
537
|
+
const titulo = /^(["'`]).*\1$/.test(valor) ? traduzirExpressao(valor) : JSON.stringify(valor);
|
|
178
538
|
return `document.title = ${titulo};`;
|
|
179
539
|
}
|
|
180
540
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
541
|
+
if (linha.startsWith('mostrar_tela ')) {
|
|
542
|
+
contexto.ehSite = true;
|
|
543
|
+
const resultado = linha.match(/^mostrar_tela\s+(.+)\s+em\s+"([A-Za-z][\w:-]*)"$/);
|
|
544
|
+
if (!resultado) criarErro('Use: mostrar_tela "Texto" em "elementoId"', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
545
|
+
const valor = traduzirExpressao(resultado[1]);
|
|
546
|
+
const id = resultado[2];
|
|
547
|
+
return `(()=>{const __el=document.getElementById(${JSON.stringify(id)});if(!__el)throw new Error(${JSON.stringify(`Elemento #${id} não encontrado.`)});__el.innerText=String(${valor});})();`;
|
|
185
548
|
}
|
|
186
549
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
550
|
+
if (linha.startsWith('ao_clicar ')) {
|
|
551
|
+
contexto.ehSite = true;
|
|
552
|
+
const resultado = linha.match(/^ao_clicar\s+"([A-Za-z][\w:-]*)"$/);
|
|
553
|
+
if (!resultado) criarErro('Use: ao_clicar "elementoId"', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
554
|
+
const id = resultado[1];
|
|
555
|
+
contexto.pilhaTraducao.push({ tipo: 'evento', linha: numeroLinha });
|
|
556
|
+
return `(()=>{const __el=document.getElementById(${JSON.stringify(id)});if(!__el)throw new Error(${JSON.stringify(`Elemento #${id} não encontrado.`)});__el.addEventListener("click",function(){`;
|
|
191
557
|
}
|
|
192
558
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
559
|
+
if (linha.startsWith('alerta ')) {
|
|
560
|
+
const valor = traduzirExpressao(linha.slice(7));
|
|
561
|
+
return `(typeof alert==="function"?alert(${valor}):console.log(${valor}));`;
|
|
562
|
+
}
|
|
197
563
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
return `let ${m[1]} = ${m[2]};`;
|
|
564
|
+
if (linha.startsWith('mudar_fundo ')) {
|
|
565
|
+
contexto.ehSite = true;
|
|
566
|
+
const valor = traduzirExpressao(linha.slice('mudar_fundo '.length));
|
|
567
|
+
return `document.body.style.background=${valor};`;
|
|
203
568
|
}
|
|
204
569
|
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: constante nome "valor"`, numeroLinha);
|
|
208
|
-
return `const ${m[1]} = ${m[2]};`;
|
|
570
|
+
if (linha.startsWith('mostrar ')) {
|
|
571
|
+
return `console.log(${traduzirExpressao(linha.slice(8))});`;
|
|
209
572
|
}
|
|
210
573
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
574
|
+
if (linha.startsWith('variavel ')) {
|
|
575
|
+
const resultado = linha.match(/^variavel\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(.+)$/);
|
|
576
|
+
if (!resultado) criarErro('Use: variavel nome = "valor"', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
577
|
+
return `let ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
|
|
578
|
+
}
|
|
216
579
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
580
|
+
if (linha.startsWith('constante ')) {
|
|
581
|
+
const resultado = linha.match(/^constante\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(.+)$/);
|
|
582
|
+
if (!resultado) criarErro('Use: constante nome = "valor"', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
583
|
+
return `const ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
|
|
221
584
|
}
|
|
222
|
-
if (t.startsWith('enquanto ')) return `while (${t.replace(/^enquanto\s+/, '')}) {`;
|
|
223
585
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
586
|
+
if (linha.startsWith('lista ')) {
|
|
587
|
+
const resultado = linha.match(/^lista\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(\[.*\])$/);
|
|
588
|
+
if (!resultado) criarErro('Use: lista nomes = ["Ana", "Rique"]', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
589
|
+
return `let ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
|
|
228
590
|
}
|
|
229
|
-
if (t.startsWith('retornar ')) return `return ${t.replace(/^retornar\s+/, '')};`;
|
|
230
591
|
|
|
231
|
-
|
|
232
|
-
|
|
592
|
+
if (linha.startsWith('definir ')) {
|
|
593
|
+
const resultado = linha.match(/^definir\s+([A-Za-z_$][\w$.[\]]*)\s*=\s*(.+)$/);
|
|
594
|
+
if (!resultado) criarErro('Use: definir nome = "novo valor"', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
595
|
+
return `${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
|
|
596
|
+
}
|
|
233
597
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
function validarBlocos(linhas) {
|
|
238
|
-
let pilha = 0;
|
|
239
|
-
linhas.forEach((linha, idx) => {
|
|
240
|
-
const t = linha.trim();
|
|
241
|
-
if (t.startsWith('se ') || t.startsWith('enquanto ') || t.startsWith('funcao ') || t.startsWith('repetir ') || t.startsWith('ao_clicar ')) pilha++;
|
|
242
|
-
if (t === 'fim') pilha--;
|
|
243
|
-
if (pilha < 0) erroDeCompilacao(`"fim" sobrando sem um bloco aberto.`, idx + 1);
|
|
244
|
-
});
|
|
245
|
-
if (pilha > 0) erroDeCompilacao(`Faltam ${pilha} "fim" para fechar bloco(s) abertos.`, linhas.length);
|
|
246
|
-
}
|
|
598
|
+
if (linha.startsWith('senaose ')) {
|
|
599
|
+
return `} else if (${traduzirExpressao(linha.slice(8))}) {`;
|
|
600
|
+
}
|
|
247
601
|
|
|
248
|
-
|
|
249
|
-
// PONTO DE ENTRADA
|
|
250
|
-
// ─────────────────────────────────────────
|
|
251
|
-
if (!arquivoOuCmd) {
|
|
252
|
-
console.log("🌱 Raizcode v1.9 — A linguagem brasileira");
|
|
253
|
-
console.log(" Uso: raizcode <arquivo.rc>");
|
|
254
|
-
console.log(" Setup VSCode: raizcode --setup");
|
|
255
|
-
process.exit();
|
|
256
|
-
}
|
|
602
|
+
if (linha === 'senao') return '} else {';
|
|
257
603
|
|
|
258
|
-
|
|
259
|
-
|
|
604
|
+
if (linha.startsWith('se ')) {
|
|
605
|
+
contexto.pilhaTraducao.push({ tipo: 'se', linha: numeroLinha });
|
|
606
|
+
return `if (${traduzirExpressao(linha.slice(3))}) {`;
|
|
607
|
+
}
|
|
260
608
|
|
|
261
|
-
if (
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
609
|
+
if (linha.startsWith('repetir ')) {
|
|
610
|
+
const resultado = linha.match(/^repetir\s+(.+)\s+vezes$/);
|
|
611
|
+
if (!resultado) criarErro('Use: repetir 5 vezes', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
612
|
+
contexto.pilhaTraducao.push({ tipo: 'repetir', linha: numeroLinha });
|
|
613
|
+
const indice = `__raiz_i_${numeroLinha}`;
|
|
614
|
+
return `for (let ${indice}=0;${indice}<(${traduzirExpressao(resultado[1])});${indice}++) {`;
|
|
615
|
+
}
|
|
265
616
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
617
|
+
if (linha.startsWith('enquanto ')) {
|
|
618
|
+
contexto.pilhaTraducao.push({ tipo: 'enquanto', linha: numeroLinha });
|
|
619
|
+
return `while (${traduzirExpressao(linha.slice(9))}) {`;
|
|
620
|
+
}
|
|
269
621
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
622
|
+
if (linha.startsWith('para cada ')) {
|
|
623
|
+
const resultado = linha.match(/^para\s+cada\s+([A-Za-z_$][\w$]*)\s+em\s+(.+)$/);
|
|
624
|
+
if (!resultado) criarErro('Use: para cada item em lista', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
625
|
+
contexto.pilhaTraducao.push({ tipo: 'para', linha: numeroLinha });
|
|
626
|
+
return `for (const ${resultado[1]} of ${traduzirExpressao(resultado[2])}) {`;
|
|
627
|
+
}
|
|
273
628
|
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
contexto.
|
|
629
|
+
if (linha.startsWith('funcao ')) {
|
|
630
|
+
const resultado = linha.match(/^funcao\s+([A-Za-z_$][\w$]*)\s*\((.*)\)$/);
|
|
631
|
+
if (!resultado) criarErro('Use: funcao nome(parametro)', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
632
|
+
contexto.pilhaTraducao.push({ tipo: 'funcao', linha: numeroLinha });
|
|
633
|
+
return `function ${resultado[1]}(${resultado[2]}) {`;
|
|
277
634
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
635
|
+
|
|
636
|
+
if (linha.startsWith('retornar ')) {
|
|
637
|
+
return `return ${traduzirExpressao(linha.slice(9))};`;
|
|
281
638
|
}
|
|
282
639
|
|
|
283
|
-
|
|
284
|
-
|
|
640
|
+
if (linha === 'parar') return 'break;';
|
|
641
|
+
if (linha === 'continuar') return 'continue;';
|
|
285
642
|
|
|
286
|
-
|
|
287
|
-
|
|
643
|
+
if (linha === 'fim') {
|
|
644
|
+
const bloco = contexto.pilhaTraducao.pop();
|
|
645
|
+
if (!bloco) criarErro('Existe um "fim" sem bloco aberto.', contexto.arquivo, numeroLinha, linhaOriginal);
|
|
646
|
+
if (bloco.tipo === 'evento') return '});})();';
|
|
647
|
+
return '}';
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (linha.startsWith('js ')) return linha.slice(3);
|
|
651
|
+
|
|
652
|
+
if (contexto.estrito && !contexto.permitirJs) {
|
|
653
|
+
criarErro(`Comando desconhecido: "${linha}"`, contexto.arquivo, numeroLinha, linhaOriginal);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return linhaOriginal;
|
|
657
|
+
}
|
|
288
658
|
|
|
289
|
-
|
|
659
|
+
function compilarRC(codigo, arquivo, opcoes = {}) {
|
|
660
|
+
const linhas = codigo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
|
|
661
|
+
validarBlocos(linhas, arquivo);
|
|
290
662
|
|
|
291
|
-
const
|
|
292
|
-
|
|
663
|
+
const contexto = {
|
|
664
|
+
arquivo,
|
|
665
|
+
ehSite: false,
|
|
666
|
+
pilhaTraducao: [],
|
|
667
|
+
permitirJs: Boolean(opcoes.permitirJs),
|
|
668
|
+
estrito: Boolean(opcoes.estrito)
|
|
669
|
+
};
|
|
293
670
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
671
|
+
const javascript = linhas
|
|
672
|
+
.map((linha, indice) => traduzirLinha(linha, indice + 1, contexto))
|
|
673
|
+
.join('\n');
|
|
674
|
+
|
|
675
|
+
return { javascript, ehSite: contexto.ehSite };
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function executarComoModulo(javascript, arquivo) {
|
|
679
|
+
const moduloRaizcode = new Module(arquivo, module);
|
|
680
|
+
moduloRaizcode.filename = arquivo;
|
|
681
|
+
moduloRaizcode.paths = Module._nodeModulePaths(path.dirname(arquivo));
|
|
682
|
+
moduloRaizcode._compile(javascript, arquivo);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function obterLinhaDoErro(erro, arquivo) {
|
|
686
|
+
if (!erro || !erro.stack) return null;
|
|
687
|
+
const arquivoEscapado = arquivo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
688
|
+
const resultado = erro.stack.match(new RegExp(`${arquivoEscapado}:(\\d+):(\\d+)`));
|
|
689
|
+
if (!resultado) return null;
|
|
690
|
+
return { linha: Number(resultado[1]), coluna: Number(resultado[2]) };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function mostrarErro(erro, arquivo, codigoFonte = '') {
|
|
694
|
+
if (erro instanceof ErroRaizcode) {
|
|
695
|
+
console.error(`\n❌ Erro Raizcode em ${path.basename(erro.arquivo)}:${erro.numeroLinha}`);
|
|
696
|
+
console.error(` ${erro.message}`);
|
|
697
|
+
if (erro.codigoLinha) {
|
|
698
|
+
console.error(`\n ${erro.numeroLinha} | ${erro.codigoLinha}`);
|
|
699
|
+
}
|
|
700
|
+
console.error('');
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const local = obterLinhaDoErro(erro, arquivo);
|
|
705
|
+
const linhas = codigoFonte.split(/\r\n|\n|\r/);
|
|
706
|
+
|
|
707
|
+
if (local) {
|
|
708
|
+
console.error(`\n❌ Erro ao executar ${path.basename(arquivo)}:${local.linha}:${local.coluna}`);
|
|
709
|
+
console.error(` ${erro.name || 'Erro'}: ${erro.message}`);
|
|
710
|
+
if (linhas[local.linha - 1] !== undefined) {
|
|
711
|
+
console.error(`\n ${local.linha} | ${linhas[local.linha - 1]}`);
|
|
712
|
+
console.error(` ${' '.repeat(Math.max(0, local.coluna - 1))}^`);
|
|
713
|
+
}
|
|
714
|
+
console.error('');
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
console.error(`\n❌ ${erro.name || 'Erro'}: ${erro.message}\n`);
|
|
719
|
+
if (flags.has('--debug') && erro.stack) console.error(erro.stack);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function montarHTML(javascript, htmlExtra, cssExtra, nomeArquivo) {
|
|
723
|
+
const origemSegura = String(nomeArquivo).replace(/[\r\n]/g, '');
|
|
724
|
+
return `<!DOCTYPE html>
|
|
297
725
|
<html lang="pt-br">
|
|
298
726
|
<head>
|
|
299
727
|
<meta charset="UTF-8">
|
|
@@ -304,7 +732,7 @@ try {
|
|
|
304
732
|
body {
|
|
305
733
|
background: #0d0d0d;
|
|
306
734
|
color: #f0f0f0;
|
|
307
|
-
font-family: sans-serif;
|
|
735
|
+
font-family: Arial, sans-serif;
|
|
308
736
|
display: flex;
|
|
309
737
|
justify-content: center;
|
|
310
738
|
align-items: center;
|
|
@@ -318,13 +746,12 @@ try {
|
|
|
318
746
|
padding: 40px;
|
|
319
747
|
border-radius: 20px;
|
|
320
748
|
background: #1a1a1a;
|
|
321
|
-
box-shadow: 0 0 30px rgba(0,255,136,0.2);
|
|
322
749
|
max-width: 800px;
|
|
323
750
|
width: 100%;
|
|
324
751
|
}
|
|
325
|
-
h1 { color: #00ff88;
|
|
752
|
+
h1 { color: #00ff88; }
|
|
326
753
|
h2 { color: #00cfff; }
|
|
327
|
-
a
|
|
754
|
+
a { color: #00ff88; }
|
|
328
755
|
button {
|
|
329
756
|
background: #00ff88;
|
|
330
757
|
color: #000;
|
|
@@ -335,7 +762,6 @@ try {
|
|
|
335
762
|
cursor: pointer;
|
|
336
763
|
margin: 8px;
|
|
337
764
|
font-size: 1rem;
|
|
338
|
-
transition: opacity 0.2s;
|
|
339
765
|
}
|
|
340
766
|
button:hover { opacity: 0.8; }
|
|
341
767
|
input {
|
|
@@ -347,52 +773,138 @@ try {
|
|
|
347
773
|
margin: 8px;
|
|
348
774
|
font-size: 1rem;
|
|
349
775
|
}
|
|
350
|
-
|
|
776
|
+
${cssExtra}
|
|
351
777
|
</style>
|
|
352
778
|
</head>
|
|
353
779
|
<body>
|
|
354
|
-
<div id="raiz-app"
|
|
780
|
+
<div id="raiz-app">
|
|
781
|
+
${htmlExtra}
|
|
782
|
+
</div>
|
|
355
783
|
<script>
|
|
356
|
-
|
|
784
|
+
window.addEventListener('error', function(event) {
|
|
785
|
+
console.error('Erro Raizcode:', event.message, 'linha', event.lineno);
|
|
786
|
+
});
|
|
787
|
+
${javascript}
|
|
788
|
+
//# sourceURL=${origemSegura}
|
|
357
789
|
</script>
|
|
358
790
|
</body>
|
|
359
791
|
</html>`;
|
|
792
|
+
}
|
|
360
793
|
|
|
361
|
-
|
|
362
|
-
|
|
794
|
+
function iniciarServidor(html, porta, abrirAutomaticamente) {
|
|
795
|
+
const servidor = http.createServer((requisicao, resposta) => {
|
|
796
|
+
if (requisicao.url === '/favicon.ico') {
|
|
797
|
+
resposta.writeHead(204);
|
|
798
|
+
resposta.end();
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
363
801
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
802
|
+
resposta.writeHead(200, {
|
|
803
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
804
|
+
'Cache-Control': 'no-store'
|
|
367
805
|
});
|
|
806
|
+
resposta.end(html);
|
|
807
|
+
});
|
|
368
808
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
console.
|
|
372
|
-
console.
|
|
373
|
-
|
|
374
|
-
|
|
809
|
+
servidor.on('error', (erro) => {
|
|
810
|
+
if (erro.code === 'EADDRINUSE') {
|
|
811
|
+
console.error(`\n❌ A porta ${porta} já está sendo usada.`);
|
|
812
|
+
console.error(` Tente: raizcode seu-arquivo.rc ${porta + 1}\n`);
|
|
813
|
+
} else {
|
|
814
|
+
console.error(`\n❌ Erro no servidor: ${erro.message}\n`);
|
|
815
|
+
}
|
|
816
|
+
process.exitCode = 1;
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
servidor.listen(porta, '127.0.0.1', () => {
|
|
820
|
+
const url = `http://127.0.0.1:${porta}`;
|
|
821
|
+
console.log('\n🌱 Raizcode — servidor ativo!');
|
|
822
|
+
console.log(`🌐 Acesse: ${url}`);
|
|
823
|
+
console.log('⏹ Pressione Ctrl+C para encerrar.\n');
|
|
824
|
+
if (abrirAutomaticamente) abrirNavegador(url);
|
|
825
|
+
});
|
|
375
826
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
827
|
+
process.once('SIGINT', () => {
|
|
828
|
+
console.log('\n👋 Servidor encerrado.');
|
|
829
|
+
servidor.close(() => process.exit(0));
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function executarProjeto() {
|
|
834
|
+
if (!arquivoOuCmd || flags.has('--help') || arquivoOuCmd === '-h') {
|
|
835
|
+
mostrarAjuda();
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (flags.has('--version') || arquivoOuCmd === '-v') {
|
|
840
|
+
console.log(obterVersao());
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (arquivoOuCmd === '--setup') {
|
|
845
|
+
instalarExtensaoVSCode();
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const arquivoEntrada = posicionais[0];
|
|
850
|
+
if (!arquivoEntrada) {
|
|
851
|
+
mostrarAjuda();
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const caminho = path.resolve(arquivoEntrada);
|
|
856
|
+
if (!fs.existsSync(caminho)) {
|
|
857
|
+
console.error(`❌ Arquivo não encontrado: ${arquivoEntrada}`);
|
|
858
|
+
process.exitCode = 1;
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (path.extname(caminho).toLowerCase() !== '.rc') {
|
|
863
|
+
console.error('❌ O arquivo principal precisa ter a extensão .rc');
|
|
864
|
+
process.exitCode = 1;
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const nomeBase = caminho.slice(0, -path.extname(caminho).length);
|
|
869
|
+
const arqRcx = `${nomeBase}.rcx`;
|
|
870
|
+
const arqRcc = `${nomeBase}.rcc`;
|
|
871
|
+
const saidaHTML = `${nomeBase}.html`;
|
|
872
|
+
let codigoFonte = '';
|
|
873
|
+
|
|
874
|
+
try {
|
|
875
|
+
const htmlExtra = fs.existsSync(arqRcx)
|
|
876
|
+
? traduzirEstrutura(fs.readFileSync(arqRcx, 'utf8'), arqRcx)
|
|
877
|
+
: '';
|
|
878
|
+
const cssExtra = fs.existsSync(arqRcc)
|
|
879
|
+
? traduzirEstilo(fs.readFileSync(arqRcc, 'utf8'), arqRcc)
|
|
880
|
+
: '';
|
|
881
|
+
|
|
882
|
+
codigoFonte = fs.readFileSync(caminho, 'utf8');
|
|
883
|
+
const compilado = compilarRC(codigoFonte, caminho, {
|
|
884
|
+
permitirJs: flags.has('--permitir-js'),
|
|
885
|
+
estrito: flags.has('--estrito')
|
|
381
886
|
});
|
|
382
887
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
888
|
+
const ehSite = compilado.ehSite || Boolean(htmlExtra) || Boolean(cssExtra);
|
|
889
|
+
if (!ehSite) {
|
|
890
|
+
executarComoModulo(compilado.javascript, caminho);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const portaInformada = posicionais[1] || '3000';
|
|
895
|
+
const porta = Number(portaInformada);
|
|
896
|
+
if (!Number.isInteger(porta) || porta < 1 || porta > 65535) {
|
|
897
|
+
criarErro(`Porta inválida: ${portaInformada}`, caminho, 1, codigoFonte.split(/\r\n|\n|\r/)[0] || '');
|
|
390
898
|
}
|
|
391
|
-
}
|
|
392
899
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
console.
|
|
900
|
+
const html = montarHTML(compilado.javascript, htmlExtra, cssExtra, path.basename(caminho));
|
|
901
|
+
fs.writeFileSync(saidaHTML, html, 'utf8');
|
|
902
|
+
console.log(`✅ Página gerada: ${saidaHTML}`);
|
|
903
|
+
iniciarServidor(html, porta, !flags.has('--no-open'));
|
|
904
|
+
} catch (erro) {
|
|
905
|
+
mostrarErro(erro, caminho, codigoFonte);
|
|
906
|
+
process.exitCode = 1;
|
|
396
907
|
}
|
|
397
|
-
|
|
398
|
-
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
executarProjeto();
|