nebulara 2.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.
Files changed (48) hide show
  1. package/Compiler/nbs-bootstrap.c +2229 -0
  2. package/Compiler/nbs_cli.c +1553 -0
  3. package/Compiler/neb-cli.exe +0 -0
  4. package/Compiler/neb-codegen.c +287 -0
  5. package/Compiler/neb-ffi.c +303 -0
  6. package/Compiler/neb-ir.c +307 -0
  7. package/Compiler/neb-knowledge.c +203 -0
  8. package/Compiler/neb-pipeline.c +787 -0
  9. package/Compiler/neb-semantic.c +261 -0
  10. package/Compiler/nebulara.exe +0 -0
  11. package/README.md +258 -0
  12. package/SPEC.md +267 -0
  13. package/bin/neb.js +36 -0
  14. package/build/neb-cli.exe +0 -0
  15. package/build/neb-codegen.exe +0 -0
  16. package/build/neb-ffi.exe +0 -0
  17. package/build/neb-knowledge.exe +0 -0
  18. package/build/neb-pipeline.exe +0 -0
  19. package/build/nebulara.exe +0 -0
  20. package/dist/index.js +131 -0
  21. package/package.json +39 -0
  22. package/std/collections.nbs +72 -0
  23. package/std/json.nbs +15 -0
  24. package/std/kanban.nbs +122 -0
  25. package/std/math.nbs +52 -0
  26. package/std/net.nbs +22 -0
  27. package/std/primitives.nbs +30 -0
  28. package/std/string.nbs +71 -0
  29. package/std/time.nbs +15 -0
  30. package/test/broken.nbs +2 -0
  31. package/test/hello.nbs +6 -0
  32. package/test/test-arrays.nbs +22 -0
  33. package/test/test-charat.nbs +7 -0
  34. package/test/test-cont-debug.nbs +8 -0
  35. package/test/test-cont-min.nbs +7 -0
  36. package/test/test-cont-parens.nbs +7 -0
  37. package/test/test-cont2.nbs +7 -0
  38. package/test/test-cont3.nbs +7 -0
  39. package/test/test-control.nbs +18 -0
  40. package/test/test-functions.nbs +13 -0
  41. package/test/test-loops.nbs +17 -0
  42. package/test/test-mod-for.nbs +6 -0
  43. package/test/test-mod.nbs +5 -0
  44. package/test/test-new-features.nbs +39 -0
  45. package/test/test-recursion.nbs +23 -0
  46. package/test/test-runtime.nbs +8 -0
  47. package/test/test-strings.nbs +14 -0
  48. package/test/test-trycatch.nbs +11 -0
