cajyu 1.0.1 → 1.2.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
@@ -1,5 +1,9 @@
1
1
  # Cajyu
2
2
 
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/abs-js/Cajyu/main/logos/logo-128.png" alt="Cajyu Logo" width="128">
5
+ </p>
6
+
3
7
  **A clean, practical programming language that transpiles to JavaScript.**
4
8
 
5
9
  Cajyu gives you a simpler, more expressive syntax while staying fully compatible with the JavaScript ecosystem.
@@ -15,7 +19,8 @@ Cajyu gives you a simpler, more expressive syntax while staying fully compatible
15
19
  - Clean syntax for variables, functions, classes, loops and conditionals
16
20
  - Transpiles to modern JavaScript
17
21
  - Real HTTP server with POST handlers, sleep/wake
18
- - Official libraries: `serial`, `fs`, `math`, `http`, `websocket`
22
+ - WebSocket that automatically sends and receives JSON
23
+ - Official libraries: `serial`, `fs`, `math`, `http`, `websocket`, `string`, `json`, `exec`, `fetch`
19
24
  - Optional runtime typing
20
25
  - Async with `sync fn` and `wait`
21
26
  - One-liners and stacking
@@ -121,16 +126,24 @@ server.post(fn b {
121
126
  })
122
127
  ```
123
128
 
124
- ### WebSocket
129
+ ### WebSocket (JSON)
125
130
  ```cajyu
126
131
  import WS from CajyuLibs.websocket
127
132
 
128
133
  var wss = new WS.Server(8080)
129
- wss.on("connection", fn client {
130
- print("client connected")
131
- })
132
134
  wss.on("message", fn msg, client {
133
- wss.broadcast(msg)
135
+ wss.broadcast({ echo: msg })
136
+ })
137
+ ```
138
+
139
+ ### Exec & Fetch
140
+ ```cajyu
141
+ import exec from CajyuLibs.exec
142
+ var files = exec.exec("ls -a")
143
+
144
+ import fetch from CajyuLibs.fetch
145
+ fetch.fetch("https://api.github.com", {}, null, fn result, ms {
146
+ print(result)
134
147
  })
135
148
  ```
136
149
 
@@ -146,6 +159,10 @@ cajyu inst fs
146
159
  cajyu inst math
147
160
  cajyu inst http
148
161
  cajyu inst websocket
162
+ cajyu inst string
163
+ cajyu inst json
164
+ cajyu inst exec
165
+ cajyu inst fetch
149
166
  ```
150
167
 
151
168
  ---
package/bin/cajyu.js CHANGED
@@ -9,7 +9,7 @@ const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
 
11
11
  const CAJYULIBS_REPO = 'https://github.com/abs-js/CajyuLibs.git';
12
- const VERSION = '1.1.0';
12
+ const VERSION = '1.2.0';
13
13
 
14
14
  const args = process.argv.slice(2);
15
15
  const command = args[0];
@@ -0,0 +1,8 @@
1
+ # Cajyu Logos
2
+
3
+ Square logo versions:
4
+ - logo-64.png (64x64)
5
+ - logo-128.png (128x128)
6
+ - logo-256.png (256x256)
7
+
8
+ Yellow ink splash with brown Y-shaped stem on black background.
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cajyu",
3
- "version": "1.0.01",
3
+ "version": "1.2.0",
4
4
  "description": "Cajyu programming language - a custom language that compiles to JavaScript",
5
5
  "main": "src/compiler.js",
6
6
  "bin": {
@@ -0,0 +1,16 @@
1
+ # AST Migration in progress
2
+
3
+ The compiler now has a real Tokenizer + recursive descent Parser that builds an AST.
4
+
5
+ Current status:
6
+ - Tokenizer handles strings, numbers, identifiers, keywords correctly
7
+ - Parser builds AST for var, fn, if/elif/else, while, for, class, return, expressions
8
+ - Code generator emits JavaScript from the AST
9
+
10
+ Still being completed:
11
+ - Full import resolution
12
+ - Builtins (range, typeCheck)
13
+ - Async (sync/wait)
14
+ - One-liners and more edge cases
15
+
16
+ This is the foundation for a robust parser.
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
  }