naider 1.24.5 → 2.0.1
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 +133 -112
- package/SPEC.naide +8 -6
- package/bin/naide.js +509 -124
- package/bin/postinstall.js +5 -15
- package/examples/model.naide +1 -1
- package/lsp/server.js +210 -101
- package/mcp/server.js +247 -22
- package/package.json +8 -5
- package/src/gen.js +853 -324
- package/src/generator-c.js +34 -14
- package/src/generator-go.js +26 -0
- package/src/generator.js +5 -1
- package/src/index.js +34 -14
- package/src/parser.js +15 -14
- package/src/project-tools.js +785 -0
- package/src/typechecker.js +147 -18
- package/vscode-naide/README.md +4 -6
- package/vscode-naide/extension.js +1 -2
- package/vscode-naide/package-lock.json +2 -2
- package/vscode-naide/package.json +3 -15
- package/SPEC-X.nx +0 -276
- package/assets/nx.ico +0 -0
- package/assets/nx_check.png +0 -0
- package/examples/ai-app.nx +0 -21
- package/examples/async.nx +0 -40
- package/examples/crud.nx +0 -64
- package/examples/fullapp.nx +0 -74
- package/examples/hello.nx +0 -29
- package/examples/server.nx +0 -27
- package/examples/sql-app.nx +0 -23
- package/examples/todo-api.nx +0 -31
- package/src/preprocess.js +0 -308
- package/vscode-naide/language-configuration-nx.json +0 -32
- package/vscode-naide/naide-lang-1.1.0.vsix +0 -0
- package/vscode-naide/syntaxes/naidex.tmLanguage.json +0 -287
package/examples/async.nx
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
-- NAIDE-X async/await
|
|
2
|
-
|
|
3
|
-
~f fetchData(s:url)a
|
|
4
|
-
a:res=~fetch(url)
|
|
5
|
-
a:data=~res.json()
|
|
6
|
-
>data
|
|
7
|
-
|
|
8
|
-
~f getTopUsers()l
|
|
9
|
-
a:data=~fetchData("https://jsonplaceholder.typicode.com/users")
|
|
10
|
-
l:result=data
|
|
11
|
-
|>filter((u)=>u.name.length>5)
|
|
12
|
-
|>map((u)=>({name:u.name,email:u.email}))
|
|
13
|
-
>result
|
|
14
|
-
|
|
15
|
-
~f safeFetch(s:url)m
|
|
16
|
-
!
|
|
17
|
-
a:data=~fetchData(url)
|
|
18
|
-
>{ok:true,data:data}
|
|
19
|
-
!!e
|
|
20
|
-
log.e"Fetch failed: {e.message}"
|
|
21
|
-
>{ok:false,error:e.message}
|
|
22
|
-
|
|
23
|
-
~f loadDash()m
|
|
24
|
-
l:urls=[
|
|
25
|
-
"https://jsonplaceholder.typicode.com/users",
|
|
26
|
-
"https://jsonplaceholder.typicode.com/posts",
|
|
27
|
-
"https://jsonplaceholder.typicode.com/todos"
|
|
28
|
-
]
|
|
29
|
-
l:results=~~urls.map((url)=>fetchData(url))
|
|
30
|
-
>{users:results[0],posts:results[1],todos:results[2]}
|
|
31
|
-
|
|
32
|
-
~f main()
|
|
33
|
-
log"Fetching data..."
|
|
34
|
-
m:dash=~safeFetch("https://jsonplaceholder.typicode.com/users")
|
|
35
|
-
?dash.ok
|
|
36
|
-
log"Got {dash.data.length} items"
|
|
37
|
-
:
|
|
38
|
-
log.e dash.error
|
|
39
|
-
|
|
40
|
-
main()
|
package/examples/crud.nx
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
-- NAIDE-X full CRUD
|
|
2
|
-
|
|
3
|
-
^Todo
|
|
4
|
-
s:id
|
|
5
|
-
s:title
|
|
6
|
-
b:done=false
|
|
7
|
-
s:createdAt
|
|
8
|
-
|
|
9
|
-
f toggle()
|
|
10
|
-
self.done=not self.done
|
|
11
|
-
|
|
12
|
-
f toJSON()m
|
|
13
|
-
>{id:self.id,title:self.title,done:self.done,createdAt:self.createdAt}
|
|
14
|
-
|
|
15
|
-
~l:todos=[]
|
|
16
|
-
~i:nextId=1
|
|
17
|
-
|
|
18
|
-
f findTodo(s:id)a
|
|
19
|
-
@todo<todos
|
|
20
|
-
?todo.id==id
|
|
21
|
-
>todo
|
|
22
|
-
>null
|
|
23
|
-
|
|
24
|
-
f genId()s
|
|
25
|
-
s:id="todo_{nextId}"
|
|
26
|
-
nextId+=1
|
|
27
|
-
>id
|
|
28
|
-
|
|
29
|
-
$app:8080
|
|
30
|
-
|
|
31
|
-
G"/api/todos"
|
|
32
|
-
>{todos:todos,count:todos.length}
|
|
33
|
-
|
|
34
|
-
G"/api/todos/:id"(req,res)
|
|
35
|
-
a:todo=findTodo(req.params.id)
|
|
36
|
-
?todo==null
|
|
37
|
-
>.s 404 {error:"Todo not found"}
|
|
38
|
-
>todo
|
|
39
|
-
|
|
40
|
-
P"/api/todos"(req,res)
|
|
41
|
-
m:body=req.body
|
|
42
|
-
?not body.title
|
|
43
|
-
>.s 400 {error:"Title is required"}
|
|
44
|
-
m:todo={id:genId(),title:body.title,done:false,createdAt:new Date().toISOString()}
|
|
45
|
-
todos.push(todo)
|
|
46
|
-
>.s 201 todo
|
|
47
|
-
|
|
48
|
-
U"/api/todos/:id"(req,res)
|
|
49
|
-
a:todo=findTodo(req.params.id)
|
|
50
|
-
?todo==null
|
|
51
|
-
>.s 404 {error:"Todo not found"}
|
|
52
|
-
m:body=req.body
|
|
53
|
-
?body.title
|
|
54
|
-
todo.title=body.title
|
|
55
|
-
?body.done!=null
|
|
56
|
-
todo.done=body.done
|
|
57
|
-
>todo
|
|
58
|
-
|
|
59
|
-
D"/api/todos/:id"(req,res)
|
|
60
|
-
i:idx=todos.findIndex((t)=>t.id==req.params.id)
|
|
61
|
-
?idx==-1
|
|
62
|
-
>.s 404 {error:"Todo not found"}
|
|
63
|
-
todos.splice(idx,1)
|
|
64
|
-
>{status:"deleted"}
|
package/examples/fullapp.nx
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
-- Full NAIDE-X app with all features
|
|
2
|
-
|
|
3
|
-
db "data/"
|
|
4
|
-
|
|
5
|
-
env:
|
|
6
|
-
PORT int default(3000)
|
|
7
|
-
JWT_SECRET str required
|
|
8
|
-
|
|
9
|
-
schema User:
|
|
10
|
-
id auto
|
|
11
|
-
name str required min(2) max(50)
|
|
12
|
-
email str required email
|
|
13
|
-
password str required
|
|
14
|
-
role str default("user")
|
|
15
|
-
|
|
16
|
-
schema Todo:
|
|
17
|
-
id auto
|
|
18
|
-
title str required min(1)
|
|
19
|
-
done bool default(false)
|
|
20
|
-
created timestamp auto
|
|
21
|
-
|
|
22
|
-
$app:PORT
|
|
23
|
-
cors "*"
|
|
24
|
-
cookie
|
|
25
|
-
auth JWT_SECRET:
|
|
26
|
-
protect "/api/*"
|
|
27
|
-
public "/api/auth/*"
|
|
28
|
-
limit "/api/*" 100 "1m"
|
|
29
|
-
static "/public"
|
|
30
|
-
crud "/api/users" User
|
|
31
|
-
crud "/api/todos" Todo
|
|
32
|
-
|
|
33
|
-
group "/api/v1":
|
|
34
|
-
G"/status"
|
|
35
|
-
>{version:"1.0",status:"ok"}
|
|
36
|
-
|
|
37
|
-
P"/api/auth/register"(req,res)
|
|
38
|
-
s:hashed=hash(req.body.password)
|
|
39
|
-
user=UserStore.create({name:req.body.name,email:req.body.email,password:hashed})
|
|
40
|
-
?user.error
|
|
41
|
-
>.s 400 {errors:user.error}
|
|
42
|
-
token=auth.sign({id:user.id,role:user.role})
|
|
43
|
-
>{token,user:{id:user.id,name:user.name,email:user.email}}
|
|
44
|
-
|
|
45
|
-
P"/api/auth/login"(req,res)
|
|
46
|
-
user=UserStore.where({email:req.body.email})[0]
|
|
47
|
-
?not user
|
|
48
|
-
>.s 401 {error:"Invalid credentials"}
|
|
49
|
-
?not verify(req.body.password,user.password)
|
|
50
|
-
>.s 401 {error:"Invalid credentials"}
|
|
51
|
-
token=auth.sign({id:user.id,role:user.role})
|
|
52
|
-
>{token}
|
|
53
|
-
|
|
54
|
-
G"/health"
|
|
55
|
-
>{status:"ok"}
|
|
56
|
-
|
|
57
|
-
G"/"
|
|
58
|
-
>.h "<h1>Welcome to NAIDE</h1>"
|
|
59
|
-
|
|
60
|
-
ws "/chat":
|
|
61
|
-
on "connect":
|
|
62
|
-
send({type:"welcome"})
|
|
63
|
-
on "message" (data):
|
|
64
|
-
broadcast(data)
|
|
65
|
-
|
|
66
|
-
error (err, req, res):
|
|
67
|
-
log.error err.message
|
|
68
|
-
>.s 500 {error:"Internal server error"}
|
|
69
|
-
|
|
70
|
-
every "30m":
|
|
71
|
-
log"cleanup running"
|
|
72
|
-
|
|
73
|
-
watch User.create (event):
|
|
74
|
-
log"new user: {event.data.name}"
|
package/examples/hello.nx
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
-- NAIDE-X hello world
|
|
2
|
-
|
|
3
|
-
s:name="World"
|
|
4
|
-
i:count=3
|
|
5
|
-
|
|
6
|
-
f greet(s:who)s
|
|
7
|
-
>"Hello, {who}!"
|
|
8
|
-
|
|
9
|
-
@i<0..count
|
|
10
|
-
s:msg=greet(name)
|
|
11
|
-
log msg
|
|
12
|
-
|
|
13
|
-
?count>5
|
|
14
|
-
log"many"
|
|
15
|
-
|count>2
|
|
16
|
-
log"a few"
|
|
17
|
-
:
|
|
18
|
-
log"few"
|
|
19
|
-
|
|
20
|
-
l:items=["apple","banana","cherry"]
|
|
21
|
-
|
|
22
|
-
@fruit<items
|
|
23
|
-
log"I like {fruit}"
|
|
24
|
-
|
|
25
|
-
s:status="ok"
|
|
26
|
-
%status
|
|
27
|
-
"ok": log "All good"
|
|
28
|
-
"error": log "Something went wrong"
|
|
29
|
-
_: log "Unknown"
|
package/examples/server.nx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
-- NAIDE-X REST API server
|
|
2
|
-
|
|
3
|
-
$app:3000
|
|
4
|
-
|
|
5
|
-
G"/"
|
|
6
|
-
>{message:"Welcome to NAIDE-X API"}
|
|
7
|
-
|
|
8
|
-
G"/users"
|
|
9
|
-
l:users=[
|
|
10
|
-
{id:1,name:"Alice",age:30},
|
|
11
|
-
{id:2,name:"Bob",age:25}
|
|
12
|
-
]
|
|
13
|
-
>users
|
|
14
|
-
|
|
15
|
-
G"/users/:id"(req,res)
|
|
16
|
-
s:id=req.params.id
|
|
17
|
-
>{id:id,name:"User {id}"}
|
|
18
|
-
|
|
19
|
-
P"/users"(req,res)
|
|
20
|
-
m:body=req.body
|
|
21
|
-
log"Creating user: {body.name}"
|
|
22
|
-
>{status:"created",user:body}
|
|
23
|
-
|
|
24
|
-
D"/users/:id"(req,res)
|
|
25
|
-
s:id=req.params.id
|
|
26
|
-
log"Deleting user {id}"
|
|
27
|
-
>{status:"deleted",id:id}
|
package/examples/sql-app.nx
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
-- SQLite-backed API (NAIDE-X)
|
|
2
|
-
-- npm install better-sqlite3
|
|
3
|
-
|
|
4
|
-
db.sql "sqlite" "app.db"
|
|
5
|
-
|
|
6
|
-
env:
|
|
7
|
-
PORT int default(3000)
|
|
8
|
-
|
|
9
|
-
schema Task:
|
|
10
|
-
id auto
|
|
11
|
-
title str required min(1) max(200)
|
|
12
|
-
done bool default(false)
|
|
13
|
-
|
|
14
|
-
$app:PORT
|
|
15
|
-
cors "*"
|
|
16
|
-
crud "/api/tasks" Task
|
|
17
|
-
|
|
18
|
-
U"/api/tasks/:id/toggle"(req,res)
|
|
19
|
-
task=TaskStore.getById(req.params.id)
|
|
20
|
-
?not task
|
|
21
|
-
>.s 404 {error:"Not found"}
|
|
22
|
-
updated=TaskStore.update(req.params.id,{done:!task.done})
|
|
23
|
-
>updated
|
package/examples/todo-api.nx
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
-- Todo API in NAIDE-X (~80% fewer tokens)
|
|
2
|
-
-- Run: naide examples/todo-api.nx
|
|
3
|
-
|
|
4
|
-
db "data/"
|
|
5
|
-
|
|
6
|
-
env:
|
|
7
|
-
PORT int default(3000)
|
|
8
|
-
|
|
9
|
-
schema Todo:
|
|
10
|
-
id auto
|
|
11
|
-
title str required min(1) max(200)
|
|
12
|
-
done bool default(false)
|
|
13
|
-
created timestamp auto
|
|
14
|
-
|
|
15
|
-
$app:PORT
|
|
16
|
-
cors "*"
|
|
17
|
-
cookie
|
|
18
|
-
cache "/api/todos" "30s"
|
|
19
|
-
validate "/api/todos" Todo
|
|
20
|
-
crud "/api/todos" Todo
|
|
21
|
-
openapi "/docs"
|
|
22
|
-
G"/"
|
|
23
|
-
>.h "<h1>Todo API</h1><p>GET /api/todos | /docs</p>"
|
|
24
|
-
G"/api/stats"(req,res)
|
|
25
|
-
l:all=TodoStore.all()
|
|
26
|
-
i:total=all.length
|
|
27
|
-
i:done=all.filter((t)=>t.done).length
|
|
28
|
-
>{total,done,pending:total-done}
|
|
29
|
-
error (err,req,res):
|
|
30
|
-
log.e err.message
|
|
31
|
-
>.s 500 {error:err.message}
|
package/src/preprocess.js
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
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*(\([^)]*\))?\s*(.*)$/);
|
|
49
|
-
if (!m) return `${method} ${rest}:`;
|
|
50
|
-
const path = m[1];
|
|
51
|
-
const middleware = m[2] ? ` ${m[2]}` : '';
|
|
52
|
-
const routeParams = m[3] ? ` ${m[3]}` : '';
|
|
53
|
-
const inline = m[4] ? m[4].trim() : '';
|
|
54
|
-
|
|
55
|
-
if (inline) {
|
|
56
|
-
const body = transformContent(inline);
|
|
57
|
-
return `${method} ${path}${middleware}${routeParams}:\n ${body}`;
|
|
58
|
-
}
|
|
59
|
-
return `${method} ${path}${middleware}${routeParams}:`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function transformAwait(text) {
|
|
63
|
-
// ~~ → await.all
|
|
64
|
-
text = text.replace(/~~/g, 'await.all ');
|
|
65
|
-
// ~ before identifier or ( → await (but not ~f, ~TYPE: which are handled at line level)
|
|
66
|
-
text = text.replace(/~(\w)/g, (_, ch) => `await ${ch}`);
|
|
67
|
-
text = text.replace(/~\(/g, 'await (');
|
|
68
|
-
return text;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function transformContent(line) {
|
|
72
|
-
// Log shorthands
|
|
73
|
-
line = line.replace(/\blog\.e\b/g, 'log.error');
|
|
74
|
-
line = line.replace(/\blog\.w\b/g, 'log.warn');
|
|
75
|
-
line = line.replace(/\blog\.i\b/g, 'log.info');
|
|
76
|
-
line = line.replace(/\blog\.d\b/g, 'log.debug');
|
|
77
|
-
// log"msg" → log "msg"
|
|
78
|
-
line = line.replace(/\blog"/g, 'log "');
|
|
79
|
-
line = line.replace(/\blog\.error"/g, 'log.error "');
|
|
80
|
-
line = line.replace(/\blog\.warn"/g, 'log.warn "');
|
|
81
|
-
line = line.replace(/\blog\.info"/g, 'log.info "');
|
|
82
|
-
line = line.replace(/\blog\.debug"/g, 'log.debug "');
|
|
83
|
-
// Await transforms
|
|
84
|
-
line = transformAwait(line);
|
|
85
|
-
return line;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function preprocess(source) {
|
|
89
|
-
const lines = source.split('\n');
|
|
90
|
-
const result = [];
|
|
91
|
-
|
|
92
|
-
for (let raw of lines) {
|
|
93
|
-
const indentM = raw.match(/^(\s*)/);
|
|
94
|
-
const indent = indentM ? indentM[1] : '';
|
|
95
|
-
let line = raw.slice(indent.length);
|
|
96
|
-
|
|
97
|
-
// Empty line
|
|
98
|
-
if (!line.trim()) { result.push(''); continue; }
|
|
99
|
-
|
|
100
|
-
// Comment: -- → #
|
|
101
|
-
if (line.startsWith('--')) { result.push(indent + '#' + line.slice(2)); continue; }
|
|
102
|
-
|
|
103
|
-
// Keep # comments as-is
|
|
104
|
-
if (line.startsWith('#') && !line.startsWith('#=')) { result.push(raw); continue; }
|
|
105
|
-
|
|
106
|
-
const first = line[0];
|
|
107
|
-
const second = line[1] || '';
|
|
108
|
-
let out = '';
|
|
109
|
-
|
|
110
|
-
// > return
|
|
111
|
-
if (first === '>' && second !== '=' && second !== '>') {
|
|
112
|
-
const rest = line.slice(1).trim();
|
|
113
|
-
if (rest.startsWith('.s ') || rest.startsWith('.s(')) {
|
|
114
|
-
out = 'ret.status ' + transformContent(rest.slice(3));
|
|
115
|
-
} else if (rest.startsWith('.r ')) {
|
|
116
|
-
out = 'ret.redirect ' + transformContent(rest.slice(3));
|
|
117
|
-
} else if (rest.startsWith('.h ')) {
|
|
118
|
-
out = 'ret.html ' + transformContent(rest.slice(3));
|
|
119
|
-
} else if (rest.startsWith('.t ')) {
|
|
120
|
-
out = 'ret.text ' + transformContent(rest.slice(3));
|
|
121
|
-
} else if (rest.startsWith('.f ')) {
|
|
122
|
-
out = 'ret.file ' + transformContent(rest.slice(3));
|
|
123
|
-
} else if (rest.startsWith('.v ')) {
|
|
124
|
-
out = 'ret.render ' + transformContent(rest.slice(3));
|
|
125
|
-
} else if (rest.startsWith('.d ')) {
|
|
126
|
-
out = 'ret.download ' + transformContent(rest.slice(3));
|
|
127
|
-
} else {
|
|
128
|
-
out = rest ? 'ret ' + transformContent(rest) : 'ret';
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// ? if
|
|
132
|
-
else if (first === '?' && second !== '.' && second !== '?') {
|
|
133
|
-
out = 'if ' + transformContent(line.slice(1).trim()) + ':';
|
|
134
|
-
}
|
|
135
|
-
// | elif (but not |>)
|
|
136
|
-
else if (first === '|' && second !== '>') {
|
|
137
|
-
out = 'elif ' + transformContent(line.slice(1).trim()) + ':';
|
|
138
|
-
}
|
|
139
|
-
// : else (alone on line)
|
|
140
|
-
else if (line.trim() === ':') {
|
|
141
|
-
out = 'else:';
|
|
142
|
-
}
|
|
143
|
-
// @ loop
|
|
144
|
-
else if (first === '@') {
|
|
145
|
-
const rest = line.slice(1);
|
|
146
|
-
const loopM = rest.match(/^(\w+(?:,\w+)?)<(.+)$/);
|
|
147
|
-
if (loopM) {
|
|
148
|
-
const vars = loopM[1];
|
|
149
|
-
const col = transformContent(loopM[2]);
|
|
150
|
-
if (col.includes('..')) {
|
|
151
|
-
const [start, end] = col.split('..');
|
|
152
|
-
out = `for ${vars} in ${start}..${end}:`;
|
|
153
|
-
} else if (vars.includes(',')) {
|
|
154
|
-
const [k, v] = vars.split(',');
|
|
155
|
-
out = `each ${k}, ${v} in ${col}:`;
|
|
156
|
-
} else {
|
|
157
|
-
out = `each ${vars} in ${col}:`;
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
out = transformContent(line);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// * while
|
|
164
|
-
else if (first === '*') {
|
|
165
|
-
out = 'while ' + transformContent(line.slice(1).trim()) + ':';
|
|
166
|
-
}
|
|
167
|
-
// !!! ensure/finally (before !! and ! checks)
|
|
168
|
-
else if (first === '!' && second === '!' && (line[2] || '') === '!') {
|
|
169
|
-
out = 'ensure:';
|
|
170
|
-
}
|
|
171
|
-
// !! catch (before ! try check)
|
|
172
|
-
else if (first === '!' && second === '!') {
|
|
173
|
-
const rest = line.slice(2).trim();
|
|
174
|
-
out = rest ? `fail ${rest}:` : 'fail:';
|
|
175
|
-
}
|
|
176
|
-
// ! try (alone)
|
|
177
|
-
else if (line.trim() === '!') {
|
|
178
|
-
out = 'try:';
|
|
179
|
-
}
|
|
180
|
-
// $ server
|
|
181
|
-
else if (first === '$') {
|
|
182
|
-
const rest = line.slice(1);
|
|
183
|
-
const srvM = rest.match(/^(\w+):(.+)$/);
|
|
184
|
-
if (srvM) {
|
|
185
|
-
out = `server ${srvM[1]} port ${transformContent(srvM[2].trim())}:`;
|
|
186
|
-
} else {
|
|
187
|
-
out = `server ${rest.trim()}:`;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
// HTTP routes: G P U D
|
|
191
|
-
else if (first === 'G' && second === '"') { out = transformRoute('get', line.slice(1)); }
|
|
192
|
-
else if (first === 'P' && second === '"') { out = transformRoute('post', line.slice(1)); }
|
|
193
|
-
else if (first === 'U' && second === '"') { out = transformRoute('put', line.slice(1)); }
|
|
194
|
-
else if (first === 'D' && second === '"') { out = transformRoute('del', line.slice(1)); }
|
|
195
|
-
else if (first === 'X' && second === '"') { out = transformRoute('patch', line.slice(1)); }
|
|
196
|
-
// ^ model
|
|
197
|
-
else if (first === '^') {
|
|
198
|
-
const rest = line.slice(1).trim();
|
|
199
|
-
const extM = rest.match(/^(\w+)<(\w+)$/);
|
|
200
|
-
if (extM) {
|
|
201
|
-
out = `model ${extM[1]} extends ${extM[2]}:`;
|
|
202
|
-
} else {
|
|
203
|
-
out = `model ${rest}:`;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// < import
|
|
207
|
-
else if (first === '<') {
|
|
208
|
-
const rest = line.slice(1);
|
|
209
|
-
if (rest.startsWith('{')) {
|
|
210
|
-
const impM = rest.match(/^\{([^}]+)\}"(.+)"$/);
|
|
211
|
-
if (impM) {
|
|
212
|
-
out = `use {${impM[1]}} from "${impM[2]}"`;
|
|
213
|
-
} else {
|
|
214
|
-
out = transformContent(line);
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
const fromM = rest.match(/^(\w+)"(.+)"$/);
|
|
218
|
-
if (fromM) {
|
|
219
|
-
out = `use ${fromM[1]} from "${fromM[2]}"`;
|
|
220
|
-
} else {
|
|
221
|
-
out = `use ${rest.trim()}`;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// % match
|
|
226
|
-
else if (first === '%') {
|
|
227
|
-
out = 'match ' + transformContent(line.slice(1).trim()) + ':';
|
|
228
|
-
}
|
|
229
|
-
// + export prefix
|
|
230
|
-
else if (first === '+') {
|
|
231
|
-
const inner = line.slice(1);
|
|
232
|
-
if (inner.startsWith('~f ')) {
|
|
233
|
-
out = transformFunc(inner.slice(3), true, true);
|
|
234
|
-
} else if (inner.startsWith('f ')) {
|
|
235
|
-
out = transformFunc(inner.slice(2), false, true);
|
|
236
|
-
} else {
|
|
237
|
-
const tvM = inner.match(/^([sinblmaj]):(\w+)=(.+)$/);
|
|
238
|
-
if (tvM) {
|
|
239
|
-
out = `pub ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
|
|
240
|
-
} else {
|
|
241
|
-
out = 'pub ' + transformContent(inner);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
// ~f async function
|
|
246
|
-
else if (first === '~' && second === 'f' && line[2] === ' ') {
|
|
247
|
-
out = transformFunc(line.slice(3), true, false);
|
|
248
|
-
}
|
|
249
|
-
// ~TYPE:name=value → mutable
|
|
250
|
-
else if (first === '~' && TYPE_CHARS.has(second) && line[2] === ':') {
|
|
251
|
-
const rest = line.slice(1);
|
|
252
|
-
const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
|
|
253
|
-
if (tvM) {
|
|
254
|
-
out = `mut ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
|
|
255
|
-
} else {
|
|
256
|
-
// Mutable field without assignment: ~s:name
|
|
257
|
-
const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
|
|
258
|
-
if (fieldM) {
|
|
259
|
-
out = `mut ${expandType(fieldM[1])} ${fieldM[2]}`;
|
|
260
|
-
} else {
|
|
261
|
-
out = transformContent(line);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// f function
|
|
266
|
-
else if (first === 'f' && second === ' ') {
|
|
267
|
-
out = transformFunc(line.slice(2), false, false);
|
|
268
|
-
}
|
|
269
|
-
// TYPE:name=value → typed variable
|
|
270
|
-
else if (TYPE_CHARS.has(first) && second === ':') {
|
|
271
|
-
const rest = line;
|
|
272
|
-
const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
|
|
273
|
-
if (tvM) {
|
|
274
|
-
out = `${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
|
|
275
|
-
} else {
|
|
276
|
-
// Could be a type field in model: s:name (no assignment)
|
|
277
|
-
const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
|
|
278
|
-
if (fieldM) {
|
|
279
|
-
out = `${expandType(fieldM[1])} ${fieldM[2]}`;
|
|
280
|
-
} else {
|
|
281
|
-
// type:name=... with complex value
|
|
282
|
-
const fieldDefM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
|
|
283
|
-
if (fieldDefM) {
|
|
284
|
-
out = `${expandType(fieldDefM[1])} ${fieldDefM[2]} = ${transformContent(fieldDefM[3])}`;
|
|
285
|
-
} else {
|
|
286
|
-
out = transformContent(line);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
// Default: apply inline transforms
|
|
292
|
-
else {
|
|
293
|
-
out = transformContent(line);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Handle multi-line output from route transform
|
|
297
|
-
if (out.includes('\n')) {
|
|
298
|
-
const subLines = out.split('\n');
|
|
299
|
-
for (const sl of subLines) {
|
|
300
|
-
result.push(indent + sl);
|
|
301
|
-
}
|
|
302
|
-
} else {
|
|
303
|
-
result.push(indent + out);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return result.join('\n');
|
|
308
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"comments": {
|
|
3
|
-
"lineComment": "--"
|
|
4
|
-
},
|
|
5
|
-
"brackets": [
|
|
6
|
-
["{", "}"],
|
|
7
|
-
["[", "]"],
|
|
8
|
-
["(", ")"]
|
|
9
|
-
],
|
|
10
|
-
"autoClosingPairs": [
|
|
11
|
-
{ "open": "{", "close": "}" },
|
|
12
|
-
{ "open": "[", "close": "]" },
|
|
13
|
-
{ "open": "(", "close": ")" },
|
|
14
|
-
{ "open": "\"", "close": "\"" },
|
|
15
|
-
{ "open": "'", "close": "'" }
|
|
16
|
-
],
|
|
17
|
-
"surroundingPairs": [
|
|
18
|
-
{ "open": "{", "close": "}" },
|
|
19
|
-
{ "open": "[", "close": "]" },
|
|
20
|
-
{ "open": "(", "close": ")" },
|
|
21
|
-
{ "open": "\"", "close": "\"" },
|
|
22
|
-
{ "open": "'", "close": "'" }
|
|
23
|
-
],
|
|
24
|
-
"indentationRules": {
|
|
25
|
-
"increaseIndentPattern": "^\\s*(fn|fn\\.async|if|elif|else|each|for|while|match|try|fail|ensure|server|model|on|schema|crud|auth|env|every|watch|ws|group|upload|queue|job|test|get|post|put|del|patch|error|sse|view|cache|mid|validate|openapi|static|session|cookie|cors|limit)\\b.*:\\s*$",
|
|
26
|
-
"decreaseIndentPattern": "^\\s*(elif|else|fail|ensure)\\b"
|
|
27
|
-
},
|
|
28
|
-
"folding": {
|
|
29
|
-
"offSide": true
|
|
30
|
-
},
|
|
31
|
-
"wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
|
|
32
|
-
}
|
|
Binary file
|