earl-lang
Version:
Earl Bahasa Pemrograman Alur Kerja
203 lines (165 loc) • 6.77 kB
JavaScript
// modules/tampilkan.js
const fs = require('fs');
const chalk = require('chalk');
function evalMathExpression(expr) {
try {
const disanitasi = expr.replace(/[^0-9+\-*/%.() ]/g, '');
if (!disanitasi.trim()) return expr;
return Function(`"use strict"; return (${disanitasi})`)();
} catch {
return expr;
}
}
function aksesBersarang(arr, indexes) {
let current = arr;
for (const idx of indexes) {
if (!Array.isArray(current)) return undefined;
if (idx < 0 || idx >= current.length || isNaN(idx)) return undefined;
current = current[idx];
}
return current;
}
function resolveToken(token, context = {}, modules = {}) {
const { memory = {}, lingkup = [{}], ini = null } = context;
function cariDiLingkup(nama) {
for (let i = lingkup.length - 1; i >= 0; i--) {
if (nama in lingkup[i]) return lingkup[i][nama];
}
return undefined;
}
const objAttrMatch = token.match(/^:([^:\[\]]+)\.([^:\[\]]+):$/);
if (objAttrMatch) {
const objName = objAttrMatch[1];
const attrName = objAttrMatch[2];
if (objName === 'ini' && ini) {
if (attrName in ini) return ini[attrName];
return `Error: Atribut '${attrName}' tidak ditemukan di 'ini'.`;
}
const obj = memory[objName] ?? cariDiLingkup(objName);
if (obj && typeof obj === 'object') {
if (attrName in obj) return obj[attrName];
return `Error: Atribut '${attrName}' tidak ditemukan di objek '${objName}'.`;
}
return `Error: '${objName}' bukan objek yang valid atau tidak ditemukan.`;
}
const daftarBersarangMatch = token.match(/^:([^:\[\]]+)((\[\d+\])+):$/);
if (daftarBersarangMatch) {
const varName = daftarBersarangMatch[1];
const indexes = [...token.matchAll(/\[(\d+)\]/g)].map(m => Number(m[1]));
const arr = memory[varName] ?? cariDiLingkup(varName);
if (!Array.isArray(arr)) {
return `Error: '${varName}' bukan array yang valid.`;
}
let current = arr;
for (const idx of indexes) {
if (!Array.isArray(current)) return undefined;
if (idx < 0 || idx >= current.length || isNaN(idx)) return undefined;
current = current[idx];
}
return current;
}
const daftarSatuMatch = token.match(/^:([a-zA-Z_][a-zA-Z0-9_]*)\[(\d+)\]:$/);
if (daftarSatuMatch) {
const varName = daftarSatuMatch[1];
const index = Number(daftarSatuMatch[2]);
const arr = memory[varName] ?? cariDiLingkup(varName);
if (!Array.isArray(arr) || index < 0 || index >= arr.length) {
return `Error: '${varName}' bukan array atau indeks tidak valid.`;
}
return arr[index];
}
const diktaMatch = token.match(/^:([^:\[\]]+):([^:\[\]]+)$/);
if (diktaMatch) {
const [_, varName, key] = diktaMatch;
const obj = memory[varName];
if (typeof obj !== 'object' || obj === null) {
return `Error: '${varName}' bukan objek yang valid`;
}
return key in obj ? obj[key] : `Kunci '${key}' tidak ditemukan`;
}
if (token.startsWith(':') && token.endsWith(':')) {
const varName = token.slice(1, -1);
return memory[varName] ?? `Token '${token}' tidak dikenali atau tidak dapat ditemukan.`;
}
if (token.includes('.')) {
const [instanceName, attrName] = token.split('.');
const instance = memory[instanceName] ?? cariDiLingkup(instanceName);
if (instance && typeof instance === 'object') {
if (attrName in instance) {
return instance[attrName];
}
return `Error: Atribut '${attrName}' tidak ditemukan di '${instanceName}'.`;
}
return `Error: '${instanceName}' bukan objek yang valid.`;
}
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(token)) {
for (let i = lingkup.length - 1; i >= 0; i--) {
if (token in lingkup[i]) return lingkup[i][token];
}
return memory[token] ?? token;
}
if (!isNaN(token)) return Number(token);
try {
const disanitasi = token.replace(/[^0-9+\-*/%.() ]/g, '');
if (!disanitasi.trim()) return token;
return Function(`"use strict"; return (${disanitasi})`)();
} catch {
return token;
}
}
function formatValue(val, verbose = false) {
if (val === undefined) return chalk.gray('[undefined]');
if (typeof val === 'number') return chalk.cyan(val);
if (typeof val === 'string') return chalk.green(`"${val}"`);
if (typeof val === 'boolean') return chalk.yellow(val);
if (Array.isArray(val)) return verbose ? chalk.magenta(JSON.stringify(val, null, 2)) : chalk.magenta(`[daftar(${val.length})]`);
if (typeof val === 'object') return verbose ? chalk.blue(JSON.stringify(val, null, 2)) : chalk.blue(`[objek]`);
return String(val);
}
function tampilkan(tokens, modules, context) {
const memory = context.memory || {};
const lingkup = context.lingkup || [{}];
if (tokens.length < 2) {
console.log(chalk.red("Perintah 'tampilkan' memelukan argumen."));
return;
}
let verbose = false;
let hasil = [];
let fileKeluaran = null;
let i = 1;
if (tokens[i] === '-v') {
verbose = true;
i++;
}
if (tokens[i] === 'memory') {
console.log('Isi memory saat ini:');
console.dir(memory, { depth: null, colors: true });
return;
}
while (i < tokens.length) {
const token = tokens[i];
if (token === '>' && i + 1 < tokens.length) {
fileKeluaran = tokens[i + 1].replace(/"/g, '');
break;
}
if (token.startsWith('"') && token.endsWith('"')) {
hasil.push(token.slice(1, -1));
} else {
const nilai = resolveToken(token, context, modules);
hasil.push(verbose ? JSON.stringify(nilai, null, 2) : formatValue(nilai, verbose));
}
i++;
}
const keluaran = hasil.join(' ');
if (fileKeluaran) {
fs.writeFileSync(fileKeluaran, keluaran + '\n');
console.log(chalk.gray(`Output disimpan ke file: ${fileKeluaran}`));
} else {
console.log(keluaran);
}
}
async function tampilkanHandler(tokens, modules, context) {
tampilkan(tokens, modules, context);
}
tampilkanHandler.isBlock = false;
module.exports = { tampilkan: tampilkanHandler, resolveToken, evalMathExpression };