Bhidu Language

TypeScript ISC

BhaiLang is an experimental, interpreted programming language designed for fun, learning, and exploring compiler/interpreter concepts through a uniquely desi syntax.

Stars
17
Forks
0
Downloads
5,118
Open Issues
0
Files main

Repository Files

Loading file structure...
src/cli.ts
#!/usr/bin/env node

import * as fs from "fs";
import * as path from "path";
import * as readline from "readline";
import { tokenize } from "./lexer";
import { Parser } from "./parser";
import { Interpreter } from "./interpreter";
import { Environment } from "./environment";
import { startDevServer, generateHTML } from "./server";

const version = "1.0.5";

function printHelp() {
  console.log(`
  Bhidu-Lang CLI v${version}
  
  Usage:
    bhidu run <file.bhidu>    Execute a .bhidu file
    bhidu ast <file.bhidu>    Print the Abstract Syntax Tree (AST) of a file
    bhidu repl                Start the interactive REPL
    bhidu hagde [project]     Scaffold a new Bhidu web app structure
    bhidu shuru hoja [file]   Start the live-reloading Dev Server (default: index.bhidu)
    bhidu faad de [file]      Compile project to a static HTML page in out/ (default: index.bhidu)
    bhidu help                Show this help message
    
  Example:
    bhidu shuru hoja
  `);
}

function runFile(filePath: string) {
  if (!filePath.endsWith(".bhidu")) {
    console.error("Kya re bhidu! File extension .bhidu hona chahiye!");
    process.exit(1);
  }

  const absolutePath = path.resolve(filePath);
  if (!fs.existsSync(absolutePath)) {
    console.error(`Kya re bhidu! File '${filePath}' mil hi nahi rahi. Sahi path dal!`);
    process.exit(1);
  }

  const code = fs.readFileSync(absolutePath, "utf-8");
  try {
    const tokens = tokenize(code);
    const parser = new Parser(tokens);
    const ast = parser.parse();
    const interpreter = new Interpreter();
    interpreter.interpret(ast);
  } catch (error: any) {
    console.error(error.message || error);
    process.exit(1);
  }
}

function printAST(filePath: string) {
  if (!filePath.endsWith(".bhidu")) {
    console.error("Kya re bhidu! File extension .bhidu hona chahiye!");
    process.exit(1);
  }

  const absolutePath = path.resolve(filePath);
  if (!fs.existsSync(absolutePath)) {
    console.error(`Kya re bhidu! File '${filePath}' mil hi nahi rahi.`);
    process.exit(1);
  }

  const code = fs.readFileSync(absolutePath, "utf-8");
  try {
    const tokens = tokenize(code);
    const parser = new Parser(tokens);
    const ast = parser.parse();
    console.log(JSON.stringify(ast, null, 2));
  } catch (error: any) {
    console.error(error.message || error);
    process.exit(1);
  }
}

function runREPL() {
  console.log(`=========================================`);
  console.log(`   Bhidu-Lang Interactive REPL v${version}`);
  console.log(`   Sab chalta hai bhidu! Type '.exit' to exit.`);
  console.log(`=========================================`);
  console.log("chalu kar bhidu");

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: "bhidu> ",
  });

  const interpreter = new Interpreter();
  const persistentEnv = new Environment();

  rl.prompt();

  rl.on("line", (line) => {
    const trimmed = line.trim();
    if (trimmed === ".exit") {
      console.log("khatam bhidu");
      rl.close();
      return;
    }

    if (!trimmed) {
      rl.prompt();
      return;
    }

    // Wrap the line in the entry/exit keywords so the parser accepts it
    const wrappedCode = `chalu kar bhidu\n${line}\nkhatam bhidu`;
    try {
      const tokens = tokenize(wrappedCode);
      const parser = new Parser(tokens);
      const ast = parser.parse();
      
      // Execute the parsed statements using the persistent environment
      interpreter.interpret(ast, persistentEnv);
    } catch (err: any) {
      console.error(err.message || err);
    }
    rl.prompt();
  });
}

