cajyu 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,57 +2,75 @@
2
2
 
3
3
  **A clean, practical programming language that transpiles to JavaScript.**
4
4
 
5
- Cajyu is designed for people who want a simpler syntax without leaving the JavaScript ecosystem. Write expressive code, get real JavaScript output, and use a growing set of official libraries.
5
+ Cajyu gives you a simpler, more expressive syntax while staying fully compatible with the JavaScript ecosystem.
6
6
 
7
7
  [![npm](https://img.shields.io/badge/npm-cajyu-blue)](https://www.npmjs.com/package/cajyu)
8
8
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
9
+ [![Version](https://img.shields.io/badge/version-1.1.0-brightgreen)](package.json)
9
10
 
10
11
  ---
11
12
 
12
13
  ## Features
13
14
 
14
- - **Familiar yet cleaner syntax** — variables, functions, classes, control flow, and loops that feel natural
15
- - **Transpiles to modern JavaScript** — runs anywhere Node.js runs
16
- - **Real HTTP server** — built-in `Server` class with POST handlers, sleep/wake, and static files
17
- - **Official libraries** — `serial`, `fs`, `math`, `http` (and more coming)
18
- - **Optional typing** — runtime type checks with `var x:Number = 10`
19
- - **Async support** — `sync fn` and `wait`
20
- - **One-liners & stacking** — concise style when you want it
21
- - **CLI** — `cajyu init`, `cajyu inst`, `cajyu run`
15
+ - Clean syntax for variables, functions, classes, loops and conditionals
16
+ - Transpiles to modern JavaScript
17
+ - Real HTTP server with POST handlers, sleep/wake
18
+ - WebSocket that automatically sends and receives JSON
19
+ - Official libraries: `serial`, `fs`, `math`, `http`, `websocket`, `string`, `json`, `exec`, `fetch`
20
+ - Optional runtime typing
21
+ - Async with `sync fn` and `wait`
22
+ - One-liners and stacking
23
+ - CLI with flags and multiple commands
22
24
 
23
25
  ---
24
26
 
25
27
  ## Quick Start
26
28
 
27
29
  ```bash
28
- # Install the CLI (once published)
30
+ # After publishing
29
31
  npm install -g cajyu
30
32
 
31
- # Or run directly from this repo
33
+ # Or from source
32
34
  node bin/cajyu.js --help
33
35
  ```
34
36
 
35
- Create a new project:
36
-
37
37
  ```bash
38
38
  cajyu init
39
+ cajyu inst serial
40
+ cajyu inst http
41
+ cajyu run examples/fatorial.cjy
39
42
  ```
40
43
 
41
- Run a file:
44
+ ---
42
45
 
43
- ```bash
44
- cajyu run examples/fatorial.cjy
46
+ ## CLI
47
+
48
+ ```
49
+ cajyu [flags]
50
+ cajyu <command> [options]
45
51
  ```
46
52
 
53
+ | Command | Description |
54
+ |------------------|--------------------------------------|
55
+ | `init` | Create a new project |
56
+ | `inst <lib>` | Install a library from CajyuLibs |
57
+ | `run <file.cjy>` | Compile and run a file |
58
+ | `list` | List installed libraries |
59
+ | `update` | Update libraries (planned) |
60
+
61
+ | Flag | Description |
62
+ |------------------|----------------------|
63
+ | `-h, --help` | Show help |
64
+ | `-v, --version` | Show version |
65
+
47
66
  ---
48
67
 
49
- ## Syntax Overview
68
+ ## Syntax
50
69
 
51
70
  ### Variables
52
71
  ```cajyu
53
72
  var name = "Cajyu"
54
73
  var age:Number = 17
55
- var items = [1, 2, 3]
56
74
  ```
57
75
 
58
76
  ### Functions
@@ -60,13 +78,9 @@ var items = [1, 2, 3]
60
78
  fn add a, b {
61
79
  return a + b
62
80
  }
63
-
64
- fn double x {
65
- return x * 2
66
- }
67
81
  ```
68
82
 
69
- ### Control Flow
83
+ ### Control flow & loops
70
84
  ```cajyu
71
85
  if x > 10 {
72
86
  print("big")
@@ -75,17 +89,10 @@ if x > 10 {
75
89
  } else {
76
90
  print("small")
77
91
  }
78
- ```
79
92
 
80
- ### Loops
81
- ```cajyu
82
93
  for i in range(10) {
83
94
  print(i)
84
95
  }
85
-
86
- while x < 100 {
87
- x = x * 2
88
- }
89
96
  ```
90
97
 
91
98
  ### Classes
@@ -96,26 +103,14 @@ class Person {
96
103
  Self.age = age
97
104
  }
98
105
  greet {
99
- return "Hello, I'm " + Self.name
106
+ return "Hello, " + Self.name
100
107
  }
101
108
  }
102
-
103
- var p = new Person("Ana", 17)
104
- print(p.greet())
105
- ```
106
-
107
- ### Async
108
- ```cajyu
109
- sync fn fetchData {
110
- wait someAsyncCall()
111
- return result
112
- }
113
109
  ```
114
110
 
115
- ### HTTP Server
111
+ ### HTTP
116
112
  ```cajyu
117
113
  import Server from CajyuLibs.http
118
- import print from CajyuLibs.serial
119
114
 
120
115
  var server = new Server(3000)
121
116
 
@@ -123,67 +118,58 @@ server.post(fn b {
123
118
  if !b.a and !b.b {
124
119
  return { code: 400 }
125
120
  }
126
- return {
127
- code: 200,
128
- result: b.a + b.b
129
- }
121
+ return { code: 200, result: b.a + b.b }
130
122
  })
131
123
  ```
132
124
 
133
- ---
134
-
135
- ## Libraries
136
-
137
- Official libraries live in the [CajyuLibs](https://github.com/abs-js/CajyuLibs) repository.
138
-
139
- Install a library:
125
+ ### WebSocket (JSON)
126
+ ```cajyu
127
+ import WS from CajyuLibs.websocket
140
128
 
141
- ```bash
142
- cajyu inst serial
143
- cajyu inst http
129
+ var wss = new WS.Server(8080)
130
+ wss.on("message", fn msg, client {
131
+ wss.broadcast({ echo: msg })
132
+ })
144
133
  ```
145
134
 
146
- Import in your code:
147
-
135
+ ### Exec & Fetch
148
136
  ```cajyu
149
- import print from CajyuLibs.serial
150
- import Server from CajyuLibs.http
137
+ import exec from CajyuLibs.exec
138
+ var files = exec.exec("ls -a")
139
+
140
+ import fetch from CajyuLibs.fetch
141
+ fetch.fetch("https://api.github.com", {}, null, fn result, ms {
142
+ print(result)
143
+ })
151
144
  ```
152
145
 
153
146
  ---
154
147
 
155
- ## CLI Commands
156
-
157
- | Command | Description |
158
- |---------|-------------|
159
- | `cajyu init` | Create a new project (`package.json` + structure) |
160
- | `cajyu inst <name>` | Install a library from CajyuLibs |
161
- | `cajyu run <file.cjy>` | Compile and run a Cajyu file |
162
-
163
- ---
148
+ ## Libraries
164
149
 
165
- ## Project Structure
150
+ Official libraries: https://github.com/abs-js/CajyuLibs
166
151
 
167
- ```
168
- Cajyu/
169
- ├── bin/cajyu.js # CLI entry point
170
- ├── src/compiler.js # Transpiler
171
- ├── examples/ # Working examples
172
- │ ├── fatorial.cjy
173
- │ ├── classe.cjy
174
- │ └── servidor-post.cjy
175
- └── package.json
152
+ ```bash
153
+ cajyu inst serial
154
+ cajyu inst fs
155
+ cajyu inst math
156
+ cajyu inst http
157
+ cajyu inst websocket
158
+ cajyu inst string
159
+ cajyu inst json
160
+ cajyu inst exec
161
+ cajyu inst fetch
176
162
  ```
177
163
 
178
164
  ---
179
165
 
180
166
  ## Contributing
181
167
 
182
- Cajyu is early but intentional. Pull requests are welcome — especially for:
168
+ Pull requests are welcome. Priority areas:
183
169
 
184
- - A proper parser (currently regex-based)
170
+ - A proper parser (current one is regex-based)
185
171
  - More language features
186
- - New official libraries
172
+ - Additional libraries
187
173
  - Better error messages
188
174
 
189
175
  ---
@@ -191,7 +177,3 @@ Cajyu is early but intentional. Pull requests are welcome — especially for:
191
177
  ## License
192
178
 
193
179
  MIT
194
-
195
- ---
196
-
197
- **Built for people who like clean code and real results.**
package/bin/cajyu.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { execSync } from 'child_process';
6
+ import { compileAndRun } from '../src/compiler.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const CAJYULIBS_REPO = 'https://github.com/abs-js/CajyuLibs.git';
12
+ const VERSION = '1.1.0';
13
+
14
+ const args = process.argv.slice(2);
15
+ const command = args[0];
16
+ const flags = args.filter(a => a.startsWith('-'));
17
+
18
+ function hasFlag(...names) {
19
+ return names.some(n => flags.includes(n) || flags.includes(`-${n}`) || flags.includes(`--${n}`));
20
+ }
21
+
22
+ async function main() {
23
+ if (hasFlag('v', 'version', '--version')) {
24
+ console.log(`Cajyu ${VERSION}`);
25
+ return;
26
+ }
27
+
28
+ if (hasFlag('h', 'help', '--help') || !command) {
29
+ showHelp();
30
+ return;
31
+ }
32
+
33
+ if (command === 'init') {
34
+ initProject();
35
+ } else if (command === 'inst' || command === 'install') {
36
+ const libName = args[1];
37
+ if (!libName || libName.startsWith('-')) {
38
+ console.error('Usage: cajyu inst <library>');
39
+ process.exit(1);
40
+ }
41
+ await installLib(libName);
42
+ } else if (command === 'run') {
43
+ const file = args.find(a => a.endsWith('.cjy') || (a !== 'run' && !a.startsWith('-')));
44
+ if (!file) {
45
+ console.error('Usage: cajyu run <file.cjy>');
46
+ process.exit(1);
47
+ }
48
+ await compileAndRun(file);
49
+ } else if (command === 'list') {
50
+ listInstalled();
51
+ } else if (command === 'update') {
52
+ console.log('Updating is not yet implemented. Re-run `cajyu inst <lib>` after deleting the folder.');
53
+ } else {
54
+ console.error(`Unknown command: ${command}`);
55
+ showHelp();
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ function showHelp() {
61
+ console.log(`
62
+ Cajyu ${VERSION} — a custom programming language that transpiles to JavaScript
63
+
64
+ Usage:
65
+ cajyu [flags]
66
+ cajyu <command> [options]
67
+
68
+ Commands:
69
+ init Create a new Cajyu project
70
+ inst <lib> Install a library from CajyuLibs
71
+ run <file.cjy> Compile and execute a Cajyu file
72
+ list List installed libraries
73
+ update Update installed libraries (coming soon)
74
+
75
+ Flags:
76
+ -h, --help Show this help
77
+ -v, --version Show version
78
+
79
+ Examples:
80
+ cajyu init
81
+ cajyu inst http
82
+ cajyu inst websocket
83
+ cajyu run examples/fatorial.cjy
84
+ cajyu list
85
+ `);
86
+ }
87
+
88
+ function initProject() {
89
+ const pkg = {
90
+ name: "cajyu-project",
91
+ version: "1.0.0",
92
+ description: "Cajyu project",
93
+ main: "index.cjy",
94
+ type: "module"
95
+ };
96
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
97
+ if (!fs.existsSync('CajyuLibs')) fs.mkdirSync('CajyuLibs');
98
+ console.log('Cajyu project initialized.');
99
+ }
100
+
101
+ async function installLib(libName) {
102
+ console.log(`Installing "${libName}" from CajyuLibs...`);
103
+
104
+ const targetDir = path.join(process.cwd(), 'CajyuLibs', libName);
105
+ if (fs.existsSync(targetDir)) {
106
+ console.log(`Library "${libName}" already installed.`);
107
+ return;
108
+ }
109
+
110
+ const tmpDir = path.join('/tmp', `cajyulibs-${Date.now()}`);
111
+ try {
112
+ console.log('Cloning CajyuLibs repository...');
113
+ execSync(`git clone --depth 1 ${CAJYULIBS_REPO} ${tmpDir}`, { stdio: 'inherit' });
114
+
115
+ const sourceLib = path.join(tmpDir, libName);
116
+ if (!fs.existsSync(sourceLib)) {
117
+ console.error(`Library "${libName}" not found in CajyuLibs.`);
118
+ process.exit(1);
119
+ }
120
+
121
+ fs.mkdirSync(path.dirname(targetDir), { recursive: true });
122
+ fs.cpSync(sourceLib, targetDir, { recursive: true });
123
+
124
+ const pkgPath = path.join(targetDir, 'package.json');
125
+ if (fs.existsSync(pkgPath)) {
126
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
127
+ if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
128
+ console.log('Installing library dependencies...');
129
+ execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
130
+ }
131
+ }
132
+
133
+ console.log(`✓ Library "${libName}" installed to CajyuLibs/${libName}/`);
134
+ } catch (err) {
135
+ console.error('Failed to install library:', err.message);
136
+ process.exit(1);
137
+ } finally {
138
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
139
+ }
140
+ }
141
+
142
+ function listInstalled() {
143
+ const libsDir = path.join(process.cwd(), 'CajyuLibs');
144
+ if (!fs.existsSync(libsDir)) {
145
+ console.log('No libraries installed yet.');
146
+ return;
147
+ }
148
+ const libs = fs.readdirSync(libsDir).filter(d => fs.statSync(path.join(libsDir, d)).isDirectory());
149
+ if (libs.length === 0) {
150
+ console.log('No libraries installed.');
151
+ } else {
152
+ console.log('Installed libraries:');
153
+ libs.forEach(l => console.log(` - ${l}`));
154
+ }
155
+ }
156
+
157
+ main().catch(console.error);
@@ -0,0 +1,51 @@
1
+ # Comparison: JavaScript vs TypeScript vs Cajyu
2
+
3
+ Three identical features implemented in three languages.
4
+
5
+ ## 1. Simple HTML Server
6
+
7
+ | Language | File | Lines (approx) | Notes |
8
+ |------------|-----------------------------------|----------------|-------|
9
+ | JavaScript | `js/simple-server.js` | 18 | Direct `http` module |
10
+ | TypeScript | `ts/simple-server.ts` | 18 | Same + types |
11
+ | Cajyu | `cajyu/simple-server.cjy` | 10 | Uses `CajyuLibs.http` |
12
+
13
+ **Cajyu is the shortest.** The HTTP library abstracts the boilerplate.
14
+
15
+ ## 2. Click Counter
16
+
17
+ | Language | File | Lines (approx) | Notes |
18
+ |------------|-----------------------------------|----------------|-------|
19
+ | JavaScript | `js/click-counter.js` | 35 | Manual route + inline HTML/JS |
20
+ | TypeScript | `ts/click-counter.ts` | 35 | Same + types |
21
+ | Cajyu | `cajyu/click-counter.cjy` | 16 | `server.post` + `server.file` |
22
+
23
+ Again, Cajyu is more concise because of the built-in server abstraction.
24
+
25
+ ## 3. Live Chat (WebSocket)
26
+
27
+ | Language | File | Lines (approx) | Notes |
28
+ |------------|-----------------------------------|----------------|-------|
29
+ | JavaScript | `js/live-chat.js` | 45 | Uses `ws` package + full HTML |
30
+ | TypeScript | `ts/live-chat.ts` | 45 | Same + types |
31
+ | Cajyu | `cajyu/live-chat.cjy` | 12 | Uses `CajyuLibs.websocket` (broadcast only) |
32
+
33
+ Cajyu version is shorter but currently only shows the WebSocket server side.
34
+
35
+ ## Summary
36
+
37
+ | Aspect | JavaScript | TypeScript | Cajyu |
38
+ |---------------------|-----------------|------------------|------------------------|
39
+ | Boilerplate | Medium | Medium | Low |
40
+ | Type safety | None | Strong | Optional runtime |
41
+ | Learning curve | Low | Medium | Low (if you know JS) |
42
+ | Ecosystem | Huge | Huge | Small (growing) |
43
+ | Abstraction level | Low-medium | Low-medium | Higher |
44
+ | Best for | Production | Large projects | Prototyping / learning |
45
+
46
+ **Conclusion:**
47
+ - **JavaScript** is the baseline.
48
+ - **TypeScript** adds safety with almost no extra cost.
49
+ - **Cajyu** reduces boilerplate the most, especially for servers, but is still a prototype language.
50
+
51
+ All examples are in `examples/comparison/`.
@@ -0,0 +1,17 @@
1
+ import Server from CajyuLibs.http
2
+ import print from CajyuLibs.serial
3
+
4
+ var clicks = 0
5
+ var server = new Server(3001)
6
+ print("Cajyu click server at http://localhost:3001")
7
+
8
+ var html = "<!DOCTYPE html><html><body><h1>Click Counter (Cajyu)</h1><button id='btn'>Click me</button><p>Total clicks: <span id='count'>0</span></p><script>const btn=document.getElementById('btn');const count=document.getElementById('count');btn.onclick=async()=>{const r=await fetch('/click',{method:'POST'});const data=await r.json();count.textContent=data.clicks;};</script></body></html>"
9
+
10
+ server.file("/", html)
11
+ server.file("index.html", html)
12
+
13
+ server.post(fn b {
14
+ clicks = clicks + 1
15
+ print("Click #" + clicks)
16
+ return { code: 200, clicks: clicks }
17
+ })
@@ -0,0 +1,16 @@
1
+ import WS from CajyuLibs.websocket
2
+ import print from CajyuLibs.serial
3
+
4
+ var wss = new WS.Server(3002)
5
+ print("Cajyu live chat WebSocket on port 3002")
6
+
7
+ wss.on("connection", fn client {
8
+ print("Client connected")
9
+ })
10
+
11
+ wss.on("message", fn msg, client {
12
+ print("Message: " + msg)
13
+ wss.broadcast(msg)
14
+ })
15
+
16
+ print("Open a WebSocket client to ws://localhost:3002 to chat")
@@ -0,0 +1,10 @@
1
+ import Server from CajyuLibs.http
2
+ import print from CajyuLibs.serial
3
+
4
+ var server = new Server(3000)
5
+ print("Cajyu simple server running at http://localhost:3000")
6
+
7
+ var html = "<!DOCTYPE html><html><head><title>Simple Server</title></head><body><h1>Hello from Cajyu!</h1><p>This is a simple text served by a Cajyu server.</p></body></html>"
8
+
9
+ server.file("/", html)
10
+ server.file("index.html", html)
@@ -0,0 +1,20 @@
1
+ // Simple HTML server - JavaScript
2
+ import http from 'http';
3
+
4
+ const server = http.createServer((req, res) => {
5
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
6
+ res.end(`
7
+ <!DOCTYPE html>
8
+ <html>
9
+ <head><title>Simple Server</title></head>
10
+ <body>
11
+ <h1>Hello from JavaScript!</h1>
12
+ <p>This is a simple text served by a Node.js server.</p>
13
+ </body>
14
+ </html>
15
+ `);
16
+ });
17
+
18
+ server.listen(3000, () => {
19
+ console.log('JS server running at http://localhost:3000');
20
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cajyu",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Cajyu programming language - a custom language that compiles to JavaScript",
5
5
  "main": "src/compiler.js",
6
6
  "bin": {
package/src/compiler.js CHANGED
@@ -2,23 +2,64 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { pathToFileURL } from 'url';
4
4
 
5
+ // Error codes
6
+ const ERRORS = {
7
+ ERR001: 'Syntax error',
8
+ ERR002: 'Unexpected token',
9
+ ERR003: 'Missing closing brace',
10
+ ERR004: 'Type mismatch',
11
+ ERR005: 'Undefined variable',
12
+ ERR006: 'Invalid function call',
13
+ ERR007: 'Missing parentheses',
14
+ ERR008: 'Invalid import',
15
+ ERR009: 'Unexpected end of file',
16
+ ERR010: 'Invalid operator',
17
+ ERR011: 'Missing semicolon or block',
18
+ ERR012: 'Invalid type annotation',
19
+ ERR013: 'Keyword used as identifier',
20
+ ERR014: 'Unterminated string',
21
+ ERR015: 'Invalid number',
22
+ ERR016: 'Duplicate declaration',
23
+ ERR017: 'Missing return value',
24
+ ERR018: 'Invalid class member',
25
+ ERR019: 'Async error',
26
+ ERR020: 'Library not found'
27
+ };
28
+
29
+ class CajyuError extends Error {
30
+ constructor(code, line, column, lineContent, message) {
31
+ const errName = ERRORS[code] || 'Unknown error';
32
+ const pointer = ' '.repeat(Math.max(0, column)) + '^';
33
+ super(`There is an ${errName.toLowerCase()} in line ${line}: ${code}.\n ${lineContent.trim()}\n ${pointer}`);
34
+ this.code = code;
35
+ this.line = line;
36
+ this.column = column;
37
+ }
38
+ }
39
+
5
40
  export async function compileAndRun(filePath) {
6
41
  if (!fs.existsSync(filePath)) {
7
- console.error(`Arquivo não encontrado: ${filePath}`);
42
+ console.error(`File not found: ${filePath}`);
8
43
  process.exit(1);
9
44
  }
10
45
  const source = fs.readFileSync(filePath, 'utf-8');
11
- const jsCode = transpile(source, path.dirname(filePath));
12
- const tempFile = path.join('/tmp', `cajyu_${Date.now()}.mjs`);
13
- fs.writeFileSync(tempFile, jsCode);
14
46
  try {
15
- await import(pathToFileURL(tempFile).href);
16
- console.log('--- Execução Cajyu ---');
47
+ const jsCode = transpile(source, path.dirname(filePath));
48
+ const tempFile = path.join('/tmp', `cajyu_${Date.now()}.mjs`);
49
+ fs.writeFileSync(tempFile, jsCode);
50
+ try {
51
+ await import(pathToFileURL(tempFile).href);
52
+ console.log('--- Cajyu execution ---');
53
+ } finally {
54
+ try { fs.unlinkSync(tempFile); } catch {}
55
+ }
17
56
  } catch (err) {
18
- console.error('Erro na execução:', err.message);
19
- console.error(err.stack);
20
- } finally {
21
- try { fs.unlinkSync(tempFile); } catch {}
57
+ if (err instanceof CajyuError) {
58
+ console.error(err.message);
59
+ } else {
60
+ console.error('Runtime error:', err.message);
61
+ }
62
+ process.exit(1);
22
63
  }
23
64
  }
24
65
 
@@ -26,13 +67,21 @@ function transpile(source, baseDir) {
26
67
  const lines = source.split('\n');
27
68
  const imports = [];
28
69
  const codeLines = [];
29
- for (const line of lines) {
70
+
71
+ for (let i = 0; i < lines.length; i++) {
72
+ const line = lines[i];
30
73
  const m = line.match(/^\s*import\s+(\*|\w+)\s+from\s+['"]?([^\s'"]+)['"]?\s*;?\s*$/);
31
- if (m) imports.push({ what: m[1], from: m[2] });
32
- else codeLines.push(line);
74
+ if (m) {
75
+ imports.push({ what: m[1], from: m[2], line: i + 1 });
76
+ } else {
77
+ codeLines.push(line);
78
+ }
33
79
  }
80
+
34
81
  let code = codeLines.join('\n');
35
82
 
83
+ code = preprocessWithErrors(code, lines);
84
+
36
85
  let importCode = '';
37
86
  for (const imp of imports) {
38
87
  const libPath = resolveLib(imp.from, baseDir);
@@ -43,10 +92,11 @@ function transpile(source, baseDir) {
43
92
  } else {
44
93
  importCode += `const { CayjuLib: _lib${imp.what} } = await import('${url}'); const ${imp.what} = _lib${imp.what}.${imp.what} || _lib${imp.what};\n`;
45
94
  }
95
+ } else {
96
+ throw new CajyuError('ERR020', imp.line, 0, lines[imp.line - 1] || '', `Library not found: ${imp.from}`);
46
97
  }
47
98
  }
48
99
 
49
- code = preprocessKeywords(code);
50
100
  code = transpileFunctions(code);
51
101
  code = transpileAnonymousFunctions(code);
52
102
  code = transpileControlFlow(code);
@@ -63,24 +113,95 @@ function _range(a, b, c) {
63
113
  function _typeCheck(val, type) {
64
114
  if (!type) return val;
65
115
  const t = String(type).toLowerCase();
66
- if (t === 'string' && typeof val !== 'string') throw new Error('esperado String');
67
- if ((t === 'number' || t === 'int' || t === 'float') && typeof val !== 'number') throw new Error('esperado Number');
68
- if (t === 'boolean' && typeof val !== 'boolean') throw new Error('esperado Boolean');
69
- if (t === 'array' && !Array.isArray(val)) throw new Error('esperado Array');
116
+ if (t === 'string' && typeof val !== 'string') throw new Error('ERR004: expected String');
117
+ if ((t === 'number' || t === 'int' || t === 'float') && typeof val !== 'number') throw new Error('ERR004: expected Number');
118
+ if (t === 'boolean' && typeof val !== 'boolean') throw new Error('ERR004: expected Boolean');
119
+ if (t === 'array' && !Array.isArray(val)) throw new Error('ERR004: expected Array');
120
+ if (t === 'object' && (typeof val !== 'object' || val === null)) throw new Error('ERR004: expected Object');
70
121
  return val;
71
122
  }
72
123
  `;
73
124
 
74
- const final = `
125
+ return `
75
126
  ${importCode}
76
127
  ${builtins}
77
128
  (async () => {
78
129
  ${code}
79
- })().catch(console.error);
130
+ })().catch(e => { console.error(e.message); process.exit(1); });
80
131
  `;
132
+ }
133
+
134
+ function preprocessWithErrors(code, originalLines) {
135
+ const result = [];
136
+ const lines = code.split('\n');
137
+
138
+ for (let i = 0; i < lines.length; i++) {
139
+ let line = lines[i];
140
+ let newLine = '';
141
+ let inString = false;
142
+ let stringChar = '';
81
143
 
82
- fs.writeFileSync('/tmp/cajyu_debug.mjs', final);
83
- return final;
144
+ for (let j = 0; j < line.length; j++) {
145
+ const ch = line[j];
146
+
147
+ if (!inString && (ch === '"' || ch === "'")) {
148
+ inString = true;
149
+ stringChar = ch;
150
+ newLine += ch;
151
+ continue;
152
+ }
153
+ if (inString && ch === stringChar && line[j - 1] !== '\\') {
154
+ inString = false;
155
+ newLine += ch;
156
+ continue;
157
+ }
158
+ if (inString) {
159
+ newLine += ch;
160
+ continue;
161
+ }
162
+
163
+ if (line.substr(j, 3) === 'and' && isWordBoundary(line, j, 3)) {
164
+ newLine += '&&';
165
+ j += 2;
166
+ continue;
167
+ }
168
+ if (line.substr(j, 2) === 'or' && isWordBoundary(line, j, 2)) {
169
+ newLine += '||';
170
+ j += 1;
171
+ continue;
172
+ }
173
+ if (line.substr(j, 3) === 'not' && isWordBoundary(line, j, 3)) {
174
+ newLine += '!';
175
+ j += 2;
176
+ continue;
177
+ }
178
+
179
+ newLine += ch;
180
+ }
181
+
182
+ if (inString) {
183
+ throw new CajyuError('ERR014', i + 1, line.length, line, 'Unterminated string');
184
+ }
185
+
186
+ newLine = newLine
187
+ .replace(/sync\s+fn\s+(\w+)/g, 'async function $1')
188
+ .replace(/\bwait\s+/g, 'await ')
189
+ .replace(/\belif\b/g, 'else if')
190
+ .replace(/\bSelf\./g, 'this.')
191
+ .replace(/\brange\(/g, '_range(')
192
+ .replace(/var\s+(\w+):(\w+)\s*=\s*(.+)/g, 'let $1 = _typeCheck($3, "$2")')
193
+ .replace(/var\s+(\w+)\s*=/g, 'let $1 =');
194
+
195
+ result.push(newLine);
196
+ }
197
+
198
+ return result.join('\n');
199
+ }
200
+
201
+ function isWordBoundary(line, index, len) {
202
+ const before = index === 0 || !/\w/.test(line[index - 1]);
203
+ const after = index + len >= line.length || !/\w/.test(line[index + len]);
204
+ return before && after;
84
205
  }
85
206
 
86
207
  function resolveLib(from, baseDir) {
@@ -100,21 +221,6 @@ function cleanParams(s) {
100
221
  return (s || '').trim().split(',').map(p => p.trim().split(':')[0].trim()).filter(Boolean).join(', ');
101
222
  }
102
223
 
103
- function preprocessKeywords(code) {
104
- return code
105
- .replace(/\band\b/g, '&&')
106
- .replace(/\bor\b/g, '||')
107
- .replace(/\bnot\b/g, '!')
108
- .replace(/sync\s+fn\s+(\w+)/g, 'async function $1')
109
- .replace(/\bwait\s+/g, 'await ')
110
- .replace(/\belif\b/g, 'else if')
111
- .replace(/\bSelf\./g, 'this.')
112
- .replace(/\brange\(/g, '_range(')
113
- .replace(/\/\/\s*private\s+(\w+)/g, '/* private $1 */')
114
- .replace(/var\s+(\w+):(\w+)\s*=\s*(.+)/g, 'let $1 = _typeCheck($3, "$2")')
115
- .replace(/var\s+(\w+)\s*=/g, 'let $1 =');
116
- }
117
-
118
224
  function transpileFunctions(code) {
119
225
  return code
120
226
  .replace(/^fn\s+(\w+)\s+([^{]*)\{/gm, (_, n, p) => `function ${n}(${cleanParams(p)}) {`)
@@ -145,5 +251,7 @@ function transpileControlFlow(code) {
145
251
  function transpileOneLiners(code) {
146
252
  return code
147
253
  .replace(/return\s+(\{[^}]*\}|.+?)\s+if\s+([^;]+);/g, 'if ($2) return $1;')
148
- .replace(/if\s+([^:{;\n]+)\s+return\s+([^;\n{]+)/g, 'if ($1) return $2;');
254
+ .replace(/if\s+([^:{;\n]+)\s+return\s+([^;\n{]+)/g, 'if ($1) return $2;')
255
+ .replace(/while\s+([^:{;\n]+)\s+([^;\n{]+);?/g, 'while ($1) { $2; }')
256
+ .replace(/for\s+(\w+)\s+in\s+([^ {\n;]+)\s+([^;\n{]+);?/g, 'for (let $1 of $2) { $3; }');
149
257
  }