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 +70 -88
- package/bin/cajyu.js +157 -0
- package/examples/comparison/README.md +51 -0
- package/examples/comparison/cajyu/click-counter.cjy +17 -0
- package/examples/comparison/cajyu/live-chat.cjy +16 -0
- package/examples/comparison/cajyu/simple-server.cjy +10 -0
- package/examples/comparison/js/simple-server.js +20 -0
- package/package.json +1 -1
- package/src/compiler.js +146 -38
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
|
|
5
|
+
Cajyu gives you a simpler, more expressive syntax while staying fully compatible with the JavaScript ecosystem.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/cajyu)
|
|
8
8
|
[](LICENSE)
|
|
9
|
+
[](package.json)
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
## Features
|
|
13
14
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
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
|
-
#
|
|
30
|
+
# After publishing
|
|
29
31
|
npm install -g cajyu
|
|
30
32
|
|
|
31
|
-
# Or
|
|
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
|
-
|
|
44
|
+
---
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
|
|
135
|
+
### Exec & Fetch
|
|
148
136
|
```cajyu
|
|
149
|
-
import
|
|
150
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
150
|
+
Official libraries: https://github.com/abs-js/CajyuLibs
|
|
166
151
|
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
168
|
+
Pull requests are welcome. Priority areas:
|
|
183
169
|
|
|
184
|
-
- A proper parser (
|
|
170
|
+
- A proper parser (current one is regex-based)
|
|
185
171
|
- More language features
|
|
186
|
-
-
|
|
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
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(`
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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)
|
|
32
|
-
|
|
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('
|
|
67
|
-
if ((t === 'number' || t === 'int' || t === 'float') && typeof val !== 'number') throw new Error('
|
|
68
|
-
if (t === 'boolean' && typeof val !== 'boolean') throw new Error('
|
|
69
|
-
if (t === 'array' && !Array.isArray(val)) throw new Error('
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
}
|