function askQuestion(query: string): Promise<string> {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  return new Promise((resolve) => {
    rl.question(query, (answer) => {
      rl.close();
      resolve(answer.trim());
    });
  });
}

function getLogoSource(): string | null {
  const pkgLogo = path.join(__dirname, "..", "docs", "logo.png");
  if (fs.existsSync(pkgLogo)) {
    return pkgLogo;
  }
  const localLogo = path.resolve("docs/logo.png");
  if (fs.existsSync(localLogo)) {
    return localLogo;
  }
  return null;
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    runREPL();
    return;
  }

  const command = args[0];
  switch (command) {
    case "run":
      if (!args[1]) {
        console.error("Kya re bhidu! File name kaun daalega? Usage: bhidu run <file.bhidu>");
        process.exit(1);
      }
      runFile(args[1]);
      break;
    case "ast":
      if (!args[1]) {
        console.error("Kya re bhidu! File name kidhar hai? Usage: bhidu ast <file.bhidu>");
        process.exit(1);
      }
      printAST(args[1]);
      break;
    case "repl":
      runREPL();
      break;
    case "shuru": {
      if (args[1] !== "hoja") {
        console.error("Kya re bhidu! 'shuru' ke baad 'hoja' likhna bhool gaya? Usage: bhidu shuru hoja [file.bhidu]");
        process.exit(1);
      }
      const devFile = args[2] || "index.bhidu";
      const TEMPLATE_CODE = `chalu kar bhidu
  bhidu bolta hai("<div style='text-align: center; padding: 4rem 2rem; max-width: 600px; margin: 0 auto;'>");
  bhidu bolta hai("  <img src='logo.png' alt='logo' style='width: 120px; height: 120px; border-radius: 20px; margin-bottom: 2rem; border: 3px solid #8ee43f; box-shadow: 0 0 35px rgba(142, 228, 63, 0.4); object-fit: cover;'>");
  bhidu bolta hai("  <h1 style='font-size: 3.2rem; font-weight: 800; background: linear-gradient(135deg, #ffffff 40%, #8ee43f); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 0; margin-bottom: 0.5rem; letter-spacing: -1px;'>Bhidu App</h1>");
  bhidu bolta hai("  <p style='font-size: 1.25rem; color: #9ca3af; margin-bottom: 3rem; line-height: 1.6; max-width: 500px; margin-left: auto; margin-right: auto;'>Bole toh ekdum solid local web app re bhidu! HTML preview is fully ready on localhost. 🕶️</p>");
  bhidu bolta hai("  <div style='display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; text-align: left; max-width: 800px; width: 100%; margin: 0 auto 3rem;'>");
  bhidu bolta hai("    <div style='background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 1.5rem;'>");
  bhidu bolta hai("      <h3 style='color: #8ee43f; margin-top: 0; margin-bottom: 0.5rem; font-size: 1.15rem;'>📁 Get Started</h3>");
  bhidu bolta hai("      <p style='color: #9ca3af; font-size: 0.95rem; margin: 0; line-height: 1.5;'>Start editing <strong>index.bhidu</strong> or place files inside <strong>pages/</strong> to see changes refresh.</p>");
  bhidu bolta hai("    </div>");
  bhidu bolta hai("    <div style='background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 1.5rem;'>");
  bhidu bolta hai("      <h3 style='color: #8ee43f; margin-top: 0; margin-bottom: 0.5rem; font-size: 1.15rem;'>🔗 GitHub Repo</h3>");
  bhidu bolta hai("      <p style='color: #9ca3af; font-size: 0.95rem; margin: 0; line-height: 1.5;'>Check out the compiler code and give it a star: <a href=\\"https://github.com/ghostcompiler/bhidu-language\\" target=\\"_blank\\" style='color: #8ee43f; text-decoration: none; font-weight: 600;'>ghostcompiler/bhidu-language</a></p>");
  bhidu bolta hai("    </div>");
  bhidu bolta hai("  </div>");
  bhidu bolta hai("  <div style='display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;'>");
  bhidu bolta hai("    <span style='font-family: monospace; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.05); padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.85rem; color: #b6f376;'>dev: bhidu shuru hoja</span>");
  bhidu bolta hai("    <span style='font-family: monospace; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.05); padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.85rem; color: #b6f376;'>build: bhidu faad de</span>");
  bhidu bolta hai("  </div>");
  bhidu bolta hai("</div>");
khatam bhidu
`;

        if (!fs.existsSync(devFile)) {
          console.log(`⚠️ '${devFile}' file nahi mili. Apun ek basic index.bhidu bana raha hai re!`);
          
          // Generate public directory and copy logo if not exists
          const publicDir = path.resolve("public");
          if (!fs.existsSync(publicDir)) {
            fs.mkdirSync(publicDir);
          }
          const logoSrc = getLogoSource();
          if (logoSrc && !fs.existsSync(path.join(publicDir, "logo.png"))) {
            fs.copyFileSync(logoSrc, path.join(publicDir, "logo.png"));
          }
          
          fs.writeFileSync(devFile, TEMPLATE_CODE);
        }
        startDevServer(devFile, 3000);
      }
      break;
    case "faad": {
      if (args[1] !== "de") {
        console.error("Kya re bhidu! 'faad' ke baad 'de' likhna bhool gaya? Usage: bhidu faad de [file.bhidu]");
        process.exit(1);
      }
      const buildFile = args[2] || "index.bhidu";
      if (!fs.existsSync(buildFile)) {
        console.error(`Kya re bhidu! Build file '${buildFile}' mil hi nahi rahi!`);
        process.exit(1);
      }
      
      console.log(`⚙️ Building project from '${buildFile}'...`);
      try {
        const buildHtml = generateHTML(buildFile, false);
        const outDir = path.resolve("out");
        if (!fs.existsSync(outDir)) {
          fs.mkdirSync(outDir);
        }
        
        fs.writeFileSync(path.join(outDir, "index.html"), buildHtml);
        
        // Copy logo from public/ to out/ if exists
        const publicLogo = path.join("public", "logo.png");
        if (fs.existsSync(publicLogo)) {
          fs.copyFileSync(publicLogo, path.join(outDir, "logo.png"));
        } else {
          // Fallback: copy from docs/logo.png
          const docsLogo = path.resolve("docs/logo.png");
          if (fs.existsSync(docsLogo)) {
            fs.copyFileSync(docsLogo, path.join(outDir, "logo.png"));
          }
        }

        // Copy entire public/ folder to out/ recursively if it exists
        const publicDir = path.resolve("public");
        if (fs.existsSync(publicDir)) {
          fs.cpSync(publicDir, outDir, { recursive: true });
        }
        
        console.log(`✨ Project compiled and built successfully inside out/!`);
        console.log(`📁 View production output: out/index.html`);
      } catch (err: any) {
        console.error(`❌ Build failure: ${err.message || err}`);
        process.exit(1);
      }
      break;
    }
    case "hagde": {
      let projectName = args[1];
      if (!projectName) {
        projectName = await askQuestion("bhidu is project ka naam kya rkhna h wo to bta: ");
        if (!projectName) {
          projectName = "bhidu-app";
        }
      }

      // Start scaffolding
      try {
        const projectDir = path.resolve(projectName);
        const publicDir = path.join(projectDir, "public");
        const componentsDir = path.join(projectDir, "components");
        const pagesDir = path.join(projectDir, "pages");

        if (!fs.existsSync(projectDir)) {
          fs.mkdirSync(projectDir, { recursive: true });
        }
        if (!fs.existsSync(publicDir)) fs.mkdirSync(publicDir);
        if (!fs.existsSync(componentsDir)) fs.mkdirSync(componentsDir);
        if (!fs.existsSync(pagesDir)) fs.mkdirSync(pagesDir);

        // Copy logo
        const logoSrc = getLogoSource();
        if (logoSrc) {
          fs.copyFileSync(logoSrc, path.join(publicDir, "logo.png"));
        }

        const TEMPLATE_CODE = `chalu kar bhidu
  // Auto-scaffolded reactive state count variable
  bhidu ye hai count = 0;

  bhidu bolta hai("<div style='text-align: center; padding: 4rem 2rem; max-width: 600px; margin: 0 auto;'>");
  bhidu bolta hai("  <img src='logo.png' alt='logo' style='width: 120px; height: 120px; border-radius: 20px; margin-bottom: 2rem; border: 3px solid #8ee43f; box-shadow: 0 0 35px rgba(142, 228, 63, 0.4); object-fit: cover;'>");
  bhidu bolta hai("  <h1 style='font-size: 3.2rem; font-weight: 800; background: linear-gradient(135deg, #ffffff 40%, #8ee43f); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 0; margin-bottom: 0.5rem; letter-spacing: -1px;'>Bhidu App</h1>");
  bhidu bolta hai("  <p style='font-size: 1.25rem; color: #9ca3af; margin-bottom: 2.5rem; line-height: 1.6;'>Bole toh ekdum solid local web app re bhidu! HTML preview is fully ready on localhost. 🕶️</p>");

  // Counter UI with custom class from public/styles.css
  bhidu bolta hai("  <div style='background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 2rem; margin-bottom: 2.5rem;'>");
  bhidu bolta hai("    <h3 style='color: #8ee43f; margin-top: 0; margin-bottom: 0.75rem; font-size: 1.2rem;'>⚡ Reactive State</h3>");
  bhidu bolta hai("    <p style='color: #f3f4f6; font-size: 1.4rem; font-weight: bold; margin: 0;'>Count: " + count + "</p>");
  bhidu bolta hai("    <button class='bhidu-button' onclick=\\"bhiduSetState('count', count + 1)\\">Increment count</button>");
  bhidu bolta hai("  </div>");

  bhidu bolta hai("  <div style='display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; text-align: left; max-width: 800px; width: 100%; margin: 0 auto 3rem;'>");
  bhidu bolta hai("    <div style='background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 1.5rem;'>");
  bhidu bolta hai("      <h3 style='color: #8ee43f; margin-top: 0; margin-bottom: 0.5rem; font-size: 1.15rem;'>📁 Get Started</h3>");
  bhidu bolta hai("      <p style='color: #9ca3af; font-size: 0.95rem; margin: 0; line-height: 1.5;'>Start editing <strong>index.bhidu</strong> or place files inside <strong>pages/</strong> to see changes refresh.</p>");
  bhidu bolta hai("    </div>");
  bhidu bolta hai("    <div style='background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 1.5rem;'>");
  bhidu bolta hai("      <h3 style='color: #8ee43f; margin-top: 0; margin-bottom: 0.5rem; font-size: 1.15rem;'>🔗 GitHub Repo</h3>");
  bhidu bolta hai("      <p style='color: #9ca3af; font-size: 0.95rem; margin: 0; line-height: 1.5;'>Check out the compiler code and give it a star: <a href=\\"https://github.com/ghostcompiler/bhidu-language\\" target=\\"_blank\\" style='color: #8ee43f; text-decoration: none; font-weight: 600;'>ghostcompiler/bhidu-language</a></p>");
  bhidu bolta hai("    </div>");
  bhidu bolta hai("  </div>");

  bhidu bolta hai("  <div style='display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;'>");
  bhidu bolta hai("    <span style='font-family: monospace; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.05); padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.85rem; color: #b6f376;'>dev: bhidu shuru hoja</span>");
  bhidu bolta hai("    <span style='font-family: monospace; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.05); padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.85rem; color: #b6f376;'>build: bhidu faad de</span>");
  bhidu bolta hai("  </div>");
  bhidu bolta hai("</div>");
khatam bhidu
`;

        const TEMPLATE_CSS = `/* Bhidu App Component Styles */
.bhidu-button {
  background: linear-gradient(135deg, #8ee43f, #76c92d);
  color: #07080b;
  border: none;
  padding: 0.6rem 1.4rem;
  font-size: 0.95rem;
  font-weight: bold;
  border-radius: 8px;
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(142, 228, 63, 0.25);
  transition: all 0.2s ease-in-out;
  margin-top: 1rem;
}

.bhidu-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(142, 228, 63, 0.45);
}

.bhidu-button:active {
  transform: translateY(1px);
}
`;

        // Create READMEs
        fs.writeFileSync(
          path.join(componentsDir, "README.md"),
          `# Components Folder\n\nPlace your modular components here re bhidu!\n`
        );
        fs.writeFileSync(
          path.join(pagesDir, "README.md"),
          `# Pages Folder\n\nPlace your page scripts and routing resources here re bhidu!\n`
        );

        // Create default styles.css inside public/
        fs.writeFileSync(path.join(publicDir, "styles.css"), TEMPLATE_CSS);

        // Create index.bhidu
        fs.writeFileSync(path.join(projectDir, "index.bhidu"), TEMPLATE_CODE);

        const absoluteProjectDir = path.resolve(projectDir);
        console.log(`\nCreating a new Bhidu app in \x1b[1m\x1b[32m${absoluteProjectDir}\x1b[0m.\n`);
        console.log(`Initializing project...`);
        console.log(`  \x1b[36m✔\x1b[0m Creating project directory: \x1b[90m${projectName}/\x1b[0m`);
        console.log(`  \x1b[36m✔\x1b[0m Creating subfolders: \x1b[90mpublic/, components/, pages/\x1b[0m`);
        console.log(`  \x1b[36m✔\x1b[0m Creating entry file: \x1b[90m${projectName}/index.bhidu\x1b[0m`);
        console.log(`  \x1b[36m✔\x1b[0m Copying brand assets: \x1b[90mpublic/logo.png\x1b[0m`);
        console.log(`  \x1b[36m✔\x1b[0m Writing folder documentation: \x1b[90mcomponents/README.md, pages/README.md\x1b[0m`);
        
        console.log(`\n\x1b[1m\x1b[32mSuccess!\x1b[0m Created \x1b[1m${projectName}\x1b[0m at \x1b[32m${absoluteProjectDir}\x1b[0m\n`);
        console.log(`Inside that directory, you can run several commands:\n`);
        console.log(`  \x1b[1mbhidu shuru hoja\x1b[0m`);
        console.log(`    Starts the live-reloading development server.\n`);
        console.log(`  \x1b[1mbhidu faad de\x1b[0m`);
        console.log(`    Builds the production-ready static HTML bundle.\n`);
        console.log(`We suggest that you begin by typing:\n`);
        console.log(`  \x1b[36mcd\x1b[0m ${projectName}`);
        console.log(`  \x1b[36mbhidu shuru hoja\x1b[0m\n`);
        console.log(`🕶️  \x1b[1mHappy coding, bhidu!\x1b[0m\n`);
      } catch (err: any) {
        console.error(`❌ Scaffolding failed: ${err.message || err}`);
        process.exit(1);
      }
      break;
    }
    case "help":
    case "-h":
    case "--help":
      printHelp();
      break;
    default:
      // If the first argument is a file, run it directly (e.g. bhidu hello.bhidu)
      if (command.endsWith(".bhidu") || fs.existsSync(command)) {
        runFile(command);
      } else {
        console.error(`Kya re bhidu! '${command}' kaunsa command hai?`);
        printHelp();
        process.exit(1);
      }
      break;
  }
}

main();