cajyu 0.0.0-stage → 1.0.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 +196 -2
- package/examples/classe.cjy +16 -0
- package/examples/fatorial.cjy +13 -0
- package/examples/servidor-post.cjy +18 -0
- package/package.json +15 -4
- package/src/compiler.js +149 -0
package/README.md
CHANGED
|
@@ -1,3 +1,197 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Cajyu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A clean, practical programming language that transpiles to JavaScript.**
|
|
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.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/cajyu)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
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`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Install the CLI (once published)
|
|
29
|
+
npm install -g cajyu
|
|
30
|
+
|
|
31
|
+
# Or run directly from this repo
|
|
32
|
+
node bin/cajyu.js --help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Create a new project:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cajyu init
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run a file:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cajyu run examples/fatorial.cjy
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Syntax Overview
|
|
50
|
+
|
|
51
|
+
### Variables
|
|
52
|
+
```cajyu
|
|
53
|
+
var name = "Cajyu"
|
|
54
|
+
var age:Number = 17
|
|
55
|
+
var items = [1, 2, 3]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Functions
|
|
59
|
+
```cajyu
|
|
60
|
+
fn add a, b {
|
|
61
|
+
return a + b
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn double x {
|
|
65
|
+
return x * 2
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Control Flow
|
|
70
|
+
```cajyu
|
|
71
|
+
if x > 10 {
|
|
72
|
+
print("big")
|
|
73
|
+
} elif x > 5 {
|
|
74
|
+
print("medium")
|
|
75
|
+
} else {
|
|
76
|
+
print("small")
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Loops
|
|
81
|
+
```cajyu
|
|
82
|
+
for i in range(10) {
|
|
83
|
+
print(i)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
while x < 100 {
|
|
87
|
+
x = x * 2
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Classes
|
|
92
|
+
```cajyu
|
|
93
|
+
class Person {
|
|
94
|
+
Main name, age {
|
|
95
|
+
Self.name = name
|
|
96
|
+
Self.age = age
|
|
97
|
+
}
|
|
98
|
+
greet {
|
|
99
|
+
return "Hello, I'm " + Self.name
|
|
100
|
+
}
|
|
101
|
+
}
|
|
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
|
+
```
|
|
114
|
+
|
|
115
|
+
### HTTP Server
|
|
116
|
+
```cajyu
|
|
117
|
+
import Server from CajyuLibs.http
|
|
118
|
+
import print from CajyuLibs.serial
|
|
119
|
+
|
|
120
|
+
var server = new Server(3000)
|
|
121
|
+
|
|
122
|
+
server.post(fn b {
|
|
123
|
+
if !b.a and !b.b {
|
|
124
|
+
return { code: 400 }
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
code: 200,
|
|
128
|
+
result: b.a + b.b
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Libraries
|
|
136
|
+
|
|
137
|
+
Official libraries live in the [CajyuLibs](https://github.com/abs-js/CajyuLibs) repository.
|
|
138
|
+
|
|
139
|
+
Install a library:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cajyu inst serial
|
|
143
|
+
cajyu inst http
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Import in your code:
|
|
147
|
+
|
|
148
|
+
```cajyu
|
|
149
|
+
import print from CajyuLibs.serial
|
|
150
|
+
import Server from CajyuLibs.http
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
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
|
+
---
|
|
164
|
+
|
|
165
|
+
## Project Structure
|
|
166
|
+
|
|
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
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
Cajyu is early but intentional. Pull requests are welcome — especially for:
|
|
183
|
+
|
|
184
|
+
- A proper parser (currently regex-based)
|
|
185
|
+
- More language features
|
|
186
|
+
- New official libraries
|
|
187
|
+
- Better error messages
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
**Built for people who like clean code and real results.**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import print from CajyuLibs.serial
|
|
2
|
+
|
|
3
|
+
class Pessoa {
|
|
4
|
+
Main nome, idade {
|
|
5
|
+
Self.nome = nome
|
|
6
|
+
Self.idade = idade
|
|
7
|
+
}
|
|
8
|
+
// private nome
|
|
9
|
+
// private idade
|
|
10
|
+
apresentar {
|
|
11
|
+
return "Oi, eu sou " + Self.nome + " e tenho " + Self.idade + " anos"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var p = new Pessoa("Ana", 17)
|
|
16
|
+
print(p.apresentar())
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Server from CajyuLibs.http
|
|
2
|
+
import print from CajyuLibs.serial
|
|
3
|
+
|
|
4
|
+
var server = new Server(3000)
|
|
5
|
+
print("Servidor Cajyu pronto na porta 3000")
|
|
6
|
+
|
|
7
|
+
server.post(fn b {
|
|
8
|
+
if !b.a and !b.b {
|
|
9
|
+
return { code: 400 }
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
code: 200,
|
|
13
|
+
result: b.a + b.b
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
print("POST / com JSON {a, b} para somar")
|
|
18
|
+
print("server.sleep() e server.wake() disponíveis")
|
package/package.json
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cajyu",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Cajyu programming language - a custom language that compiles to JavaScript",
|
|
5
|
+
"main": "src/compiler.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cajyu": "./bin/cajyu.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/cajyu.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["cajyu", "compiler", "language", "javascript"],
|
|
13
|
+
"author": "Cajyu Team",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {},
|
|
16
|
+
"type": "module"
|
|
17
|
+
}
|
package/src/compiler.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
|
|
5
|
+
export async function compileAndRun(filePath) {
|
|
6
|
+
if (!fs.existsSync(filePath)) {
|
|
7
|
+
console.error(`Arquivo não encontrado: ${filePath}`);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
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
|
+
try {
|
|
15
|
+
await import(pathToFileURL(tempFile).href);
|
|
16
|
+
console.log('--- Execução Cajyu ---');
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error('Erro na execução:', err.message);
|
|
19
|
+
console.error(err.stack);
|
|
20
|
+
} finally {
|
|
21
|
+
try { fs.unlinkSync(tempFile); } catch {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function transpile(source, baseDir) {
|
|
26
|
+
const lines = source.split('\n');
|
|
27
|
+
const imports = [];
|
|
28
|
+
const codeLines = [];
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
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);
|
|
33
|
+
}
|
|
34
|
+
let code = codeLines.join('\n');
|
|
35
|
+
|
|
36
|
+
let importCode = '';
|
|
37
|
+
for (const imp of imports) {
|
|
38
|
+
const libPath = resolveLib(imp.from, baseDir);
|
|
39
|
+
if (libPath && fs.existsSync(libPath)) {
|
|
40
|
+
const url = pathToFileURL(libPath).href;
|
|
41
|
+
if (imp.what === '*') {
|
|
42
|
+
importCode += `const _lib = await import('${url}'); Object.assign(globalThis, _lib.CayjuLib || _lib);\n`;
|
|
43
|
+
} else {
|
|
44
|
+
importCode += `const { CayjuLib: _lib${imp.what} } = await import('${url}'); const ${imp.what} = _lib${imp.what}.${imp.what} || _lib${imp.what};\n`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
code = preprocessKeywords(code);
|
|
50
|
+
code = transpileFunctions(code);
|
|
51
|
+
code = transpileAnonymousFunctions(code);
|
|
52
|
+
code = transpileControlFlow(code);
|
|
53
|
+
code = transpileOneLiners(code);
|
|
54
|
+
|
|
55
|
+
const builtins = `
|
|
56
|
+
function _range(a, b, c) {
|
|
57
|
+
const r = [];
|
|
58
|
+
if (b == null) for (let i = 1; i <= a; i++) r.push(i);
|
|
59
|
+
else if (c == null) for (let i = a; i <= b; i++) r.push(i);
|
|
60
|
+
else for (let i = a; i <= b; i += c) r.push(i);
|
|
61
|
+
return r;
|
|
62
|
+
}
|
|
63
|
+
function _typeCheck(val, type) {
|
|
64
|
+
if (!type) return val;
|
|
65
|
+
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');
|
|
70
|
+
return val;
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const final = `
|
|
75
|
+
${importCode}
|
|
76
|
+
${builtins}
|
|
77
|
+
(async () => {
|
|
78
|
+
${code}
|
|
79
|
+
})().catch(console.error);
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
fs.writeFileSync('/tmp/cajyu_debug.mjs', final);
|
|
83
|
+
return final;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolveLib(from, baseDir) {
|
|
87
|
+
if (from.startsWith('CajyuLibs')) {
|
|
88
|
+
const name = from.split('.')[1] || 'serial';
|
|
89
|
+
const cands = [
|
|
90
|
+
path.resolve(baseDir, 'CajyuLibs', name, 'main.js'),
|
|
91
|
+
path.resolve(baseDir, '..', 'CajyuLibs', name, 'main.js'),
|
|
92
|
+
path.resolve(process.cwd(), 'CajyuLibs', name, 'main.js'),
|
|
93
|
+
];
|
|
94
|
+
return cands.find(p => fs.existsSync(p));
|
|
95
|
+
}
|
|
96
|
+
return path.resolve(baseDir, from);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function cleanParams(s) {
|
|
100
|
+
return (s || '').trim().split(',').map(p => p.trim().split(':')[0].trim()).filter(Boolean).join(', ');
|
|
101
|
+
}
|
|
102
|
+
|
|
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
|
+
function transpileFunctions(code) {
|
|
119
|
+
return code
|
|
120
|
+
.replace(/^fn\s+(\w+)\s+([^{]*)\{/gm, (_, n, p) => `function ${n}(${cleanParams(p)}) {`)
|
|
121
|
+
.replace(/^fn\s+(\w+)\s*\{/gm, 'function $1() {')
|
|
122
|
+
.replace(/Main\s+([^{]*)\{/g, (_, p) => `constructor(${cleanParams(p)}) {`)
|
|
123
|
+
.replace(/^\s*(\w+)\s*\{/gm, (m, name) => {
|
|
124
|
+
const keywords = ['if','else','while','for','return','class','fn','function','constructor'];
|
|
125
|
+
if (keywords.includes(name)) return m;
|
|
126
|
+
return ` ${name}() {`;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function transpileAnonymousFunctions(code) {
|
|
131
|
+
return code.replace(/(?<!^|\n)fn\s+([^{]*)\{/g, (_, params) => {
|
|
132
|
+
const p = cleanParams(params);
|
|
133
|
+
return `function(${p}) {`;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function transpileControlFlow(code) {
|
|
138
|
+
return code
|
|
139
|
+
.replace(/if\s+([^{]+?)\s*\{/g, 'if ($1) {')
|
|
140
|
+
.replace(/else\s+if\s+([^{]+?)\s*\{/g, 'else if ($1) {')
|
|
141
|
+
.replace(/while\s+([^{]+?)\s*\{/g, 'while ($1) {')
|
|
142
|
+
.replace(/for\s+(\w+)\s+in\s+([^ {\n]+)(\s*\{)/g, 'for (let $1 of $2) $3');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function transpileOneLiners(code) {
|
|
146
|
+
return code
|
|
147
|
+
.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;');
|
|
149
|
+
}
|