package/SPEC.md ADDED
@@ -0,0 +1,267 @@
1
+ # Nebulara Language Specification v3.0
2
+
3
+ ## File Extension
4
+ `.nbs` - Nebulara Source
5
+
6
+ ## Comments
7
+ ```
8
+ # This is a line comment
9
+ ```
10
+
11
+ ## Keywords
12
+
13
+ | Keyword | Usage | Example |
14
+ |---------|-------|---------|
15
+ | `FUNC!` | Define function | `FUNC! add(a, b):` |
16
+ | `END!` | Block end | `END!` |
17
+ | `LET` | Variable declaration | `LET x = 10` |
18
+ | `CONST` | Constant declaration | `CONST PI = 314` |
19
+ | `IF?` | Conditional | `IF? x > 10:` |
20
+ | `ELSE` | Else branch | `ELSE:` |
21
+ | `ELSEIF?` | Else-if branch | `ELSEIF? x > 5:` |
22
+ | `WHILE?` | While loop | `WHILE? i < 10:` |
23
+ | `FOR!` | For loop | `FOR! i = 1 TO 10:` |
24
+ | `TO` | FOR loop range | `FOR! i = 1 TO 10:` |
25
+ | `STEP` | FOR loop step | `FOR! i = 0 TO 20 STEP 5:` |
26
+ | `RETURN` | Return value | `RETURN a + b` |
27
+ | `BREAK` | Exit loop | `BREAK` |
28
+ | `CONTINUE` | Skip iteration | `CONTINUE` |
29
+ | `TRY` | Try block | `TRY:` |
30
+ | `CATCH` | Catch block | `CATCH err:` |
31
+ | `THROW` | Throw exception | `THROW "error"` |
32
+ | `AND` | Logical AND | `IF? a AND b:` |
33
+ | `OR` | Logical OR | `IF? a OR b:` |
34
+ | `NOT` | Logical NOT | `IF? NOT done:` |
35
+ | `TRUE` | Boolean true | `LET x = TRUE` |
36
+ | `FALSE` | Boolean false | `LET x = FALSE` |
37
+ | `NULL` | Null value | `IF? x == NULL:` |
38
+
39
+ ## Types
40
+
41
+ | Type | Description | Example |
42
+ |------|-------------|---------|
43
+ | Int | 64-bit signed integer | `42`, `-7` |
44
+ | String | UTF-8 text | `"hello"` |
45
+ | Bool | Boolean | `TRUE`, `FALSE` |
46
+ | Array | Dynamic list | `[1, 2, 3]` |
47
+ | Null | Absence of value | `NULL` |
48
+ | Func | Function reference | `FUNC! name():` |
49
+
50
+ ## Syntax
51
+
52
+ ### Variable Declaration
53
+ ```
54
+ LET x = 10
55
+ LET name = "Nebulara"
56
+ LET arr = [1, 2, 3]
57
+ x = 20 # reassignment (no LET)
58
+ ```
59
+
60
+ ### Function Definition
61
+ ```
62
+ FUNC! add(a, b):
63
+ RETURN a + b
64
+ END!
65
+
66
+ FUNC! greet(name):
67
+ PRINT "Hello " + name
68
+ END!
69
+
70
+ FUNC! noArgs():
71
+ PRINT "no parameters"
72
+ END!
73
+ ```
74
+
75
+ ### Function Call
76
+ ```
77
+ PRINT add(3, 4) # prints 7
78
+ greet("World") # prints "Hello World"
79
+ ```
80
+
81
+ ### Control Flow
82
+
83
+ #### If/Else
84
+ ```
85
+ IF? x > 10:
86
+ PRINT "big"
87
+ ELSEIF? x > 5:
88
+ PRINT "medium"
89
+ ELSE:
90
+ PRINT "small"
91
+ END!
92
+ ```
93
+
94
+ #### While Loop
95
+ ```
96
+ LET i = 1
97
+ WHILE? i <= 5:
98
+ PRINT i
99
+ i = i + 1
100
+ END!
101
+ ```
102
+
103
+ #### For Loop
104
+ ```
105
+ FOR! i = 1 TO 5:
106
+ PRINT i
107
+ END!
108
+
109
+ # With STEP
110
+ FOR! i = 0 TO 20 STEP 5:
111
+ PRINT i
112
+ END!
113
+ ```
114
+
115
+ #### Try/Catch/Throw
116
+ ```
117
+ TRY:
118
+ LET x = TO_NUMBER("not a number")
119
+ CATCH err:
120
+ PRINT "Error: " + err
121
+ END!
122
+ ```
123
+
124
+ `THROW` raises an exception with a value (typically a string). If no `TRY` block is active, the program terminates with an uncaught exception error.
125
+
126
+ ### Operators
127
+
128
+ #### Arithmetic
129
+ ```
130
+ + # addition / string concatenation
131
+ - # subtraction
132
+ * # multiplication
133
+ / # division
134
+ % # modulo
135
+ - # unary negation
136
+ ```
137
+
138
+ #### Bitwise
139
+ ```
140
+ & # bitwise AND: 5 & 3 → 1
141
+ | # bitwise OR: 5 | 3 → 7
142
+ << # left shift: 1 << 3 → 8
143
+ >> # right shift: 8 >> 2 → 2
144
+ ```
145
+
146
+ #### Comparison
147
+ ```
148
+ == # equal
149
+ != # not equal
150
+ < # less than
151
+ > # greater than
152
+ <= # less or equal
153
+ >= # greater or equal
154
+ ```
155
+
156
+ #### Logical
157
+ ```
158
+ AND # logical and
159
+ OR # logical or
160
+ NOT # logical not
161
+ ```
162
+
163
+ #### String
164
+ ```
165
+ + # concatenation: "hello" + " " + "world" → "hello world"
166
+ ```
167
+
168
+ ### Arrays
169
+ ```
170
+ LET arr = [10, 20, 30]
171
+ PRINT arr[0] # 10
172
+ arr[1] = 99 # mutation: arr is now [10, 99, 30]
173
+ PRINT LEN(arr) # 3
174
+ ```
175
+
176
+ Arrays support index-based assignment (`arr[i] = value`) to mutate elements in place.
177
+
178
+ ### Built-in Functions
179
+
180
+ #### Output & Input
181
+
182
+ | Function | Signature | Description |
183
+ |----------|-----------|-------------|
184
+ | `PRINT` | `PRINT(value)` | Print value to stdout with newline. Accepts any type. |
185
+
186
+ #### String Functions
187
+
188
+ | Function | Signature | Description |
189
+ |----------|-----------|-------------|
190
+ | `LEN` | `LEN(value)` | Returns the length of a string (byte count) or array (element count). Returns `0` for other types. |
191
+ | `TYPEOF` | `TYPEOF(value)` | Returns the type name as a string: `"int"`, `"string"`, `"bool"`, `"array"`, `"null"`, `"func"` |
192
+ | `TO_STRING` | `TO_STRING(value)` | Convert value to its string representation. |
193
+ | `TO_NUMBER` | `TO_NUMBER(value)` | Parse a string as an integer (`atoll`). Returns `0` for non-parseable strings. Returns the value unchanged if already an int. |
194
+ | `CHAR_AT` | `CHAR_AT(str, index)` | Returns the single-character string at `index`. Returns `NULL` if index is out of bounds. |
195
+ | `SUBSTR` | `SUBSTR(str, start, length)` | Returns a substring starting at `start` for `length` characters. Clamps to string bounds. |
196
+ | `TRIM` | `TRIM(str)` | Removes leading and trailing whitespace (spaces, tabs, newlines, carriage returns). |
197
+ | `TO_UPPER` | `TO_UPPER(str)` | Returns an uppercase copy of the string. Returns `""` for non-strings. |
198
+ | `TO_LOWER` | `TO_LOWER(str)` | Returns a lowercase copy of the string. Returns `""` for non-strings. |
199
+ | `CHAR` | `CHAR(code)` | Convert an integer code point to a single-character string. |
200
+ | `ORD` | `ORD(char)` | Convert the first character of a string to its integer code point. Returns `0` for empty strings. |
201
+
202
+ #### Math Functions
203
+
204
+ | Function | Signature | Description |
205
+ |----------|-----------|-------------|
206
+ | `ABS` | `ABS(n)` | Absolute value. Returns `0` for non-integers. |
207
+ | `MIN` | `MIN(a, b)` | Returns the smaller of two integers. |
208
+ | `MAX` | `MAX(a, b)` | Returns the larger of two integers. |
209
+ | `SQRT` | `SQRT(n)` | Integer square root (truncated). |
210
+ | `POW` | `POW(base, exp)` | Integer power: `base` raised to `exp`. |
211
+ | `FLOOR` | `FLOOR(n)` | Floor (no-op on integers, passes value through). |
212
+ | `CEIL` | `CEIL(n)` | Ceiling (no-op on integers, passes value through). |
213
+ | `ROUND` | `ROUND(n)` | Round (no-op on integers, passes value through). |
214
+ | `RANDOM` | `RANDOM()` | Returns a random integer from `0` to `99`. |
215
+
216
+ #### Array Functions
217
+
218
+ | Function | Signature | Description |
219
+ |----------|-----------|-------------|
220
+ | `PUSH` | `PUSH(arr, value)` | Appends `value` to the end of `arr`. Returns the modified array. |
221
+ | `POP` | `POP(arr)` | Removes and returns the last element. Returns `NULL` if the array is empty. |
222
+
223
+ #### System Functions
224
+
225
+ | Function | Signature | Description |
226
+ |----------|-----------|-------------|
227
+ | `TIME` | `TIME()` | Returns the current epoch time in seconds. |
228
+ | `READ_FILE` | `READ_FILE(path)` | Reads and returns the entire contents of a file as a string. Returns `NULL` if the file cannot be opened. |
229
+ | `WRITE_FILE` | `WRITE_FILE(path, content)` | Writes `content` to the file at `path`. Returns `TRUE` on success, `FALSE` on failure. |
230
+
231
+ ## Example Programs
232
+
233
+ ### Factorial
234
+ ```
235
+ FUNC! factorial(n):
236
+ IF? n <= 1:
237
+ RETURN 1
238
+ END!
239
+ RETURN n * factorial(n - 1)
240
+ END!
241
+
242
+ PRINT factorial(5) # 120
243
+ PRINT factorial(10) # 3628800
244
+ ```
245
+
246
+ ### Array Mutation
247
+ ```
248
+ LET arr = [1, 2, 3]
249
+ PUSH(arr, 4)
250
+ PRINT arr # [1, 2, 3, 4]
251
+ arr[0] = 99
252
+ PRINT arr # [99, 2, 3, 4]
253
+ PRINT POP(arr) # 4
254
+ PRINT arr # [99, 2, 3]
255
+ ```
256
+
257
+ ### Try/Catch
258
+ ```
259
+ TRY:
260
+ LET result = TO_NUMBER("not a number")
261
+ IF? result == 0:
262
+ THROW "parse failed"
263
+ END!
264
+ CATCH err:
265
+ PRINT err
266
+ END!
267
+ ```
package/bin/neb.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { execFileSync } = require('child_process');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+
8
+ const args = process.argv.slice(2);
9
+ const exeName = process.platform === 'win32' ? 'nebulara.exe' : 'nebulara';
10
+
11
+ // Try to find the native binary
12
+ function findBinary() {
13
+ // In installed package: bin/../build/
14
+ const pkgRoot = path.resolve(__dirname, '..');
15
+ const candidates = [
16
+ path.join(pkgRoot, 'build', exeName),
17
+ path.join(pkgRoot, exeName),
18
+ ];
19
+ for (const p of candidates) {
20
+ if (fs.existsSync(p)) return p;
21
+ }
22
+ return null;
23
+ }
24
+
25
+ const bin = findBinary();
26
+ if (!bin) {
27
+ console.error('Error: Nebulara binary not found. Run "npm run build" first.');
28
+ process.exit(1);
29
+ }
30
+
31
+ try {
32
+ execFileSync(bin, args, { stdio: 'inherit' });
33
+ } catch (err) {
34
+ if (err.status !== undefined) process.exit(err.status);
35
+ process.exit(1);
36
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/index.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @codurra/nebulara - Nebulara Programming Language Runtime
3
+ *
4
+ * Provides programmatic access to the Nebulara interpreter, transpiler, and compiler.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { execFileSync } = require('child_process');
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+ const os = require('os');
13
+
14
+ const BUILD_DIR = path.join(__dirname, '..', 'build');
15
+ const PLATFORM = os.platform();
16
+ const EXTENSION = PLATFORM === 'win32' ? '.exe' : '';
17
+
18
+ const INTERPRETER = path.join(BUILD_DIR, `nebulara${EXTENSION}`);
19
+ const CLI = path.join(BUILD_DIR, `neb-cli${EXTENSION}`);
20
+ const PIPELINE = path.join(BUILD_DIR, `neb-pipeline${EXTENSION}`);
21
+
22
+ /**
23
+ * Run a Nebulara source file
24
+ * @param {string} sourcePath - Path to .nbs file
25
+ * @param {object} options - { debug: boolean }
26
+ * @returns {string} stdout output
27
+ */
28
+ function run(sourcePath, options = {}) {
29
+ if (!fs.existsSync(sourcePath)) throw new Error(`Source file not found: ${sourcePath}`);
30
+ const args = [sourcePath];
31
+ if (options.debug) args.unshift('-d');
32
+ return execFileSync(INTERPRETER, args, { encoding: 'utf8', timeout: 30000 });
33
+ }
34
+
35
+ /**
36
+ * Run a Nebulara source string
37
+ * @param {string} source - Nebulara source code
38
+ * @param {object} options - { debug: boolean }
39
+ * @returns {string} stdout output
40
+ */
41
+ function runString(source, options = {}) {
42
+ const tmpFile = path.join(os.tmpdir(), `neb_${Date.now()}.nbs`);
43
+ try {
44
+ fs.writeFileSync(tmpFile, source, 'utf8');
45
+ return run(tmpFile, options);
46
+ } finally {
47
+ try { fs.unlinkSync(tmpFile); } catch (e) {}
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Transpile Nebulara to JavaScript
53
+ * @param {string} sourcePath - Path to .nbs file
54
+ * @returns {string} Transpiled JavaScript code
55
+ */
56
+ function transpileToJS(sourcePath) {
57
+ if (!fs.existsSync(PIPELINE)) throw new Error('Pipeline binary not found. Run "npm run build" first.');
58
+ return execFileSync(PIPELINE, [sourcePath, '--target', 'js'], { encoding: 'utf8' });
59
+ }
60
+
61
+ /**
62
+ * Transpile Nebulara to Python
63
+ * @param {string} sourcePath - Path to .nbs file
64
+ * @returns {string} Transpiled Python code
65
+ */
66
+ function transpileToPython(sourcePath) {
67
+ if (!fs.existsSync(PIPELINE)) throw new Error('Pipeline binary not found. Run "npm run build" first.');
68
+ return execFileSync(PIPELINE, [sourcePath, '--target', 'py'], { encoding: 'utf8' });
69
+ }
70
+
71
+ /**
72
+ * Check if a .nbs file parses correctly
73
+ * @param {string} sourcePath - Path to .nbs file
74
+ * @returns {{ ok: boolean, output: string }}
75
+ */
76
+ function check(sourcePath) {
77
+ if (!fs.existsSync(PIPELINE)) throw new Error('Pipeline binary not found. Run "npm run build" first.');
78
+ try {
79
+ const output = execFileSync(PIPELINE, [sourcePath], { encoding: 'utf8' });
80
+ return { ok: true, output: output.trim() };
81
+ } catch (e) {
82
+ return { ok: false, output: (e.stderr || e.message).trim() };
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Get the language specification
88
+ * @returns {string} SPEC.md content
89
+ */
90
+ function getSpec() {
91
+ return fs.readFileSync(path.join(__dirname, '..', 'SPEC.md'), 'utf8');
92
+ }
93
+
94
+ /**
95
+ * Run the built-in test suite
96
+ * @returns {{ passed: number, failed: number, results: object[] }}
97
+ */
98
+ function test() {
99
+ const testDir = path.join(__dirname, '..', 'test');
100
+ const results = [];
101
+ let passed = 0, failed = 0;
102
+ const testFiles = fs.readdirSync(testDir).filter(f => f.endsWith('.nbs'));
103
+ for (const file of testFiles) {
104
+ try {
105
+ const output = run(path.join(testDir, file));
106
+ results.push({ file, status: 'pass', output: output.trim() });
107
+ passed++;
108
+ } catch (e) {
109
+ results.push({ file, status: 'fail', error: e.message });
110
+ failed++;
111
+ }
112
+ }
113
+ return { passed, failed, results };
114
+ }
115
+
116
+ /**
117
+ * Check if the Nebulara binary is available
118
+ * @returns {boolean}
119
+ */
120
+ function isAvailable() { return fs.existsSync(INTERPRETER); }
121
+
122
+ /**
123
+ * Get the version string
124
+ * @returns {string}
125
+ */
126
+ function getVersion() { return '2.0.0'; }
127
+
128
+ module.exports = {
129
+ run, runString, transpileToJS, transpileToPython, check, getSpec,
130
+ test, isAvailable, getVersion, INTERPRETER, CLI, PIPELINE, BUILD_DIR
131
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "nebulara",
3
+ "version": "2.0.0",
4
+ "description": "Nebulara - AI-native universal programming language ecosystem",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "neb": "bin/neb.js",
8
+ "nebulara": "bin/neb.js"
9
+ },
10
+ "scripts": {
11
+ "build": "node scripts/build.js",
12
+ "test": "node scripts/test.js",
13
+ "clean": "node -e \"require('fs').rmSync('build',{recursive:true,force:true})\"",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "dependencies": {},
17
+ "devDependencies": {},
18
+ "files": [
19
+ "bin/",
20
+ "dist/",
21
+ "build/",
22
+ "Compiler/",
23
+ "std/",
24
+ "test/",
25
+ "SPEC.md",
26
+ "README.md"
27
+ ],
28
+ "keywords": ["compiler", "programming-language", "ai-native", "nebulara"],
29
+ "author": "Codurra",
30
+ "license": "UNLICENSED",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/CODURRALABS/NEBULARA.git"
34
+ },
35
+ "engines": {
36
+ "node": ">=14.0.0"
37
+ },
38
+ "os": ["win32", "linux", "darwin"]
39
+ }
@@ -0,0 +1,72 @@
1
+ # Nebulara Standard Library - Collections
2
+ # Array and collection utilities
3
+
4
+ FUNC! find(arr, value):
5
+ LET i = 0
6
+ LET len = LEN(arr)
7
+ WHILE? i < len:
8
+ IF? arr[i] == value:
9
+ RETURN i
10
+ END!
11
+ i = i + 1
12
+ END!
13
+ RETURN -1
14
+ END!
15
+
16
+ FUNC! contains(arr, value):
17
+ RETURN find(arr, value) != -1
18
+ END!
19
+
20
+ FUNC! reverse_array(arr):
21
+ LET len = LEN(arr)
22
+ LET result = []
23
+ LET i = len - 1
24
+ WHILE? i >= 0:
25
+ result = result + [arr[i]]
26
+ i = i - 1
27
+ END!
28
+ RETURN result
29
+ END!
30
+
31
+ FUNC! sum_array(arr):
32
+ LET total = 0
33
+ LET i = 0
34
+ LET len = LEN(arr)
35
+ WHILE? i < len:
36
+ total = total + arr[i]
37
+ i = i + 1
38
+ END!
39
+ RETURN total
40
+ END!
41
+
42
+ FUNC! max_array(arr):
43
+ IF? LEN(arr) == 0:
44
+ RETURN 0
45
+ END!
46
+ LET m = arr[0]
47
+ LET i = 1
48
+ LET len = LEN(arr)
49
+ WHILE? i < len:
50
+ IF? arr[i] > m:
51
+ m = arr[i]
52
+ END!
53
+ i = i + 1
54
+ END!
55
+ RETURN m
56
+ END!
57
+
58
+ FUNC! min_array(arr):
59
+ IF? LEN(arr) == 0:
60
+ RETURN 0
61
+ END!
62
+ LET m = arr[0]
63
+ LET i = 1
64
+ LET len = LEN(arr)
65
+ WHILE? i < len:
66
+ IF? arr[i] < m:
67
+ m = arr[i]
68
+ END!
69
+ i = i + 1
70
+ END!
71
+ RETURN m
72
+ END!
package/std/json.nbs ADDED
@@ -0,0 +1,15 @@
1
+ # Nebulara Standard Library - JSON
2
+ # Simple JSON operations (basic implementation)
3
+
4
+ # JSON is not natively supported yet.
5
+ # These are placeholder functions that demonstrate the API.
6
+
7
+ FUNC! json_stringify(value):
8
+ RETURN TO_STRING(value)
9
+ END!
10
+
11
+ # json_parse is not yet implemented - requires FFI to C JSON parser library
12
+ FUNC! json_parse(str):
13
+ PRINT "JSON.parse: not yet implemented - requires FFI to C JSON parser library"
14
+ RETURN NULL
15
+ END!
package/std/kanban.nbs ADDED
@@ -0,0 +1,122 @@
1
+ # Nebulara Standard Library - Kanban
2
+ # Task board data structures and operations
3
+ # Uses arrays for columns and a flat task list
4
+
5
+ # Board state: four column arrays storing task IDs, and a tasks array
6
+ LET _todo = []
7
+ LET _in_progress = []
8
+ LET _review = []
9
+ LET _done = []
10
+ LET _tasks = []
11
+ LET _next_id = 1
12
+
13
+ # Create a new task and add it to the todo column
14
+ FUNC! create_task(title, description, priority):
15
+ LET task = [_next_id, title, description, priority, "todo"]
16
+ _tasks = _tasks + [task]
17
+ _todo = _todo + [_next_id]
18
+ _next_id = _next_id + 1
19
+ RETURN task[0]
20
+ END!
21
+
22
+ # Move a task to a new column
23
+ # status must be: "todo", "in_progress", "review", or "done"
24
+ FUNC! move_task(task_id, new_status):
25
+ LET i = 0
26
+ LET len = LEN(_tasks)
27
+ WHILE? i < len:
28
+ IF? _tasks[i][0] == task_id:
29
+ LET old_status = _tasks[i][4]
30
+ IF? old_status == new_status:
31
+ RETURN TRUE
32
+ END!
33
+ _tasks[i][4] = new_status
34
+ # Remove task_id from old column
35
+ IF? old_status == "todo":
36
+ _todo = _remove_id(_todo, task_id)
37
+ END!
38
+ IF? old_status == "in_progress":
39
+ _in_progress = _remove_id(_in_progress, task_id)
40
+ END!
41
+ IF? old_status == "review":
42
+ _review = _remove_id(_review, task_id)
43
+ END!
44
+ IF? old_status == "done":
45
+ _done = _remove_id(_done, task_id)
46
+ END!
47
+ # Add task_id to new column
48
+ IF? new_status == "todo":
49
+ _todo = _todo + [task_id]
50
+ END!
51
+ IF? new_status == "in_progress":
52
+ _in_progress = _in_progress + [task_id]
53
+ END!
54
+ IF? new_status == "review":
55
+ _review = _review + [task_id]
56
+ END!
57
+ IF? new_status == "done":
58
+ _done = _done + [task_id]
59
+ END!
60
+ RETURN TRUE
61
+ END!
62
+ i = i + 1
63
+ END!
64
+ RETURN FALSE
65
+ END!
66
+
67
+ # Get a task by ID, returns the task array or NULL
68
+ FUNC! get_task(task_id):
69
+ LET i = 0
70
+ LET len = LEN(_tasks)
71
+ WHILE? i < len:
72
+ IF? _tasks[i][0] == task_id:
73
+ RETURN _tasks[i]
74
+ END!
75
+ i = i + 1
76
+ END!
77
+ RETURN NULL
78
+ END!
79
+
80
+ # Print the full board state
81
+ FUNC! print_board():
82
+ PRINT "=== TODO ==="
83
+ _print_column(_todo)
84
+ PRINT "=== IN PROGRESS ==="
85
+ _print_column(_in_progress)
86
+ PRINT "=== REVIEW ==="
87
+ _print_column(_review)
88
+ PRINT "=== DONE ==="
89
+ _print_column(_done)
90
+ END!
91
+
92
+ # Print task summary for a list of task IDs
93
+ FUNC! _print_column(column):
94
+ LET i = 0
95
+ LET len = LEN(column)
96
+ WHILE? i < len:
97
+ LET task = get_task(column[i])
98
+ IF? task != NULL:
99
+ PRINT task[0] + ": " + task[1] + " [" + task[3] + "]"
100
+ END!
101
+ i = i + 1
102
+ END!
103
+ END!
104
+
105
+ # Remove a task ID from an array of IDs, return new array
106
+ FUNC! _remove_id(arr, task_id):
107
+ LET result = []
108
+ LET i = 0
109
+ LET len = LEN(arr)
110
+ WHILE? i < len:
111
+ IF? arr[i] != task_id:
112
+ result = result + [arr[i]]
113
+ END!
114
+ i = i + 1
115
+ END!
116
+ RETURN result
117
+ END!
118
+
119
+ # Count tasks in a column
120
+ FUNC! column_count(column):
121
+ RETURN LEN(column)
122
+ END!