Skip to content

Commit 2f02107

Browse files
committed
feat: Implement initial CLI structure and install core dependencies.
1 parent 1baad98 commit 2f02107

191 files changed

Lines changed: 25493 additions & 60 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,14 @@ main();
114114
```
115115

116116
### 2. Run It
117+
Using the CLI:
117118
```bash
118-
prox hello.prox
119+
prox run hello.prox
120+
```
121+
122+
Or using the legacy direct execution:
123+
```bash
124+
./proxpl hello.prox
119125
```
120126

121127
### 3. Build It (Coming Soon)
@@ -133,14 +139,25 @@ Your lucky number is: 42
133139

134140
## 📥 Installation
135141

136-
### Option 1: Pre-built Binaries (Recommended)
142+
### Option 1: CLI via Node.js (Recommended for Developers)
143+
The ProXPL CLI provides an enhanced experience with watch mode and better logging.
144+
145+
```bash
146+
cd src/cli
147+
npm install
148+
npm link
149+
```
150+
151+
Now you can use the `prox` command globally.
152+
153+
### Option 2: Pre-built Binaries
137154
No compilation required. Download the latest release for your OS:
138155

139-
* **Windows**: [Download `prox.exe`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
140-
* **Linux**: [Download `prox`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
141-
* **macOS**: [Download `prox-macos`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
156+
* **Windows**: [Download `proxpl.exe`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
157+
* **Linux**: [Download `proxpl`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
158+
* **macOS**: [Download `proxpl-macos`](https://github.com/ProgrammerKR/ProXPL/releases/latest)
142159

143-
Add the executable to your system `PATH` to run it from anywhere.
160+
Add the executable to your system `PATH` to run it as `proxpl`.
144161

145162
### Option 2: Build from Source (CMake)
146163
Requirements: **GCC/Clang** (C99+), **CMake** 3.10+, **Git**.

examples/cli_test.prox

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// examples/cli_test.prox
2+
func main() {
3+
print("CLI Test Script");
4+
let x = 10;
5+
let y = 20;
6+
print("Sum: " + to_string(x + y));
7+
8+
if (x < y) {
9+
print("x is less than y");
10+
}
11+
}
12+
13+
main();

src/cli/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# ProXPL CLI
2+
3+
Professional Command-Line Interface for the ProX Programming Language.
4+
5+
## Features
6+
7+
- **Run Script**: Execute `.prox` files with `prox run <file>`.
8+
- **REPL**: Interactive shell with `prox repl` or by running `prox` without arguments.
9+
- **Build**: Compile scripts to bytecode (currently a placeholder for future VM support).
10+
- **Watch Mode**: Automatically rerun scripts on change with `--watch`.
11+
- **Debug/Verbose**: Get detailed execution info with `--debug` and `--verbose`.
12+
- **Config**: Support for `.proxrc` or `prox.json` configuration files.
13+
14+
## Installation
15+
16+
```bash
17+
cd src/cli
18+
npm install
19+
npm link
20+
```
21+
22+
## Usage
23+
24+
### Running a script
25+
```bash
26+
prox run examples/hello.prox
27+
```
28+
29+
### Watch mode
30+
```bash
31+
prox run main.prox --watch
32+
```
33+
34+
### REPL
35+
```bash
36+
prox repl
37+
```
38+
39+
### Help
40+
```bash
41+
prox --help
42+
```
43+
44+
## Configuration
45+
46+
You can create a `.proxrc` or `prox.json` in your project root to set default options:
47+
48+
```json
49+
{
50+
"debug": true,
51+
"verbose": false
52+
}
53+
```
54+
55+
## Development
56+
57+
The CLI is built with Node.js and uses the `commander` package. It interacts with the C-based ProXPL core by spawning it as a child process.
58+
59+
### Directory Structure
60+
- `index.js`: Main entry point.
61+
- `commands/`: Individual command implementations.
62+
- `utils/`: Helper utilities (logger, runner, config).

src/cli/commands/build.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { runProXPL } from '../utils/runner.js';
2+
import { logger } from '../utils/logger.js';
3+
import path from 'path';
4+
import fs from 'fs-extra';
5+
6+
export async function buildCommand(file, options) {
7+
if (!file) {
8+
logger.error('No file specified.', 'Usage: prox build <file>');
9+
process.exit(1);
10+
}
11+
12+
const filePath = path.resolve(file);
13+
14+
if (!await fs.pathExists(filePath)) {
15+
logger.error(`File not found: ${file}`);
16+
process.exit(1);
17+
}
18+
19+
logger.info(`Compiling ${path.basename(file)}...`);
20+
21+
try {
22+
// Current C implementation has a stub for build
23+
await runProXPL(['build', filePath]);
24+
logger.success('Build successful.');
25+
} catch (err) {
26+
process.exit(1);
27+
}
28+
}

src/cli/commands/repl.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { runProXPL } from '../utils/runner.js';
2+
import { logger } from '../utils/logger.js';
3+
4+
export async function replCommand() {
5+
logger.info('Starting ProXPL REPL...');
6+
try {
7+
await runProXPL([]);
8+
} catch (err) {
9+
process.exit(1);
10+
}
11+
}

src/cli/commands/run.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { runProXPL } from '../utils/runner.js';
2+
import { logger } from '../utils/logger.js';
3+
import chokidar from 'chokidar';
4+
import path from 'path';
5+
import fs from 'fs-extra';
6+
7+
export async function runCommand(file, options) {
8+
if (!file) {
9+
logger.error('No file specified.', 'Usage: prox run <file>');
10+
process.exit(1);
11+
}
12+
13+
const filePath = path.resolve(file);
14+
15+
if (!await fs.pathExists(filePath)) {
16+
logger.error(`File not found: ${file}`);
17+
process.exit(1);
18+
}
19+
20+
if (options.watch) {
21+
logger.info(`Watching for changes in ${path.basename(file)}...`);
22+
23+
const watcher = chokidar.watch(filePath);
24+
25+
let isRunning = false;
26+
27+
const execute = async () => {
28+
if (isRunning) return;
29+
isRunning = true;
30+
31+
console.clear();
32+
logger.info(`Rerunning ${path.basename(file)}...`);
33+
34+
try {
35+
await runProXPL(['run', filePath]);
36+
logger.success('Execution finished.');
37+
} catch (err) {
38+
// Error already logged by runner stdio:inherit
39+
} finally {
40+
isRunning = false;
41+
logger.dim('\nWaiting for changes...');
42+
}
43+
};
44+
45+
watcher.on('change', execute);
46+
execute(); // Initial run
47+
} else {
48+
try {
49+
await runProXPL(['run', filePath]);
50+
} catch (err) {
51+
process.exit(1);
52+
}
53+
}
54+
}

src/cli/index.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env node
2+
3+
import { Command } from 'commander';
4+
import { runCommand } from './commands/run.js';
5+
import { buildCommand } from './commands/build.js';
6+
import { replCommand } from './commands/repl.js';
7+
import { logger } from './utils/logger.js';
8+
import fs from 'fs-extra';
9+
import path from 'path';
10+
import { fileURLToPath } from 'url';
11+
12+
import { loadConfig, mergeOptions } from './utils/config.js';
13+
14+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
15+
const packageJson = fs.readJsonSync(path.join(__dirname, 'package.json'));
16+
const config = await loadConfig();
17+
18+
const program = new Command();
19+
20+
program
21+
.name('prox')
22+
.description('ProXPL CLI - Professional Command-Line Interface for ProX Programming Language')
23+
.version(packageJson.version, '-v, --version');
24+
25+
program
26+
.command('run')
27+
.alias('r')
28+
.description('Execute a .prox script')
29+
.argument('<file>', 'Path to the .prox script')
30+
.option('-w, --watch', 'Watch mode: automatically rerun on file changes')
31+
.option('-d, --debug', 'Show detailed debug information')
32+
.option('-v, --verbose', 'Show verbose execution info')
33+
.action((file, options) => {
34+
const merged = mergeOptions(options, config);
35+
runCommand(file, merged);
36+
});
37+
38+
program
39+
.command('build')
40+
.alias('b')
41+
.description('Compile a .prox script to bytecode')
42+
.argument('<file>', 'Path to the .prox script')
43+
.option('-o, --output <output>', 'Specified output file')
44+
.action((file, options) => {
45+
const merged = mergeOptions(options, config);
46+
buildCommand(file, merged);
47+
});
48+
49+
program
50+
.command('repl')
51+
.description('Start interactive ProXPL shell')
52+
.action(() => {
53+
replCommand();
54+
});
55+
56+
// Handle unknown commands
57+
program.on('command:*', () => {
58+
logger.error('Invalid command: ' + program.args.join(' '));
59+
logger.info('See "prox --help" for available commands.');
60+
process.exit(1);
61+
});
62+
63+
// Default behavior: run repl if no args, or run file if first arg is a file
64+
if (process.argv.length === 2) {
65+
replCommand();
66+
} else {
67+
program.parse(process.argv);
68+
}

0 commit comments

Comments
 (0)