UNPKG

petcarescript

Version:

PetCareScript - A modern, expressive programming language designed for humans

460 lines (363 loc) โ€ข 12.4 kB
#!/usr/bin/env node /** * PetCareScript CLI * Command Line Interface */ const fs = require('fs'); const path = require('path'); const readline = require('readline'); const PetCareScript = require('../src/petcarescript'); class PetCareScriptCLI { constructor() { this.pcs = new PetCareScript(); this.version = '1.0.0'; } run() { const args = process.argv.slice(2); if (args.length === 0) { this.startRepl(); } else { this.handleArguments(args); } } handleArguments(args) { const command = args[0]; switch (command) { case '--version': case '-v': console.log(`PetCareScript v${this.version}`); break; case '--help': case '-h': this.showHelp(); break; case 'init': this.initProject(args[1]); break; case 'run': this.runFile(args[1]); break; case 'repl': this.startRepl(); break; case 'compile': this.compileFile(args[1], args[2]); break; case 'test': this.runTests(args[1]); break; case 'check': this.checkSyntax(args[1]); break; case 'format': this.formatFile(args[1]); break; default: if (command.endsWith('.pcs')) { this.runFile(command); } else { console.error(`Unknown command: ${command}`); console.error('Use --help to see available commands.'); process.exit(1); } break; } } showHelp() { console.log(` PetCareScript v${this.version} A modern and expressive programming language USAGE: pcs [command] [options] COMMANDS: <file.pcs> Execute a PetCareScript file run <file.pcs> Execute a PetCareScript file repl Start interactive REPL init [name] Create a new PetCareScript project compile <src> <dest> Compile to JavaScript test [folder] Run tests check <file.pcs> Check syntax without executing format <file.pcs> Format PetCareScript file OPTIONS: -h, --help Show this help -v, --version Show version EXAMPLES: pcs myfile.pcs # Execute myfile.pcs pcs repl # Start REPL pcs init myapp # Create new project pcs test # Run all tests `); } runFile(filename) { if (!filename) { console.error('Error: Filename is required'); process.exit(1); } if (!fs.existsSync(filename)) { console.error(`Error: File '${filename}' not found`); process.exit(1); } console.log(`๐Ÿš€ Executing ${filename}...`); const result = this.pcs.runFile(filename); if (!result.success) { console.error(`โŒ Error: ${result.error}`); process.exit(1); } console.log('โœ… Execution completed successfully'); } checkSyntax(filename) { if (!filename) { console.error('Error: Filename is required'); process.exit(1); } if (!fs.existsSync(filename)) { console.error(`Error: File '${filename}' not found`); process.exit(1); } try { const source = fs.readFileSync(filename, 'utf8'); const Tokenizer = require('../src/lexer/tokenizer'); const Parser = require('../src/parser/parser'); // Try to tokenize and parse const tokenizer = new Tokenizer(source); const tokens = tokenizer.tokenize(); const parser = new Parser(tokens); const ast = parser.parse(); console.log(`โœ… Syntax check passed for ${filename}`); } catch (error) { console.error(`โŒ Syntax error in ${filename}: ${error.message}`); process.exit(1); } } formatFile(filename) { console.log(`๐ŸŽจ Formatting ${filename}...`); console.log('โš ๏ธ Formatter not yet implemented'); // TODO: Implement code formatter } startRepl() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'pcs> ' }); console.log(`PetCareScript REPL v${this.version}`); console.log('Type "exit", "quit" or Ctrl+C to quit.'); console.log('Type "help" for available commands.\n'); rl.prompt(); rl.on('line', (line) => { const input = line.trim(); if (input === 'exit' || input === 'quit') { rl.close(); return; } if (input === 'help') { this.showReplHelp(); rl.prompt(); return; } if (input === 'clear' || input === 'cls') { console.clear(); rl.prompt(); return; } if (input === 'version') { console.log(`PetCareScript v${this.version}`); rl.prompt(); return; } if (input === '') { rl.prompt(); return; } try { const result = this.pcs.run(input); if (!result.success) { console.error(`โŒ ${result.error}`); } } catch (error) { console.error(`โŒ ${error.message}`); } rl.prompt(); }); rl.on('close', () => { console.log('\n๐Ÿ‘‹ Goodbye!'); process.exit(0); }); rl.on('SIGINT', () => { console.log('\n๐Ÿ‘‹ Goodbye!'); process.exit(0); }); } showReplHelp() { console.log(` REPL COMMANDS: help Show this help clear, cls Clear screen version Show version exit, quit Exit REPL EXAMPLES: store x = 42; # Define a variable show x; # Print the value build hello() { show "Hello!"; } # Define function hello(); # Call function `); } initProject(projectName = 'my-pcs-project') { const projectDir = path.resolve(projectName); if (fs.existsSync(projectDir)) { console.error(`Error: Directory '${projectName}' already exists`); process.exit(1); } console.log(`๐Ÿ“ Creating project '${projectName}'...`); // Create project structure fs.mkdirSync(projectDir); fs.mkdirSync(path.join(projectDir, 'src')); fs.mkdirSync(path.join(projectDir, 'tests')); // Main file const mainFile = `// ${projectName} // Main PetCareScript project file build main() { show "Hello, PetCareScript!"; show "Project: ${projectName}"; store message = "Welcome to your new project!"; show message; } main(); `; fs.writeFileSync(path.join(projectDir, 'src', 'main.pcs'), mainFile); // Package.json const packageJson = { name: projectName, version: '1.0.0', description: `PetCareScript project: ${projectName}`, main: 'src/main.pcs', scripts: { start: 'pcs src/main.pcs', test: 'pcs test tests/' }, dependencies: { petcarescript: `^${this.version}` } }; fs.writeFileSync( path.join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2) ); // README const readme = `# ${projectName} Project created with PetCareScript. ## Run \`\`\`bash npm start # or pcs src/main.pcs \`\`\` ## Structure - \`src/\` - Source code - \`tests/\` - Tests - \`package.json\` - Project configuration ## Documentation Visit [petcarescript.org](https://petcarescript.org) for complete documentation. `; fs.writeFileSync(path.join(projectDir, 'README.md'), readme); // Test file const testFile = `// Tests for ${projectName} build testBasic() { show "Running basic test..."; store result = 2 + 2; when (result == 4) { show "โœ… Test passed: 2 + 2 = 4"; } otherwise { show "โŒ Test failed"; } } testBasic(); `; fs.writeFileSync(path.join(projectDir, 'tests', 'test.pcs'), testFile); console.log(`โœ… Project '${projectName}' created successfully!`); console.log(`\n๐Ÿ“ Next steps:`); console.log(` cd ${projectName}`); console.log(` pcs src/main.pcs`); } compileFile(srcFile, destFile) { if (!srcFile) { console.error('Error: Source file is required'); process.exit(1); } if (!destFile) { destFile = srcFile.replace('.pcs', '.js'); } console.log(`๐Ÿ”„ Compiling ${srcFile} -> ${destFile}...`); try { const source = fs.readFileSync(srcFile, 'utf8'); // Basic conversion to JavaScript let jsCode = this.transpileToJS(source); fs.writeFileSync(destFile, jsCode); console.log(`โœ… Compilation completed: ${destFile}`); } catch (error) { console.error(`โŒ Compilation error: ${error.message}`); process.exit(1); } } transpileToJS(pcsCode) { // Basic PetCareScript -> JavaScript conversion let jsCode = pcsCode; // Simple substitutions jsCode = jsCode.replace(/store /g, 'let '); jsCode = jsCode.replace(/build /g, 'function '); jsCode = jsCode.replace(/when \(/g, 'if ('); jsCode = jsCode.replace(/otherwise/g, 'else'); jsCode = jsCode.replace(/repeat \(/g, 'while ('); jsCode = jsCode.replace(/loop \(/g, 'for ('); jsCode = jsCode.replace(/give /g, 'return '); jsCode = jsCode.replace(/show /g, 'console.log'); jsCode = jsCode.replace(/yes/g, 'true'); jsCode = jsCode.replace(/no/g, 'false'); jsCode = jsCode.replace(/empty/g, 'null'); jsCode = jsCode.replace(/also /g, '&& '); jsCode = jsCode.replace(/either /g, '|| '); jsCode = jsCode.replace(/blueprint /g, 'class '); jsCode = jsCode.replace(/self\./g, 'this.'); return `// Generated from PetCareScript // Do not edit this file directly ${jsCode} `; } runTests(testDir = './tests') { console.log(`๐Ÿงช Running tests in ${testDir}...`); if (!fs.existsSync(testDir)) { console.error(`Error: Test directory '${testDir}' not found`); process.exit(1); } const testFiles = fs.readdirSync(testDir) .filter(file => file.endsWith('.pcs')) .map(file => path.join(testDir, file)); if (testFiles.length === 0) { console.log('โš ๏ธ No test files found'); return; } let passed = 0; let failed = 0; for (const testFile of testFiles) { console.log(`\n๐Ÿ” Running ${path.basename(testFile)}...`); const result = this.pcs.runFile(testFile); if (result.success) { passed++; console.log(`โœ… ${path.basename(testFile)} passed`); } else { failed++; console.log(`โŒ ${path.basename(testFile)} failed: ${result.error}`); } } console.log(`\n๐Ÿ“Š Results:`); console.log(` โœ… Passed: ${passed}`); console.log(` โŒ Failed: ${failed}`); console.log(` ๐Ÿ“ Total: ${testFiles.length}`); if (failed > 0) { process.exit(1); } } } // Execute CLI const cli = new PetCareScriptCLI(); cli.run();