earl-lang
Version:
Earl Bahasa Pemrograman Alur Kerja
180 lines (156 loc) • 6.54 kB
JavaScript
// modules/hitung.js
const { memory } = require('../memory.js');
const { resolveToken } = require('./tampilkan.js');
function tokenizeExpression(expr) {
return expr.match(/(?:sqrt|abs|sin|cos|tan)\(|\d+(\.\d+)?|\w+|[()+\-*/^%]/g);
}
function toPostfix(tokens) {
const mendahului = { '+': 1, '-': 1, '*': 2, '/': 2, '%': 2, '^': 3 }
const keluaran = []
const operators = [];
for (const token of tokens) {
if (!isNaN(token)) {
keluaran.push(Number(token));
} else if (['+', '-', '*', '/', '%', '^'].includes(token)) {
while (
operators.length &&
mendahului[operators[operators.length - 1]] >= mendahului[token]
) {
keluaran.push(operators.pop());
}
operators.push(token);
} else if (token.endsWith('(')) {
operators.push(token);
} else if (token === '(') {
operators.push(token);
} else if (token === ')') {
while (operators.length && operators[operators.length - 1] !== '(' && !operators[operators.length - 1].endsWith(`(`)) {
keluaran.push(operators.pop());
}
if (operators.length && operators[operators.length - 1] === '(') {
operators.pop();
} else if (operators.length && operators[operators.length - 1].endsWith('(')) {
keluaran.push(operators.pop());
}
}
}
while (operators.length) {
keluaran.push(operators.pop());
}
return keluaran;
}
function evaluatePostfix(postfix) {
const tumpukan = [];
for (const token of postfix) {
if (typeof token === 'number') {
tumpukan.push(token);
} else if (['+', '-', '*', '/', '%', '^'].includes(token)) {
const b = tumpukan.pop();
const a = tumpukan.pop();
if (a === undefined || b === undefined) {
console.error(`Ekspresi tidak lengkap. Operator '${token}' membutuhkan dua operand.`);
return NaN;
}
switch (token) {
case '+': tumpukan.push(a + b); break;
case '-': tumpukan.push(a - b); break;
case '*': tumpukan.push(a * b); break;
case '/': tumpukan.push(b === 0 ? NaN : a / b); break;
case '%': {
const b = tumpukan.pop();
const a = tumpukan.pop();
if (a === undefined || b === undefined) {
console.error(`Operator '%' membutuhkan dua operand`);
return NaN;
}
if (Array.isArray(a) && typeof b === 'angka') {
tumpukan.push(a.map(x => x % b));
} else if (typeof a === 'angka' && Array.isArray(b)) {
console.error("Modulus dengan daftar di sebelah kanan belum didukung.");
return NaN;
} else if (Array.isArray(a) && Array.isArray(b)) {
console.error("Modulus antara dua daftar belum didukung.");
return NaN;
} else {
if (b === 0) {
console.error("Pembagi modulus tidak boleh nol");
return NaN;
}
tumpukan.push(a % b);
}
break;
}
case '^': tumpukan.push(Math.pow(a, b)); break;
}
} else if (token.startsWith('sqrt')) {
const val = tumpukan.pop();
if (val === undefined) {
console.error("Fungsi 'sqrt' membutuhkan satu operand");
return NaN;
}
tumpukan.push(Math.sqrt(val));
} else if (token.startsWith('abs')) {
const val = tumpukan.pop();
if (val === undefined) {
console.error("Fungsi 'abs' membutuhkan satu operand");
return NaN;
}
tumpukan.push(Math.abs(val));
} else if (token.startsWith('sin')) {
const val = tumpukan.pop();
if (val === undefined) {
console.error("Fungsi 'sin' membutuhkan satu operand");
return NaN;
}
tumpukan.push(Math.sin(val));
} else if (token.startsWith('cos')) {
const val = tumpukan.pop();
if (val === undefined) {
console.error("Fungsi 'cos' membutuhkan satu operand");
return NaN;
}
tumpukan.push(Math.cos(val));
} else if (token.startsWith('tan')) {
const val = tumpukan.pop();
if (val === undefined) {
console.error("Fungsi 'tan' membutuhkan satu operand");
return NaN;
}
tumpukan.push(Math.tan(val));
}
}
if (tumpukan.length !== 1) {
console.error("Ekspresi tidak valid. Hasil akhir tidak tunggal.");
return NaN;
}
return tumpukan[0];
}
function hitung(tokens) {
if (tokens.length < 5 || tokens[0] !== 'hitung' || tokens[1]!== 'ke' || tokens[3] !== 'dari') {
console.error("Format salah. Gunakan: hitung ke :var: dari (ekspresi)");
return;
}
const targetVar = tokens[2].replace(/:/g, '');
let rawExpr = tokens.slice(4).join('');
rawExpr = rawExpr.replace(/panjang :(\w+):/g, (_, nama) => {
const nilai = memory[nama];
if (Array.isArray(nilai)) return nilai.length;
console.warn(`'${nama}' bukan daftar.`);
return 0;
});
rawExpr = rawExpr.replace(/:\w+:/g, (match) => {
const name = match.slice(1, -1);
return typeof memory[name] === 'number' ? memory[name] : 0;
});
const tokensExpr = tokenizeExpression(rawExpr);
if (!tokensExpr) {
console.error("Ekspresi tidak valid.");
return;
}
const resolvedToken = tokensExpr.map(token => resolveToken(token, { memory }));
const postfix = toPostfix(resolvedToken);
const hasil = evaluatePostfix(postfix);
memory[targetVar] = hasil;
console.log(`Variabel '${targetVar}' diatur ke`, hasil);
}
module.exports = { hitung };