ekushscript
Version:
EkushScript is a Bengali Romanized programming language designed to be simple, fun, and expressive — written in TypeScript.
1,562 lines (1,560 loc) • 71.2 kB
JavaScript
#!/usr/bin/env node
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
// libs/core/dist/index.js
import { stdin as input, stdout as output } from "node:process";
import * as readline from "node:readline/promises";
function parseEkushInput(raw) {
const val = raw.trim();
if (val === "thik")
return true;
if (val === "bhul")
return false;
if (val === "faka")
return null;
if (!isNaN(Number(val)))
return Number(val);
return val;
}
function throwTypeError(msg, line) {
throw new EkushRuntimeError(`Ektu bhabo! ${msg}`, line);
}
function throwUndefinedError(name, line) {
throw new EkushRuntimeError(`Khuje dekho! '${name}' variable pawa jayni.`, line);
}
async function runEkushScript(code, isRepl = false) {
const lexer = new Lexer(code);
const parser = new Parser(lexer);
const ast = parser.parseProgram();
if (isRepl) {
if (!replInterpreter) {
replInterpreter = new Interpreter();
}
await replInterpreter.evaluate(ast);
} else {
const interpreter = new Interpreter();
await interpreter.evaluate(ast);
}
}
var builtins, EkushRuntimeError, Interpreter, TokenType, keywords, booleanLiterals, nullLiteral, operatorKeywords, symbols, punctuation, Lexer, Parser, replInterpreter, Compiler;
var init_dist = __esm({
"libs/core/dist/index.js"() {
"use strict";
builtins = {
/**
* bolo(): Prints output to the console
* Example: bolo("Hello")
*/
bolo: (...args2) => {
let env2 = {};
if (args2.length && typeof args2[args2.length - 1] === "object" && !Array.isArray(args2[args2.length - 1])) {
env2 = args2.pop();
}
const formatValue = (arg) => {
if (typeof arg === "boolean") {
return arg ? "thik" : "bhul";
}
if (arg === null) {
return "faka";
}
if (typeof arg === "number" && !Number.isInteger(arg)) {
return arg.toFixed(2);
}
if (arg instanceof String || typeof arg === "string") {
return arg.replace(/\$\{([^}]+)\}/g, (_, expr) => {
try {
const fn = new Function(...Object.keys(env2), `return ${expr}`);
return fn(...Object.values(env2));
} catch {
return "${" + expr + "}";
}
});
}
if (Array.isArray(arg)) {
return arg.map((item) => formatValue(item));
}
return arg;
};
for (let i = 0; i < args2.length; i++) {
if (typeof args2[i] === "string" || args2[i] instanceof String) {
for (let j = 0; j < args2.length; j++) {
if (i !== j && typeof args2[j] !== "string" && typeof args2[j] !== "number" && !(args2[j] instanceof String)) {
throw new Error("bolo(): String concatenation only allowed with other strings or numbers.");
}
}
}
}
const formattedArgs = args2.map((arg) => formatValue(arg));
console.log(...formattedArgs);
},
/**
* shuno(): Gets input from the user
* Example: rakho nam = shuno("Tomar nam ki?")
*/
shuno: async (text) => {
const rl2 = readline.createInterface({ input, output });
const raw = await rl2.question(text ?? "Input: ");
rl2.close();
return parseEkushInput(raw);
},
/**
* lottery(min, max): Returns a random integer between min and max
* Example: rakho num = lottery(1, 100)
*/
lottery: (min, max) => {
if (typeof min !== "number" || typeof max !== "number") {
throw new Error("lottery(): min and max must be numbers");
}
return Math.floor(Math.random() * (max - min + 1)) + min;
},
/**
* ghumao(ms): Waits asynchronously for the given milliseconds
* Example: ghumao(1000) // waits 1 second
*/
ghumao: async (ms) => {
if (typeof ms !== "number") {
throw new Error("ghumao(): ms must be a number");
}
return new Promise((resolve2) => setTimeout(resolve2, ms));
},
/**
* kat(start, length): Returns a substring from string
* Example: "Bangladesh".kat(1, 3) → "ang"
*/
kat: function(start, length) {
if (typeof this !== "string") {
throw new Error("kat(): should be called on a string");
}
return this.substring(start, start + length);
},
/**
* jog_koro(value): Appends a value to the array
* Example: list.jog_koro(5)
*/
arrayMethods: {
jog_koro: function(...items) {
if (!Array.isArray(this)) {
throw new Error("jog_koro(): array er jonno");
}
this.push(...items);
return this;
},
ber_koro: function() {
if (!Array.isArray(this)) {
throw new Error("ber_koro(): array er jonno");
}
return this.pop();
},
lomba: function() {
if (this instanceof String || typeof this === "string") {
return this.length;
}
if (Array.isArray(this)) {
return this.length;
}
throw new Error("lomba: string ba array er jonno");
}
}
};
EkushRuntimeError = class extends Error {
constructor(message, line) {
super(`\u{1F6A8} EkushScript Error${line ? ` [Line ${line}]` : ""}:
${message}`);
this.line = line;
this.name = "EkushRuntimeError";
}
};
Interpreter = class {
env = {};
constructor() {
this.env = { ...builtins };
}
async evaluate(node) {
let returnValue;
switch (node.type) {
case "Program":
return await this.evaluateProgram(node);
case "VariableDeclaration":
return await this.evaluateVariableDeclaration(node);
case "FunctionDeclaration":
return await this.evaluateFunctionDeclaration(node);
case "ExpressionStatement":
return await this.evaluate(node.expression);
case "BlockStatement":
return await this.evaluateBlockStatement(node);
case "IfStatement":
return await this.evaluateIfStatement(node);
case "WhileStatement":
return await this.evaluateWhileStatement(node);
case "ForStatement":
return await this.evaluateForStatement(node);
case "BreakStatement":
throw "break";
case "ContinueStatement":
throw "continue";
case "ReturnStatement":
returnValue = node.argument ? await this.evaluate(node.argument) : void 0;
throw { type: "return", value: returnValue };
case "Literal":
return node.value;
case "Identifier":
return this.evaluateIdentifier(node);
case "BinaryExpression":
return await this.evaluateBinaryExpression(node);
case "CallExpression":
return await this.evaluateCallExpression(node);
case "MethodCallExpression":
return await this.evaluateMethodCallExpression(node);
case "ArrayLiteral":
return await this.evaluateArrayLiteral(node);
case "UnaryExpression":
return await this.evaluateUnaryExpression(node);
case "AssignmentExpression":
return await this.evaluateAssignmentExpression(node);
case "Comment":
return void 0;
case "StringLiteral":
return await this.evaluateStringLiteral(node);
case "MemberExpression":
return await this.evaluateMemberExpression(node);
default:
throw new Error(`Unknown node type: ${node.type}`);
}
}
async evaluateProgram(program) {
let result;
for (const stmt of program.body) {
result = await this.evaluate(stmt);
}
return result;
}
async evaluateBlockStatement(block) {
let result;
for (const stmt of block.body) {
try {
result = await this.evaluate(stmt);
} catch (error) {
if (error === "break" || error === "continue") {
throw error;
}
if (this.isReturnSignal(error)) {
throw error;
}
throw error;
}
}
return result;
}
isReturnSignal(error) {
return error && typeof error === "object" && error.type === "return";
}
async evaluateVariableDeclaration(node) {
const value = await this.evaluate(node.value);
this.env[node.identifier] = value;
return value;
}
evaluateIdentifier(node) {
const val = this.env[node.name];
if (val === void 0) {
throwUndefinedError(node.name, node.line);
}
return val;
}
async evaluateBinaryExpression(node) {
const left = await this.evaluate(node.left);
const right = await this.evaluate(node.right);
switch (node.operator) {
case "+":
case "jog":
if (typeof left === "number" && typeof right === "number")
return left + right;
if ((typeof left === "string" || left instanceof String) && (typeof right === "string" || right instanceof String))
return String(left) + String(right);
if ((typeof left === "string" || left instanceof String) && (typeof right === "number" || right instanceof Number)) {
return String(left) + String(right);
}
if ((typeof left === "number" || left instanceof Number) && (typeof right === "string" || right instanceof String)) {
return String(left) + String(right);
}
throwTypeError("Jog ba concatenation shudhu string er shathe string, ba number kora jabe.", node.line);
break;
case "-":
case "biyog":
return left - right;
case "*":
case "gun":
return left * right;
case "/":
case "vag":
return left / right;
case "%":
case "vag_shesh":
return left % right;
case "==":
return left === right;
case "!=":
return left !== right;
case "<":
return left < right;
case "<=":
return left <= right;
case ">":
return left > right;
case ">=":
return left >= right;
default:
throw new Error(`Unknown operator: ${node.operator}`);
}
}
async evaluateUnaryExpression(node) {
const value = await this.evaluate(node.argument);
switch (node.operator) {
case "-":
return -value;
case "!":
return !value;
default:
throw new Error(`Unknown unary operator: ${node.operator}`);
}
}
async evaluateCallExpression(node) {
const callee = await this.evaluate(node.callee);
const args2 = await Promise.all(node.arguments.map((arg) => this.evaluate(arg)));
if (typeof callee !== "function") {
const name = node.callee.name;
throwTypeError(`'${name}' ke call kora jabena`);
}
if (node.callee.type === "Identifier" && node.callee.name === "bolo") {
return await callee(...args2, this.env);
}
return await callee(...args2);
}
async evaluateMethodCallExpression(node) {
const object = await this.evaluate(node.object);
const method = object[node.method];
if (typeof method !== "function") {
throwTypeError(`'${node.method}' method ta exist korena`);
}
const args2 = await Promise.all(node.arguments.map((arg) => this.evaluate(arg)));
return await method.apply(object, args2);
}
async evaluateArrayLiteral(node) {
const elements = await Promise.all(node.elements.map((element) => this.evaluate(element)));
Object.defineProperties(elements, {
jog_koro: {
value: builtins.arrayMethods.jog_koro,
enumerable: false,
configurable: true,
writable: true
},
ber_koro: {
value: builtins.arrayMethods.ber_koro,
enumerable: false,
configurable: true,
writable: true
},
lomba: {
value: builtins.arrayMethods.lomba,
enumerable: false,
configurable: true,
writable: true
}
});
return elements;
}
async evaluateStringLiteral(node) {
const str = node.value;
const strObj = new String(str);
Object.defineProperties(strObj, {
lomba: {
value: builtins.arrayMethods.lomba,
enumerable: false,
configurable: true,
writable: true
}
});
return strObj;
}
async evaluateIfStatement(node) {
const test = await this.evaluate(node.test);
if (test)
return await this.evaluate(node.consequent);
if (node.alternate)
return await this.evaluate(node.alternate);
return void 0;
}
async evaluateWhileStatement(node) {
let result;
while (await this.evaluate(node.test)) {
try {
result = await this.evaluate(node.body);
} catch (error) {
if (error === "break") {
break;
} else if (error === "continue") {
continue;
}
throw error;
}
}
return result;
}
async evaluateForStatement(node) {
let result;
await this.evaluate(node.init);
while (await this.evaluate(node.test)) {
try {
result = await this.evaluate(node.body);
await this.evaluate(node.update);
} catch (error) {
if (error === "break") {
break;
} else if (error === "continue") {
await this.evaluate(node.update);
continue;
}
throw error;
}
}
return result;
}
async evaluateAssignmentExpression(node) {
let target;
let value;
if (node.left.type === "Identifier") {
target = node.left.name;
if (!(target in this.env)) {
throwUndefinedError(target, node.left.line);
}
value = this.env[target];
} else {
const object = await this.evaluate(node.left.object);
const property = await this.evaluate(node.left.property);
target = [object, property];
value = object[property];
}
const right = await this.evaluate(node.right);
switch (node.operator) {
case "=":
if (Array.isArray(target)) {
target[0][target[1]] = right;
} else {
this.env[target] = right;
}
break;
case "+=":
case "jog=":
if (Array.isArray(target)) {
target[0][target[1]] = value + right;
} else {
this.env[target] = value + right;
}
break;
case "-=":
case "biyog=":
if (Array.isArray(target)) {
target[0][target[1]] = value - right;
} else {
this.env[target] = value - right;
}
break;
case "*=":
case "gun=":
if (Array.isArray(target)) {
target[0][target[1]] = value * right;
} else {
this.env[target] = value * right;
}
break;
case "/=":
case "vag=":
if (Array.isArray(target)) {
target[0][target[1]] = value / right;
} else {
this.env[target] = value / right;
}
break;
case "%=":
case "vag_shesh=":
if (Array.isArray(target)) {
target[0][target[1]] = value % right;
} else {
this.env[target] = value % right;
}
break;
default:
throw new Error(`Unsupported assignment operator: ${node.operator}`);
}
return Array.isArray(target) ? target[0][target[1]] : this.env[target];
}
async evaluateFunctionDeclaration(node) {
const func = async (...args2) => {
const localEnv = { ...this.env };
node.params.forEach((param, index) => {
const value = args2[index] !== void 0 ? args2[index] : this.evaluate(param.default);
localEnv[param.name] = value;
});
const prevEnv = this.env;
this.env = localEnv;
let result;
try {
result = await this.evaluate(node.body);
} catch (error) {
if (this.isReturnSignal(error)) {
result = error.value;
} else {
throw error;
}
} finally {
this.env = prevEnv;
}
return result;
};
if (node.name) {
this.env[node.name] = func;
}
return func;
}
async evaluateMemberExpression(node) {
const object = await this.evaluate(node.object);
const property = await this.evaluate(node.property);
if (Array.isArray(object)) {
if (typeof property !== "number") {
throwTypeError("Array index must be a number", node.line);
}
return object[property];
}
if (node.computed) {
return object[property];
} else {
return object[property];
}
}
};
TokenType = /* @__PURE__ */ ((TokenType2) => {
TokenType2[TokenType2["Keyword"] = 0] = "Keyword";
TokenType2[TokenType2["Identifier"] = 1] = "Identifier";
TokenType2[TokenType2["Number"] = 2] = "Number";
TokenType2[TokenType2["String"] = 3] = "String";
TokenType2[TokenType2["Boolean"] = 4] = "Boolean";
TokenType2[TokenType2["Null"] = 5] = "Null";
TokenType2[TokenType2["Operator"] = 6] = "Operator";
TokenType2[TokenType2["Punctuation"] = 7] = "Punctuation";
TokenType2[TokenType2["Comment"] = 8] = "Comment";
TokenType2[TokenType2["EOF"] = 9] = "EOF";
return TokenType2;
})(TokenType || {});
keywords = /* @__PURE__ */ new Set([
"rakho",
"jodi",
"nahoi",
"kaj",
"ferot",
"cheshta",
"koro",
"dhoro",
"ghuro",
"jotokkhon",
"thamo",
"cholo"
]);
booleanLiterals = /* @__PURE__ */ new Set(["thik", "bhul"]);
nullLiteral = "faka";
operatorKeywords = /* @__PURE__ */ new Set([
"jog",
"biyog",
"gun",
"vag",
"vag_shesh",
"jog=",
"biyog=",
"gun=",
"vag=",
"vag_shesh="
]);
symbols = /* @__PURE__ */ new Set([
"+",
"-",
"*",
"/",
"%",
"+=",
"-=",
"*=",
"/=",
"%=",
"=",
"==",
"!=",
"<",
">",
"<=",
">=",
"(",
")",
"{",
"}",
"[",
"]",
";",
",",
".",
"++",
"--"
]);
punctuation = /* @__PURE__ */ new Set([
"(",
")",
"{",
"}",
"[",
"]",
";",
",",
"."
]);
Lexer = class {
code;
pos = 0;
line = 1;
col = 1;
currentChar;
constructor(code) {
this.code = code;
this.currentChar = this.code[this.pos] || null;
}
advance() {
if (this.currentChar === "\n") {
this.line++;
this.col = 1;
} else {
this.col++;
}
this.pos++;
this.currentChar = this.code[this.pos] || null;
}
peek() {
return this.code[this.pos + 1] || null;
}
skipWhitespace() {
while (this.currentChar && /\s/.test(this.currentChar)) {
this.advance();
}
}
skipComment() {
if (this.currentChar === "/" && this.peek() === "/") {
while (this.currentChar !== null && this.currentChar !== "\n") {
this.advance();
}
}
if (this.currentChar === "/" && this.peek() === "*") {
this.advance();
this.advance();
while (this.currentChar !== null && !(this.currentChar === "*" && this.peek() === "/")) {
this.advance();
}
if (this.currentChar)
this.advance();
if (this.currentChar)
this.advance();
}
}
isIdentifierStart(char) {
return /[a-zA-Z_]/.test(char);
}
isIdentifierPart(char) {
return /[a-zA-Z0-9_]/.test(char);
}
getOperator() {
let op = this.currentChar || "";
this.advance();
if (this.currentChar === "=") {
op += this.currentChar;
this.advance();
}
return {
type: 6,
value: op,
line: this.line,
col: this.col - op.length
};
}
getNumber() {
let num = "";
const startCol = this.col;
while (this.currentChar && /[0-9.]/.test(this.currentChar)) {
num += this.currentChar;
this.advance();
}
return {
type: 2,
value: num,
line: this.line,
col: startCol
};
}
getString() {
const quoteType = this.currentChar;
let str = "";
const startCol = this.col;
const startLine = this.line;
this.advance();
while (this.currentChar && this.currentChar !== quoteType) {
if (this.currentChar === "\\") {
this.advance();
if (typeof this.currentChar === "string") {
const ch = this.currentChar;
if (ch === "n")
str += "\n";
else if (ch === "t")
str += " ";
else if (ch === "\\")
str += "\\";
else if (ch === '"')
str += '"';
else if (ch === "'")
str += "'";
else if (ch === "r")
str += "\r";
else if (ch === "b")
str += "\b";
else if (ch === "v")
str += "\v";
else
str += ch;
}
this.advance();
} else {
str += this.currentChar;
this.advance();
}
}
if (this.currentChar !== quoteType) {
throw new Error(`String not closed properly. Line ${startLine}`);
}
this.advance();
return {
type: 3,
value: str,
line: startLine,
col: startCol
};
}
getIdentifier() {
let id = "";
const startCol = this.col;
while (this.currentChar && this.isIdentifierPart(this.currentChar)) {
id += this.currentChar;
this.advance();
}
if (this.currentChar === "=" && operatorKeywords.has(id)) {
id += this.currentChar;
this.advance();
return {
type: 6,
value: id,
line: this.line,
col: startCol
};
}
if (keywords.has(id)) {
return {
type: 0,
value: id,
line: this.line,
col: startCol
};
}
if (operatorKeywords.has(id)) {
return {
type: 6,
value: id,
line: this.line,
col: startCol
};
}
if (booleanLiterals.has(id)) {
return {
type: 4,
value: id,
line: this.line,
col: startCol
};
}
if (id === nullLiteral) {
return {
type: 5,
value: id,
line: this.line,
col: startCol
};
}
return {
type: 1,
value: id,
line: this.line,
col: startCol
};
}
getNextToken() {
while (this.currentChar !== null) {
if (/\s/.test(this.currentChar)) {
this.skipWhitespace();
continue;
}
if (this.currentChar === "/" && (this.peek() === "/" || this.peek() === "*")) {
this.skipComment();
continue;
}
if (/[0-9]/.test(this.currentChar))
return this.getNumber();
if (this.currentChar === `"`)
return this.getString();
if (this.isIdentifierStart(this.currentChar))
return this.getIdentifier();
const char = this.currentChar;
const next = this.peek();
const twoChar = char + next;
if (symbols.has(twoChar)) {
this.advance();
this.advance();
return {
type: 6,
value: twoChar,
line: this.line,
col: this.col - 2
};
}
if (operatorKeywords.has(char)) {
return this.getOperator();
}
if (punctuation.has(char)) {
this.advance();
return {
type: 7,
value: char,
line: this.line,
col: this.col - 1
};
}
if (symbols.has(char)) {
this.advance();
return {
type: 6,
value: char,
line: this.line,
col: this.col - 1
};
}
throw new Error(
`Unknown character '${char}' at line ${this.line}, column ${this.col}`
);
}
return {
type: 9,
value: "EOF",
line: this.line,
col: this.col
};
}
};
Parser = class {
lexer;
current;
constructor(lexer) {
this.lexer = lexer;
this.current = this.lexer.getNextToken();
}
eat(type, value) {
if (this.current.type === type && (!value || this.current.value === value)) {
const prev = this.current;
this.current = this.lexer.getNextToken();
return prev;
}
throw new Error(`Expected ${TokenType[type]} but got ${this.current.value}`);
}
parseProgram() {
const body = [];
while (this.current.type !== 9) {
const stmt = this.parseStatement();
if (stmt)
body.push(stmt);
}
return { type: "Program", body };
}
parseStatement() {
if (this.current.type === 8) {
const comment = this.current.value;
const multiline = comment.startsWith("/*");
this.eat(
8
/* Comment */
);
return {
type: "Comment",
value: comment,
multiline
};
}
if (this.current.type === 0) {
switch (this.current.value) {
case "rakho":
return this.parseVariableDeclaration();
case "kaj":
return this.parseFunctionDeclaration();
case "jodi":
return this.parseIfStatement();
case "jotokkhon":
return this.parseWhileStatement();
case "ghuro":
return this.parseForStatement();
case "thamo":
return this.parseBreakStatement();
case "cholo":
return this.parseContinueStatement();
case "ferot":
return this.parseReturnStatement();
case "cheshta":
return this.parseTryCatchStatement();
}
}
return this.parseExpressionStatement();
}
parseVariableDeclaration() {
this.eat(0, "rakho");
const identifier = this.eat(
1
/* Identifier */
).value;
this.eat(6, "=");
const value = this.parseExpression();
this.optionalSemicolon();
return {
type: "VariableDeclaration",
identifier,
value
};
}
parseFunctionDeclaration() {
this.eat(0, "kaj");
let name = null;
if (this.current.type === 1) {
name = this.eat(
1
/* Identifier */
).value;
}
this.eat(7, "(");
const params = [];
while (this.current.type !== 7 || this.current.value !== ")") {
const paramName = this.eat(
1
/* Identifier */
).value;
let defaultValue = void 0;
if (this.current.value === "=") {
this.eat(6, "=");
defaultValue = this.parseExpression();
}
params.push({ name: paramName, default: defaultValue });
if (this.current.value === ",")
this.eat(7, ",");
}
this.eat(7, ")");
const body = this.parseBlockStatement();
return { type: "FunctionDeclaration", name, params, body };
}
parseIfStatement() {
this.eat(0, "jodi");
this.eat(7, "(");
const test = this.parseExpression();
this.eat(7, ")");
const consequent = this.parseBlockStatement();
let alternate = void 0;
if (this.current.type === 0 && this.current.value === "nahoi") {
this.eat(0, "nahoi");
if (this.current.type === 0 && this.current.value === "jodi") {
alternate = this.parseIfStatement();
} else {
alternate = this.parseBlockStatement();
}
}
return { type: "IfStatement", test, consequent, alternate };
}
parseWhileStatement() {
this.eat(0, "jotokkhon");
this.eat(7, "(");
const test = this.parseExpression();
this.eat(7, ")");
const body = this.parseBlockStatement();
return { type: "WhileStatement", test, body };
}
parseForStatement() {
this.eat(0, "ghuro");
this.eat(7, "(");
const init = this.parseVariableDeclaration();
const test = this.parseExpression();
this.eat(7, ";");
const update = this.parseExpression();
this.eat(7, ")");
const body = this.parseBlockStatement();
return { type: "ForStatement", init, test, update, body };
}
parseBreakStatement() {
this.eat(0, "thamo");
this.optionalSemicolon();
return { type: "BreakStatement" };
}
parseContinueStatement() {
this.eat(0, "cholo");
this.optionalSemicolon();
return { type: "ContinueStatement" };
}
parseReturnStatement() {
this.eat(0, "ferot");
let argument = void 0;
if (this.current.value !== ";" && this.current.type !== 9) {
argument = this.parseExpression();
}
this.optionalSemicolon();
return { type: "ReturnStatement", argument };
}
parseTryCatchStatement() {
this.eat(0, "cheshta");
this.eat(0, "koro");
const tryBlock = this.parseBlockStatement();
this.eat(0, "dhoro");
this.eat(7, "(");
const catchParam = this.eat(
1
/* Identifier */
).value;
this.eat(7, ")");
const catchBlock = this.parseBlockStatement();
return {
type: "TryCatchStatement",
tryBlock,
catchParam,
catchBlock
};
}
parseBlockStatement() {
this.eat(7, "{");
const body = [];
while (this.current.value !== "}" && this.current.type !== 9) {
const stmt = this.parseStatement();
if (stmt)
body.push(stmt);
}
this.eat(7, "}");
return { type: "BlockStatement", body };
}
parseExpressionStatement() {
const expr = this.parseExpression();
this.optionalSemicolon();
return { type: "ExpressionStatement", expression: expr };
}
parseExpression() {
const left = this.parseAssignmentExpression();
return this.parseMemberExpression(left);
}
parseAssignmentExpression() {
let left = this.parseBinaryExpression();
left = this.parseMemberExpression(left);
const compoundOperators = /* @__PURE__ */ new Set([
"=",
"+=",
"-=",
"*=",
"/=",
"%=",
"jog=",
"biyog=",
"gun=",
"vag=",
"vag_shesh="
]);
if (this.current.type === 6 && compoundOperators.has(this.current.value)) {
if (left.type !== "Identifier" && left.type !== "MemberExpression") {
throw new Error("Assignment target must be a variable name or member expression.");
}
const operatorToken = this.eat(
6
/* Operator */
);
const operator = operatorToken.value;
const right = this.parseAssignmentExpression();
return {
type: "AssignmentExpression",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
operator,
left,
right,
line: operatorToken.line
};
}
return left;
}
parseBinaryExpression() {
let left = this.parsePrimaryExpression();
const binaryOperators = /* @__PURE__ */ new Set([
"+",
"-",
"*",
"/",
"%",
"==",
"!=",
"<",
">",
"<=",
">=",
"jog",
"biyog",
"gun",
"vag",
"vag_shesh"
]);
while (this.current.type === 6 && binaryOperators.has(this.current.value)) {
const operatorToken = this.eat(
6
/* Operator */
);
const operator = operatorToken.value;
const right = this.parsePrimaryExpression();
left = {
type: "BinaryExpression",
operator,
left,
right,
line: operatorToken.line
};
}
return left;
}
parsePrimaryExpression() {
if (this.current.type === 2) {
const token = this.eat(
2
/* Number */
);
const value = Number(token.value);
return { type: "Literal", value, line: token.line };
}
if (this.current.type === 3) {
const token = this.eat(
3
/* String */
);
return { type: "StringLiteral", value: token.value, line: token.line };
}
if (this.current.type === 4) {
const token = this.eat(
4
/* Boolean */
);
return {
type: "Literal",
value: token.value === "thik",
line: token.line
};
}
if (this.current.type === 5) {
const token = this.eat(
5
/* Null */
);
return { type: "Literal", value: null, line: token.line };
}
if (this.current.type === 1) {
const token = this.eat(
1
/* Identifier */
);
const name = token.value;
if (this.current.value === "(") {
this.eat(7, "(");
const args2 = [];
while (this.current.type !== 7 || this.current.value !== ")") {
if (this.current.type === 9) {
throw new Error("Expected ')' but reached end of file.");
}
args2.push(this.parseExpression());
if (this.current.type === 7 && this.current.value === ",") {
this.eat(7, ",");
} else if (this.current.type === 7 && this.current.value !== ")") {
throw new Error(`Unexpected token in arguments: ${this.current.value}`);
}
}
this.eat(7, ")");
return {
type: "CallExpression",
callee: { type: "Identifier", name, line: token.line },
arguments: args2,
line: token.line
};
}
if (this.current.value === ".") {
this.eat(7, ".");
const property = this.eat(
1
/* Identifier */
).value;
if (this.current.value === "(") {
this.eat(7, "(");
const args2 = [];
while (this.current.type !== 7 || this.current.value !== ")") {
if (this.current.type === 9) {
throw new Error("Expected ')' but reached end of file.");
}
args2.push(this.parseExpression());
if (this.current.type === 7 && this.current.value === ",") {
this.eat(7, ",");
} else if (this.current.type === 7 && this.current.value !== ")") {
throw new Error(`Unexpected token in arguments: ${this.current.value}`);
}
}
this.eat(7, ")");
return {
type: "MethodCallExpression",
object: { type: "Identifier", name, line: token.line },
method: property,
arguments: args2,
line: token.line
};
}
}
return { type: "Identifier", name, line: token.line };
}
if (this.current.type === 7 && this.current.value === "[") {
return this.parseArrayLiteral();
}
if (this.current.type === 7 && this.current.value === "(") {
this.eat(7, "(");
const expr = this.parseExpression();
this.eat(7, ")");
return expr;
}
throw new Error(`Unexpected token: ${this.current.value}`);
}
parseArrayLiteral() {
this.eat(7, "[");
const elements = [];
while (this.current.type !== 7 || this.current.value !== "]") {
elements.push(this.parseExpression());
if (this.current.value === ",") {
this.eat(7, ",");
}
}
this.eat(7, "]");
return { type: "ArrayLiteral", elements };
}
parseMemberExpression(object) {
if (this.current.type === 7 && this.current.value === "[") {
this.eat(7, "[");
const property = this.parseExpression();
this.eat(7, "]");
return {
type: "MemberExpression",
object,
property,
computed: true
};
}
return object;
}
optionalSemicolon() {
if (this.current.value === ";") {
this.eat(7, ";");
}
}
};
replInterpreter = null;
Compiler = class {
indentLevel = 0;
INDENT = " ";
// 2 spaces
compile(ast) {
return this.compileNode(ast);
}
indent() {
return this.INDENT.repeat(this.indentLevel);
}
compileNode(node) {
switch (node.type) {
case "Program":
return this.compileProgram(node);
case "VariableDeclaration":
return this.compileVariableDeclaration(node);
case "FunctionDeclaration":
return this.compileFunctionDeclaration(node);
case "BlockStatement":
return this.compileBlockStatement(node);
case "ExpressionStatement":
return this.compileExpressionStatement(node);
case "CallExpression":
return this.compileCallExpression(node);
case "MethodCallExpression":
return this.compileMethodCallExpression(node);
case "BinaryExpression":
return this.compileBinaryExpression(node);
case "UnaryExpression":
return this.compileUnaryExpression(node);
case "Identifier":
return this.compileIdentifier(node);
case "Literal":
return this.compileLiteral(node);
case "StringLiteral":
return this.compileStringLiteral(node);
case "ArrayLiteral":
return this.compileArrayLiteral(node);
case "IfStatement":
return this.compileIfStatement(node);
case "WhileStatement":
return this.compileWhileStatement(node);
case "ForStatement":
return this.compileForStatement(node);
case "ReturnStatement":
return this.compileReturnStatement(node);
case "BreakStatement":
return this.compileBreakStatement(node);
case "ContinueStatement":
return this.compileContinueStatement(node);
case "AssignmentExpression":
return this.compileAssignmentExpression(node);
case "MemberExpression":
return this.compileMemberExpression(node);
case "TryCatchStatement":
return this.compileTryCatchStatement(node);
case "Comment":
return this.compileComment(node);
default:
throw new Error(`Unknown node type: ${node.type}`);
}
}
compileProgram(node) {
return node.body.map((stmt) => this.compileNode(stmt)).join("\n");
}
compileVariableDeclaration(node) {
const isReassigned = this.isVariableReassigned(node.identifier);
const declarationType = isReassigned ? "let" : "const";
return `${this.indent()}${declarationType} ${node.identifier} = ${this.compileNode(node.value)};`;
}
isVariableReassigned(identifier) {
return false;
}
compileFunctionDeclaration(node) {
const name = node.name || "";
const params = node.params.map((param) => {
if (param.default) {
return `${param.name} = ${this.compileNode(param.default)}`;
}
return param.name;
}).join(", ");
return `${this.indent()}function ${name}(${params}) ${this.compileNode(node.body)}`;
}
compileBlockStatement(node) {
this.indentLevel++;
const body = node.body.map((stmt) => this.compileNode(stmt)).join("\n");
this.indentLevel--;
return `{
${body}
${this.indent()}}`;
}
compileExpressionStatement(node) {
return `${this.indent()}${this.compileNode(node.expression)};`;
}
compileCallExpression(node) {
const args2 = node.arguments.map((arg) => this.compileNode(arg)).join(", ");
return `${this.compileNode(node.callee)}(${args2})`;
}
compileMethodCallExpression(node) {
const args2 = node.arguments.map((arg) => this.compileNode(arg)).join(", ");
const methodTranslations = {
"lomba": "length",
"jog_koro": "push",
"ber_koro": "pop",
"suru_koro": "unshift",
"suru_ber_koro": "shift",
"khojo": "indexOf",
"ache": "includes"
};
const translatedMethod = methodTranslations[node.method] || node.method;
if (translatedMethod === "length") {
return `${this.compileNode(node.object)}.${translatedMethod}`;
}
return `${this.compileNode(node.object)}.${translatedMethod}(${args2})`;
}
compileBinaryExpression(node) {
const operator = this.translateOperator(node.operator);
return `${this.compileNode(node.left)} ${operator} ${this.compileNode(node.right)}`;
}
compileUnaryExpression(node) {
return `${node.operator}${this.compileNode(node.argument)}`;
}
compileIdentifier(node) {
const translations = {
"bolo": "console.log",
"shuno": "prompt",
"thik": "true",
"bhul": "false",
"faka": "null"
};
return translations[node.name] || node.name;
}
compileLiteral(node) {
if (node.value === null)
return "null";
if (typeof node.value === "boolean") {
return node.value ? "true" : "false";
}
return String(node.value);
}
compileStringLiteral(node) {
return `"${node.value}"`;
}
compileArrayLiteral(node) {
const elements = node.elements.map((elem) => this.compileNode(elem)).join(", ");
return `[${elements}]`;
}
compileIfStatement(node) {
let result = `${this.indent()}if (${this.compileNode(node.test)}) ${this.compileNode(node.consequent)}`;
if (node.alternate) {
if (node.alternate.type === "IfStatement") {
result += ` else ${this.compileIfStatement(node.alternate)}`;
} else {
result += ` else ${this.compileNode(node.alternate)}`;
}
}
return result;
}
compileWhileStatement(node) {
return `${this.indent()}while (${this.compileNode(node.test)}) ${this.compileNode(node.body)}`;
}
compileForStatement(node) {
const init = this.compileNode(node.init).replace("const", "let");
return `${this.indent()}for (${init} ${this.compileNode(node.test)}; ${this.compileNode(node.update)}) ${this.compileNode(node.body)}`;
}
compileReturnStatement(node) {
return `${this.indent()}return${node.argument ? " " + this.compileNode(node.argument) : ""};`;
}
compileBreakStatement(_node) {
return `${this.indent()}break;`;
}
compileContinueStatement(_node) {
return `${this.indent()}continue;`;
}
compileAssignmentExpression(node) {
const operator = this.translateOperator(node.operator);
return `${this.compileNode(node.left)} ${operator} ${this.compileNode(node.right)}`;
}
compileMemberExpression(node) {
if (node.computed) {
return `${this.compileNode(node.object)}[${this.compileNode(node.property)}]`;
}
return `${this.compileNode(node.object)}.${this.compileNode(node.property)}`;
}
compileTryCatchStatement(node) {
return `${this.indent()}try ${this.compileNode(node.tryBlock)}
${this.indent()}catch(${node.catchParam}) ${this.compileNode(node.catchBlock)}`;
}
compileComment(node) {
return node.multiline ? `${this.indent()}/*${node.value}*/` : `${this.indent()}//${node.value}`;
}
translateOperator(operator) {
const operatorMap = {
"jog": "+",
"biyog": "-",
"gun": "*",
"vag": "/",
"vag_shesh": "%",
"jog=": "+=",
"biyog=": "-=",
"gun=": "*=",
"vag=": "/=",
"vag_shesh=": "%="
};
return operatorMap[operator] || operator;
}
};
}
});
// node_modules/.pnpm/chalk@5.4.1/node_modules/chalk/source/vendor/ansi-styles/index.js
function assembleStyles() {
const codes = /* @__PURE__ */ new Map();
for (const [groupName, group] of Object.entries(styles)) {
for (const [styleName, style] of Object.entries(group)) {
styles[styleName] = {
open: `\x1B[${style[0]}m`,
close: `\x1B[${style[1]}m`
};
group[styleName] = styles[styleName];
codes.set(style[0], style[1]);
}
Object.defineProperty(styles, groupName, {
value: group,
enumerable: false
});
}
Object.defineProperty(styles, "codes", {
value: codes,
enumerable: false
});
styles.color.close = "\x1B[39m";
styles.bgColor.close = "\x1B[49m";
styles.color.ansi = wrapAnsi16();
styles.color.ansi256 = wrapAnsi256();
styles.color.ansi16m = wrapAnsi16m();
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
Object.defineProperties(styles, {
rgbToAnsi256: {
value(red, green, blue) {
if (red === green && green === blue) {
if (red < 8) {
return 16;
}
if (red > 248) {
return 231;
}
return Math.round((red - 8) / 247 * 24) + 232;
}
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
},
enumerable: false
},
hexToRgb: {
value(hex) {
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
if (!matches) {
return [0, 0, 0];
}
let [colorString] = matches;
if (colorString.length === 3) {
colorString = [...colorString].map((character) => character + character).join("");
}
const integer = Number.parseInt(colorString, 16);
return [
/* eslint-disable no-bitwise */
integer >> 16 & 255,
integer >> 8 & 255,
integer & 255
/* eslint-enable no-bitwise */
];
},
enumerable: false
},
hexToAnsi256: {
value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
enumerable: false
},
ansi256ToAnsi: {
value(code) {
if (code < 8) {
return 30 + code;
}
if (code < 16) {
re