cajyu 1.0.1 → 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
@@ -15,7 +15,8 @@ Cajyu gives you a simpler, more expressive syntax while staying fully compatible
15
15
  - Clean syntax for variables, functions, classes, loops and conditionals
16
16
  - Transpiles to modern JavaScript
17
17
  - Real HTTP server with POST handlers, sleep/wake
18
- - Official libraries: `serial`, `fs`, `math`, `http`, `websocket`
18
+ - WebSocket that automatically sends and receives JSON
19
+ - Official libraries: `serial`, `fs`, `math`, `http`, `websocket`, `string`, `json`, `exec`, `fetch`
19
20
  - Optional runtime typing
20
21
  - Async with `sync fn` and `wait`
21
22
  - One-liners and stacking
@@ -121,16 +122,24 @@ server.post(fn b {
121
122
  })
122
123
  ```
123
124
 
124
- ### WebSocket
125
+ ### WebSocket (JSON)
125
126
  ```cajyu
126
127
  import WS from CajyuLibs.websocket
127
128
 
128
129
  var wss = new WS.Server(8080)
129
- wss.on("connection", fn client {
130
- print("client connected")
131
- })
132
130
  wss.on("message", fn msg, client {
133
- wss.broadcast(msg)
131
+ wss.broadcast({ echo: msg })
132
+ })
133
+ ```
134
+
135
+ ### Exec & Fetch
136
+ ```cajyu
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)
134
143
  })
135
144
  ```
136
145
 
@@ -146,6 +155,10 @@ cajyu inst fs
146
155
  cajyu inst math
147
156
  cajyu inst http
148
157
  cajyu inst websocket
158
+ cajyu inst string
159
+ cajyu inst json
160
+ cajyu inst exec
161
+ cajyu inst fetch
149
162
  ```
150
163
 
151
164
  ---
@@ -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.01",
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
  }