raizcode-ofc 1.9.0 → 1.10.1
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/compilador.js +772 -255
- package/icons/raizcode-icon-theme.json +17 -0
- package/package.json +1 -1
- package/exemplos/teste.js +0 -6
package/compilador.js
CHANGED
|
@@ -1,294 +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 corretamente
|
|
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
|
-
icon: { dark: "./icons/rc.png", light: "./icons/rc.png" }
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: "raizcode-estrutura",
|
|
46
|
-
aliases: ["Raizcode Estrutura", "rcx"],
|
|
47
|
-
extensions: [".rcx"],
|
|
48
|
-
icon: { dark: "./icons/rcx.png", light: "./icons/rcx.png" }
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
id: "raizcode-estilo",
|
|
52
|
-
aliases: ["Raizcode Estilo", "rcc"],
|
|
53
|
-
extensions: [".rcc"],
|
|
54
|
-
icon: { dark: "./icons/rcc.png", light: "./icons/rcc.png" }
|
|
55
|
-
}
|
|
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'] }
|
|
56
110
|
],
|
|
57
|
-
grammars
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
path: "./syntaxes/raizcode.tmLanguage.json"
|
|
62
|
-
}
|
|
63
|
-
]
|
|
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
|
+
: []
|
|
64
115
|
}
|
|
65
116
|
};
|
|
66
117
|
|
|
67
|
-
fs.writeFileSync(
|
|
68
|
-
path.join(extDir, 'package.json'),
|
|
69
|
-
JSON.stringify(pkgExtensao, null, 2)
|
|
70
|
-
);
|
|
118
|
+
fs.writeFileSync(path.join(extDir, 'package.json'), JSON.stringify(pkgExtensao, null, 2));
|
|
71
119
|
|
|
72
|
-
console.log(
|
|
120
|
+
console.log('✅ Extensão Raizcode instalada.');
|
|
73
121
|
console.log(`📁 Local: ${extDir}`);
|
|
74
|
-
|
|
75
|
-
|
|
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;
|
|
76
130
|
}
|
|
77
|
-
process.exit();
|
|
78
131
|
}
|
|
79
132
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
+
}
|
|
83
147
|
|
|
84
|
-
// Abre o navegador de forma correta em qualquer sistema
|
|
85
148
|
function abrirNavegador(url) {
|
|
86
149
|
const plataforma = os.platform();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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);
|
|
91
194
|
}
|
|
92
195
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.error(`\n❌ Erro na linha ${numeroLinha}: ${mensagem}`);
|
|
96
|
-
console.error(` Verifique seu código .rc e tente novamente.\n`);
|
|
97
|
-
process.exit(1);
|
|
196
|
+
function criarErro(mensagem, arquivo, numeroLinha, codigoLinha = '') {
|
|
197
|
+
throw new ErroRaizcode(mensagem, arquivo, numeroLinha, codigoLinha);
|
|
98
198
|
}
|
|
99
199
|
|
|
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, ''');
|
|
207
|
+
}
|
|
103
208
|
|
|
104
|
-
function
|
|
105
|
-
|
|
209
|
+
function escaparAtributo(valor) {
|
|
210
|
+
return escaparHTML(valor).replace(/`/g, '`');
|
|
211
|
+
}
|
|
106
212
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
213
|
+
function substituirForaDeStrings(texto, transformador) {
|
|
214
|
+
let resultado = '';
|
|
215
|
+
let trecho = '';
|
|
216
|
+
let aspas = null;
|
|
217
|
+
let escapado = false;
|
|
110
218
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
219
|
+
const descarregar = () => {
|
|
220
|
+
if (!trecho) return;
|
|
221
|
+
resultado += aspas ? trecho : transformador(trecho);
|
|
222
|
+
trecho = '';
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
for (const caractere of texto) {
|
|
226
|
+
trecho += caractere;
|
|
227
|
+
|
|
228
|
+
if (escapado) {
|
|
229
|
+
escapado = false;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
116
232
|
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
}
|
|
245
|
+
|
|
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}`;
|
|
141
422
|
}
|
|
142
423
|
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
.replace(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.replace(/margem:/g, 'margin:')
|
|
150
|
-
.replace(/borda:/g, 'border:')
|
|
151
|
-
.replace(/arredondado:/g, 'border-radius:')
|
|
152
|
-
.replace(/verde-neon/g, '#00ff88')
|
|
153
|
-
.replace(/amarelo-neon/g, '#ffff00')
|
|
154
|
-
.replace(/azul-neon/g, '#00cfff')
|
|
155
|
-
.replace(/vermelho-neon/g, '#ff4444')
|
|
156
|
-
.replace(/ao-passar-mouse:/g, '&:hover')
|
|
157
|
-
.replace(/estilo\s+/g, '.')
|
|
158
|
-
.replace(/\bfim\b/g, '}');
|
|
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;
|
|
159
430
|
}
|
|
160
431
|
|
|
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 = [];
|
|
164
438
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
+
}
|
|
168
461
|
|
|
169
|
-
|
|
170
|
-
|
|
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');
|
|
487
|
+
}
|
|
488
|
+
|
|
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
|
+
}
|
|
529
|
+
|
|
530
|
+
function traduzirLinha(linhaOriginal, numeroLinha, contexto) {
|
|
531
|
+
const linha = linhaOriginal.trim();
|
|
532
|
+
if (!linha || linha.startsWith('//')) return linhaOriginal;
|
|
533
|
+
|
|
534
|
+
if (linha.startsWith('pagina ')) {
|
|
171
535
|
contexto.ehSite = true;
|
|
172
|
-
const
|
|
536
|
+
const valor = linha.slice(7).trim();
|
|
537
|
+
const titulo = /^(["'`]).*\1$/.test(valor) ? traduzirExpressao(valor) : JSON.stringify(valor);
|
|
173
538
|
return `document.title = ${titulo};`;
|
|
174
539
|
}
|
|
175
540
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
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});})();`;
|
|
548
|
+
}
|
|
549
|
+
|
|
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(){`;
|
|
180
557
|
}
|
|
181
558
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (m) return `document.getElementById("${m[1]}").addEventListener("click", function() {`;
|
|
559
|
+
if (linha.startsWith('alerta ')) {
|
|
560
|
+
const valor = traduzirExpressao(linha.slice(7));
|
|
561
|
+
return `(typeof alert==="function"?alert(${valor}):console.log(${valor}));`;
|
|
186
562
|
}
|
|
187
563
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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};`;
|
|
568
|
+
}
|
|
192
569
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const m = t.match(/^variavel\s+(\w+)\s+(.*)/);
|
|
196
|
-
if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: variavel nome "valor"`, numeroLinha);
|
|
197
|
-
return `let ${m[1]} = ${m[2]};`;
|
|
570
|
+
if (linha.startsWith('mostrar ')) {
|
|
571
|
+
return `console.log(${traduzirExpressao(linha.slice(8))});`;
|
|
198
572
|
}
|
|
199
573
|
|
|
200
|
-
if (
|
|
201
|
-
const
|
|
202
|
-
if (!
|
|
203
|
-
return `
|
|
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])};`;
|
|
204
578
|
}
|
|
205
579
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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])};`;
|
|
584
|
+
}
|
|
211
585
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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])};`;
|
|
216
590
|
}
|
|
217
|
-
if (t.startsWith('enquanto ')) return `while (${t.replace(/^enquanto\s+/, '')}) {`;
|
|
218
591
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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])};`;
|
|
223
596
|
}
|
|
224
|
-
if (t.startsWith('retornar ')) return `return ${t.replace(/^retornar\s+/, '')};`;
|
|
225
597
|
|
|
226
|
-
|
|
227
|
-
}
|
|
598
|
+
if (linha.startsWith('senaose ')) {
|
|
599
|
+
return `} else if (${traduzirExpressao(linha.slice(8))}) {`;
|
|
600
|
+
}
|
|
228
601
|
|
|
229
|
-
|
|
230
|
-
// VALIDAÇÃO DE BLOCOS
|
|
231
|
-
// ─────────────────────────────────────────
|
|
232
|
-
function validarBlocos(linhas) {
|
|
233
|
-
let pilha = 0;
|
|
234
|
-
linhas.forEach((linha, idx) => {
|
|
235
|
-
const t = linha.trim();
|
|
236
|
-
if (t.startsWith('se ') || t.startsWith('enquanto ') || t.startsWith('funcao ') || t.startsWith('repetir ') || t.startsWith('ao_clicar ')) pilha++;
|
|
237
|
-
if (t === 'fim') pilha--;
|
|
238
|
-
if (pilha < 0) erroDeCompilacao(`"fim" sobrando sem um bloco aberto.`, idx + 1);
|
|
239
|
-
});
|
|
240
|
-
if (pilha > 0) erroDeCompilacao(`Faltam ${pilha} "fim" para fechar bloco(s) abertos.`, linhas.length);
|
|
241
|
-
}
|
|
602
|
+
if (linha === 'senao') return '} else {';
|
|
242
603
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
console.log("🌱 Raizcode v1.8 — A linguagem brasileira");
|
|
248
|
-
console.log(" Uso: raizcode <arquivo.rc>");
|
|
249
|
-
console.log(" Setup VSCode: raizcode --setup");
|
|
250
|
-
process.exit();
|
|
251
|
-
}
|
|
604
|
+
if (linha.startsWith('se ')) {
|
|
605
|
+
contexto.pilhaTraducao.push({ tipo: 'se', linha: numeroLinha });
|
|
606
|
+
return `if (${traduzirExpressao(linha.slice(3))}) {`;
|
|
607
|
+
}
|
|
252
608
|
|
|
253
|
-
|
|
254
|
-
const
|
|
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
|
+
}
|
|
255
616
|
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
617
|
+
if (linha.startsWith('enquanto ')) {
|
|
618
|
+
contexto.pilhaTraducao.push({ tipo: 'enquanto', linha: numeroLinha });
|
|
619
|
+
return `while (${traduzirExpressao(linha.slice(9))}) {`;
|
|
620
|
+
}
|
|
260
621
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
}
|
|
264
628
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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]}) {`;
|
|
634
|
+
}
|
|
268
635
|
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
contexto.ehSite = true;
|
|
636
|
+
if (linha.startsWith('retornar ')) {
|
|
637
|
+
return `return ${traduzirExpressao(linha.slice(9))};`;
|
|
272
638
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
639
|
+
|
|
640
|
+
if (linha === 'parar') return 'break;';
|
|
641
|
+
if (linha === 'continuar') return 'continue;';
|
|
642
|
+
|
|
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);
|
|
276
654
|
}
|
|
277
655
|
|
|
278
|
-
|
|
279
|
-
|
|
656
|
+
return linhaOriginal;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function compilarRC(codigo, arquivo, opcoes = {}) {
|
|
660
|
+
const linhas = codigo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
|
|
661
|
+
validarBlocos(linhas, arquivo);
|
|
662
|
+
|
|
663
|
+
const contexto = {
|
|
664
|
+
arquivo,
|
|
665
|
+
ehSite: false,
|
|
666
|
+
pilhaTraducao: [],
|
|
667
|
+
permitirJs: Boolean(opcoes.permitirJs),
|
|
668
|
+
estrito: Boolean(opcoes.estrito)
|
|
669
|
+
};
|
|
670
|
+
|
|
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
|
+
}
|
|
280
703
|
|
|
281
|
-
|
|
282
|
-
|
|
704
|
+
const local = obterLinhaDoErro(erro, arquivo);
|
|
705
|
+
const linhas = codigoFonte.split(/\r\n|\n|\r/);
|
|
283
706
|
|
|
284
|
-
|
|
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
|
+
}
|
|
285
717
|
|
|
286
|
-
|
|
287
|
-
|
|
718
|
+
console.error(`\n❌ ${erro.name || 'Erro'}: ${erro.message}\n`);
|
|
719
|
+
if (flags.has('--debug') && erro.stack) console.error(erro.stack);
|
|
720
|
+
}
|
|
288
721
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
722
|
+
function montarHTML(javascript, htmlExtra, cssExtra, nomeArquivo) {
|
|
723
|
+
const origemSegura = String(nomeArquivo).replace(/[\r\n]/g, '');
|
|
724
|
+
return `<!DOCTYPE html>
|
|
292
725
|
<html lang="pt-br">
|
|
293
726
|
<head>
|
|
294
727
|
<meta charset="UTF-8">
|
|
@@ -299,7 +732,7 @@ try {
|
|
|
299
732
|
body {
|
|
300
733
|
background: #0d0d0d;
|
|
301
734
|
color: #f0f0f0;
|
|
302
|
-
font-family: sans-serif;
|
|
735
|
+
font-family: Arial, sans-serif;
|
|
303
736
|
display: flex;
|
|
304
737
|
justify-content: center;
|
|
305
738
|
align-items: center;
|
|
@@ -313,13 +746,12 @@ try {
|
|
|
313
746
|
padding: 40px;
|
|
314
747
|
border-radius: 20px;
|
|
315
748
|
background: #1a1a1a;
|
|
316
|
-
box-shadow: 0 0 30px rgba(0,255,136,0.2);
|
|
317
749
|
max-width: 800px;
|
|
318
750
|
width: 100%;
|
|
319
751
|
}
|
|
320
|
-
h1 { color: #00ff88;
|
|
752
|
+
h1 { color: #00ff88; }
|
|
321
753
|
h2 { color: #00cfff; }
|
|
322
|
-
a
|
|
754
|
+
a { color: #00ff88; }
|
|
323
755
|
button {
|
|
324
756
|
background: #00ff88;
|
|
325
757
|
color: #000;
|
|
@@ -330,7 +762,6 @@ try {
|
|
|
330
762
|
cursor: pointer;
|
|
331
763
|
margin: 8px;
|
|
332
764
|
font-size: 1rem;
|
|
333
|
-
transition: opacity 0.2s;
|
|
334
765
|
}
|
|
335
766
|
button:hover { opacity: 0.8; }
|
|
336
767
|
input {
|
|
@@ -342,52 +773,138 @@ try {
|
|
|
342
773
|
margin: 8px;
|
|
343
774
|
font-size: 1rem;
|
|
344
775
|
}
|
|
345
|
-
|
|
776
|
+
${cssExtra}
|
|
346
777
|
</style>
|
|
347
778
|
</head>
|
|
348
779
|
<body>
|
|
349
|
-
<div id="raiz-app"
|
|
780
|
+
<div id="raiz-app">
|
|
781
|
+
${htmlExtra}
|
|
782
|
+
</div>
|
|
350
783
|
<script>
|
|
351
|
-
|
|
784
|
+
window.addEventListener('error', function(event) {
|
|
785
|
+
console.error('Erro Raizcode:', event.message, 'linha', event.lineno);
|
|
786
|
+
});
|
|
787
|
+
${javascript}
|
|
788
|
+
//# sourceURL=${origemSegura}
|
|
352
789
|
</script>
|
|
353
790
|
</body>
|
|
354
791
|
</html>`;
|
|
792
|
+
}
|
|
355
793
|
|
|
356
|
-
|
|
357
|
-
|
|
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
|
+
}
|
|
358
801
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
802
|
+
resposta.writeHead(200, {
|
|
803
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
804
|
+
'Cache-Control': 'no-store'
|
|
362
805
|
});
|
|
806
|
+
resposta.end(html);
|
|
807
|
+
});
|
|
363
808
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
console.
|
|
367
|
-
console.
|
|
368
|
-
|
|
369
|
-
|
|
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
|
+
});
|
|
370
826
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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')
|
|
376
886
|
});
|
|
377
887
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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] || '');
|
|
385
898
|
}
|
|
386
|
-
}
|
|
387
899
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
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;
|
|
391
907
|
}
|
|
392
|
-
|
|
393
|
-
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
executarProjeto();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"iconDefinitions": {
|
|
3
|
+
"_rc": { "iconPath": "./rc.png" },
|
|
4
|
+
"_rcx": { "iconPath": "./rcx.png" },
|
|
5
|
+
"_rcc": { "iconPath": "./rcc.png" }
|
|
6
|
+
},
|
|
7
|
+
"fileExtensions": {
|
|
8
|
+
"rc": "_rc",
|
|
9
|
+
"rcx": "_rcx",
|
|
10
|
+
"rcc": "_rcc"
|
|
11
|
+
},
|
|
12
|
+
"fileNames": {
|
|
13
|
+
"index.rc": "_rc",
|
|
14
|
+
"index.rcx": "_rcx",
|
|
15
|
+
"index.rcc": "_rcc"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED