naidejs 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.
@@ -0,0 +1,286 @@
1
+ const TYPE_MAP = { s: 'str', i: 'int', n: 'num', b: 'bool', l: 'list', m: 'map', a: 'any', j: 'json', v: 'void' };
2
+ const TYPE_CHARS = new Set(Object.keys(TYPE_MAP));
3
+
4
+ function expandType(ch) { return TYPE_MAP[ch] || ch; }
5
+
6
+ function expandParams(paramStr) {
7
+ if (!paramStr) return '';
8
+ return paramStr.split(',').map(p => {
9
+ p = p.trim();
10
+ if (!p) return p;
11
+ const m = p.match(/^(\.\.\.)?([ sinblmaj]):(.+)$/);
12
+ if (m) return `${m[1] || ''}${expandType(m[2])} ${m[3]}`;
13
+ const m2 = p.match(/^(\.\.\.)?([ sinblmaj])(\w+)$/);
14
+ if (m2) return `${m2[1] || ''}${expandType(m2[2])} ${m2[3]}`;
15
+ return p;
16
+ }).join(', ');
17
+ }
18
+
19
+ function transformFunc(rest, isAsync, isPublic) {
20
+ const pub = isPublic ? 'pub ' : '';
21
+ const fn = isAsync ? 'fn.async' : 'fn';
22
+ const nameM = rest.match(/^(\w+)/);
23
+ if (!nameM) return `${pub}${fn} ${rest}:`;
24
+ const name = nameM[1];
25
+ let rem = rest.slice(name.length);
26
+
27
+ let params = '';
28
+ if (rem.startsWith('(')) {
29
+ let depth = 1, i = 1;
30
+ while (i < rem.length && depth > 0) {
31
+ if (rem[i] === '(') depth++;
32
+ if (rem[i] === ')') depth--;
33
+ i++;
34
+ }
35
+ params = expandParams(rem.slice(1, i - 1));
36
+ rem = rem.slice(i).trim();
37
+ }
38
+
39
+ let retType = '';
40
+ if (rem.length > 0 && TYPE_CHARS.has(rem[0]) && (rem.length === 1 || !rem[1].match(/\w/))) {
41
+ retType = ` -> ${expandType(rem[0])}`;
42
+ }
43
+
44
+ return `${pub}${fn} ${name}(${params})${retType}:`;
45
+ }
46
+
47
+ function transformRoute(method, rest) {
48
+ const m = rest.match(/^("[^"]*")\s*(\([^)]*\))?\s*(.*)$/);
49
+ if (!m) return `${method} ${rest}:`;
50
+ const path = m[1];
51
+ const routeParams = m[2] ? ` ${m[2]}` : '';
52
+ const inline = m[3] ? m[3].trim() : '';
53
+
54
+ if (inline) {
55
+ const body = transformContent(inline);
56
+ return `${method} ${path}${routeParams}:\n ${body}`;
57
+ }
58
+ return `${method} ${path}${routeParams}:`;
59
+ }
60
+
61
+ function transformAwait(text) {
62
+ // ~~ → await.all
63
+ text = text.replace(/~~/g, 'await.all ');
64
+ // ~ before identifier or ( → await (but not ~f, ~TYPE: which are handled at line level)
65
+ text = text.replace(/~(\w)/g, (_, ch) => `await ${ch}`);
66
+ text = text.replace(/~\(/g, 'await (');
67
+ return text;
68
+ }
69
+
70
+ function transformContent(line) {
71
+ // Log shorthands
72
+ line = line.replace(/\blog\.e\b/g, 'log.error');
73
+ line = line.replace(/\blog\.w\b/g, 'log.warn');
74
+ // log"msg" → log "msg"
75
+ line = line.replace(/\blog"/g, 'log "');
76
+ line = line.replace(/\blog\.error"/g, 'log.error "');
77
+ line = line.replace(/\blog\.warn"/g, 'log.warn "');
78
+ // Await transforms
79
+ line = transformAwait(line);
80
+ return line;
81
+ }
82
+
83
+ export function preprocess(source) {
84
+ const lines = source.split('\n');
85
+ const result = [];
86
+
87
+ for (let raw of lines) {
88
+ const indentM = raw.match(/^(\s*)/);
89
+ const indent = indentM ? indentM[1] : '';
90
+ let line = raw.slice(indent.length);
91
+
92
+ // Empty line
93
+ if (!line.trim()) { result.push(''); continue; }
94
+
95
+ // Comment: -- → #
96
+ if (line.startsWith('--')) { result.push(indent + '#' + line.slice(2)); continue; }
97
+
98
+ // Keep # comments as-is
99
+ if (line.startsWith('#') && !line.startsWith('#=')) { result.push(raw); continue; }
100
+
101
+ const first = line[0];
102
+ const second = line[1] || '';
103
+ let out = '';
104
+
105
+ // > return
106
+ if (first === '>' && second !== '=' && second !== '>') {
107
+ const rest = line.slice(1).trim();
108
+ if (rest.startsWith('.s ') || rest.startsWith('.s(')) {
109
+ out = 'ret.status ' + transformContent(rest.slice(3));
110
+ } else {
111
+ out = rest ? 'ret ' + transformContent(rest) : 'ret';
112
+ }
113
+ }
114
+ // ? if
115
+ else if (first === '?' && second !== '.' && second !== '?') {
116
+ out = 'if ' + transformContent(line.slice(1).trim()) + ':';
117
+ }
118
+ // | elif (but not |>)
119
+ else if (first === '|' && second !== '>') {
120
+ out = 'elif ' + transformContent(line.slice(1).trim()) + ':';
121
+ }
122
+ // : else (alone on line)
123
+ else if (line.trim() === ':') {
124
+ out = 'else:';
125
+ }
126
+ // @ loop
127
+ else if (first === '@') {
128
+ const rest = line.slice(1);
129
+ const loopM = rest.match(/^(\w+(?:,\w+)?)<(.+)$/);
130
+ if (loopM) {
131
+ const vars = loopM[1];
132
+ const col = transformContent(loopM[2]);
133
+ if (col.includes('..')) {
134
+ const [start, end] = col.split('..');
135
+ out = `for ${vars} in ${start}..${end}:`;
136
+ } else if (vars.includes(',')) {
137
+ const [k, v] = vars.split(',');
138
+ out = `each ${k}, ${v} in ${col}:`;
139
+ } else {
140
+ out = `each ${vars} in ${col}:`;
141
+ }
142
+ } else {
143
+ out = transformContent(line);
144
+ }
145
+ }
146
+ // * while
147
+ else if (first === '*') {
148
+ out = 'while ' + transformContent(line.slice(1).trim()) + ':';
149
+ }
150
+ // !! catch (before ! try check)
151
+ else if (first === '!' && second === '!') {
152
+ const rest = line.slice(2).trim();
153
+ out = rest ? `fail ${rest}:` : 'fail:';
154
+ }
155
+ // ! try (alone)
156
+ else if (line.trim() === '!') {
157
+ out = 'try:';
158
+ }
159
+ // $ server
160
+ else if (first === '$') {
161
+ const rest = line.slice(1);
162
+ const srvM = rest.match(/^(\w+):(.+)$/);
163
+ if (srvM) {
164
+ out = `server ${srvM[1]} port ${transformContent(srvM[2].trim())}:`;
165
+ } else {
166
+ out = `server ${rest.trim()}:`;
167
+ }
168
+ }
169
+ // HTTP routes: G P U D
170
+ else if (first === 'G' && second === '"') { out = transformRoute('get', line.slice(1)); }
171
+ else if (first === 'P' && second === '"') { out = transformRoute('post', line.slice(1)); }
172
+ else if (first === 'U' && second === '"') { out = transformRoute('put', line.slice(1)); }
173
+ else if (first === 'D' && second === '"') { out = transformRoute('del', line.slice(1)); }
174
+ // ^ model
175
+ else if (first === '^') {
176
+ const rest = line.slice(1).trim();
177
+ const extM = rest.match(/^(\w+)<(\w+)$/);
178
+ if (extM) {
179
+ out = `model ${extM[1]} extends ${extM[2]}:`;
180
+ } else {
181
+ out = `model ${rest}:`;
182
+ }
183
+ }
184
+ // < import
185
+ else if (first === '<') {
186
+ const rest = line.slice(1);
187
+ if (rest.startsWith('{')) {
188
+ const impM = rest.match(/^\{([^}]+)\}"(.+)"$/);
189
+ if (impM) {
190
+ out = `use {${impM[1]}} from "${impM[2]}"`;
191
+ } else {
192
+ out = transformContent(line);
193
+ }
194
+ } else {
195
+ const fromM = rest.match(/^(\w+)"(.+)"$/);
196
+ if (fromM) {
197
+ out = `use ${fromM[1]} from "${fromM[2]}"`;
198
+ } else {
199
+ out = `use ${rest.trim()}`;
200
+ }
201
+ }
202
+ }
203
+ // % match
204
+ else if (first === '%') {
205
+ out = 'match ' + transformContent(line.slice(1).trim()) + ':';
206
+ }
207
+ // + export prefix
208
+ else if (first === '+') {
209
+ const inner = line.slice(1);
210
+ if (inner.startsWith('~f ')) {
211
+ out = transformFunc(inner.slice(3), true, true);
212
+ } else if (inner.startsWith('f ')) {
213
+ out = transformFunc(inner.slice(2), false, true);
214
+ } else {
215
+ const tvM = inner.match(/^([sinblmaj]):(\w+)=(.+)$/);
216
+ if (tvM) {
217
+ out = `pub ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
218
+ } else {
219
+ out = 'pub ' + transformContent(inner);
220
+ }
221
+ }
222
+ }
223
+ // ~f async function
224
+ else if (first === '~' && second === 'f' && line[2] === ' ') {
225
+ out = transformFunc(line.slice(3), true, false);
226
+ }
227
+ // ~TYPE:name=value → mutable
228
+ else if (first === '~' && TYPE_CHARS.has(second) && line[2] === ':') {
229
+ const rest = line.slice(1);
230
+ const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
231
+ if (tvM) {
232
+ out = `mut ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
233
+ } else {
234
+ // Mutable field without assignment: ~s:name
235
+ const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
236
+ if (fieldM) {
237
+ out = `mut ${expandType(fieldM[1])} ${fieldM[2]}`;
238
+ } else {
239
+ out = transformContent(line);
240
+ }
241
+ }
242
+ }
243
+ // f function
244
+ else if (first === 'f' && second === ' ') {
245
+ out = transformFunc(line.slice(2), false, false);
246
+ }
247
+ // TYPE:name=value → typed variable
248
+ else if (TYPE_CHARS.has(first) && second === ':') {
249
+ const rest = line;
250
+ const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
251
+ if (tvM) {
252
+ out = `${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
253
+ } else {
254
+ // Could be a type field in model: s:name (no assignment)
255
+ const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
256
+ if (fieldM) {
257
+ out = `${expandType(fieldM[1])} ${fieldM[2]}`;
258
+ } else {
259
+ // type:name=... with complex value
260
+ const fieldDefM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
261
+ if (fieldDefM) {
262
+ out = `${expandType(fieldDefM[1])} ${fieldDefM[2]} = ${transformContent(fieldDefM[3])}`;
263
+ } else {
264
+ out = transformContent(line);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ // Default: apply inline transforms
270
+ else {
271
+ out = transformContent(line);
272
+ }
273
+
274
+ // Handle multi-line output from route transform
275
+ if (out.includes('\n')) {
276
+ const subLines = out.split('\n');
277
+ for (const sl of subLines) {
278
+ result.push(indent + sl);
279
+ }
280
+ } else {
281
+ result.push(indent + out);
282
+ }
283
+ }
284
+
285
+ return result.join('\n');
286
+ }
package/src/tokens.js ADDED
@@ -0,0 +1,162 @@
1
+ export const T = {
2
+ // Literals
3
+ NUMBER: 'NUMBER',
4
+ STRING: 'STRING',
5
+ BOOL: 'BOOL',
6
+ NULL: 'NULL',
7
+ IDENT: 'IDENT',
8
+
9
+ // Types
10
+ TYPE_STR: 'TYPE_STR',
11
+ TYPE_INT: 'TYPE_INT',
12
+ TYPE_NUM: 'TYPE_NUM',
13
+ TYPE_BOOL: 'TYPE_BOOL',
14
+ TYPE_LIST: 'TYPE_LIST',
15
+ TYPE_MAP: 'TYPE_MAP',
16
+ TYPE_ANY: 'TYPE_ANY',
17
+ TYPE_JSON: 'TYPE_JSON',
18
+ TYPE_VOID: 'TYPE_VOID',
19
+
20
+ // Keywords
21
+ USE: 'USE',
22
+ FN: 'FN',
23
+ FN_ASYNC: 'FN_ASYNC',
24
+ RET: 'RET',
25
+ MUT: 'MUT',
26
+ PUB: 'PUB',
27
+ IF: 'IF',
28
+ ELIF: 'ELIF',
29
+ ELSE: 'ELSE',
30
+ EACH: 'EACH',
31
+ FOR: 'FOR',
32
+ WHILE: 'WHILE',
33
+ IN: 'IN',
34
+ MATCH: 'MATCH',
35
+ TRY: 'TRY',
36
+ FAIL: 'FAIL',
37
+ SERVER: 'SERVER',
38
+ GET: 'GET',
39
+ POST: 'POST',
40
+ PUT: 'PUT',
41
+ DEL: 'DEL',
42
+ MID: 'MID',
43
+ MODEL: 'MODEL',
44
+ ON: 'ON',
45
+ AWAIT: 'AWAIT',
46
+ AWAIT_ALL: 'AWAIT_ALL',
47
+ SELF: 'SELF',
48
+ NOT: 'NOT',
49
+ AND: 'AND',
50
+ OR: 'OR',
51
+ THEN: 'THEN',
52
+ DB: 'DB',
53
+ LOG: 'LOG',
54
+ NEW: 'NEW',
55
+ FROM: 'FROM',
56
+ AS: 'AS',
57
+ BREAK: 'BREAK',
58
+ CONTINUE: 'CONTINUE',
59
+ THROW: 'THROW',
60
+
61
+ // Operators
62
+ ASSIGN: 'ASSIGN',
63
+ PLUS: 'PLUS',
64
+ MINUS: 'MINUS',
65
+ STAR: 'STAR',
66
+ SLASH: 'SLASH',
67
+ PERCENT: 'PERCENT',
68
+ EQ: 'EQ',
69
+ NEQ: 'NEQ',
70
+ GT: 'GT',
71
+ LT: 'LT',
72
+ GTE: 'GTE',
73
+ LTE: 'LTE',
74
+ PIPE: 'PIPE',
75
+ ARROW: 'ARROW',
76
+ FAT_ARROW: 'FAT_ARROW',
77
+ RANGE: 'RANGE',
78
+ NULLISH: 'NULLISH',
79
+ OPTIONAL: 'OPTIONAL',
80
+ SPREAD: 'SPREAD',
81
+ PLUS_ASSIGN: 'PLUS_ASSIGN',
82
+ MINUS_ASSIGN: 'MINUS_ASSIGN',
83
+
84
+ // Delimiters
85
+ LPAREN: 'LPAREN',
86
+ RPAREN: 'RPAREN',
87
+ LBRACKET: 'LBRACKET',
88
+ RBRACKET: 'RBRACKET',
89
+ LBRACE: 'LBRACE',
90
+ RBRACE: 'RBRACE',
91
+ COLON: 'COLON',
92
+ COMMA: 'COMMA',
93
+ DOT: 'DOT',
94
+
95
+ // Structure
96
+ INDENT: 'INDENT',
97
+ DEDENT: 'DEDENT',
98
+ NEWLINE: 'NEWLINE',
99
+ EOF: 'EOF',
100
+
101
+ // Special
102
+ COMMENT: 'COMMENT',
103
+ INTERP_START: 'INTERP_START',
104
+ INTERP_END: 'INTERP_END',
105
+ };
106
+
107
+ export const KEYWORDS = {
108
+ 'use': T.USE,
109
+ 'fn': T.FN,
110
+ 'ret': T.RET,
111
+ 'mut': T.MUT,
112
+ 'pub': T.PUB,
113
+ 'if': T.IF,
114
+ 'elif': T.ELIF,
115
+ 'else': T.ELSE,
116
+ 'each': T.EACH,
117
+ 'for': T.FOR,
118
+ 'while': T.WHILE,
119
+ 'in': T.IN,
120
+ 'match': T.MATCH,
121
+ 'try': T.TRY,
122
+ 'fail': T.FAIL,
123
+ 'server': T.SERVER,
124
+ 'get': T.GET,
125
+ 'post': T.POST,
126
+ 'put': T.PUT,
127
+ 'del': T.DEL,
128
+ 'mid': T.MID,
129
+ 'model': T.MODEL,
130
+ 'on': T.ON,
131
+ 'await': T.AWAIT,
132
+ 'self': T.SELF,
133
+ 'not': T.NOT,
134
+ 'and': T.AND,
135
+ 'or': T.OR,
136
+ 'then': T.THEN,
137
+ 'db': T.DB,
138
+ 'log': T.LOG,
139
+ 'new': T.NEW,
140
+ 'from': T.FROM,
141
+ 'as': T.AS,
142
+ 'break': T.BREAK,
143
+ 'continue': T.CONTINUE,
144
+ 'throw': T.THROW,
145
+ 'true': T.BOOL,
146
+ 'false': T.BOOL,
147
+ 'null': T.NULL,
148
+ 'str': T.TYPE_STR,
149
+ 'int': T.TYPE_INT,
150
+ 'num': T.TYPE_NUM,
151
+ 'bool': T.TYPE_BOOL,
152
+ 'list': T.TYPE_LIST,
153
+ 'map': T.TYPE_MAP,
154
+ 'any': T.TYPE_ANY,
155
+ 'json': T.TYPE_JSON,
156
+ 'void': T.TYPE_VOID,
157
+ };
158
+
159
+ export const TYPE_TOKENS = new Set([
160
+ T.TYPE_STR, T.TYPE_INT, T.TYPE_NUM, T.TYPE_BOOL,
161
+ T.TYPE_LIST, T.TYPE_MAP, T.TYPE_ANY, T.TYPE_JSON, T.TYPE_VOID,
162
+ ]);