kimchilang 1.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/.github/workflows/ci.yml +66 -0
- package/README.md +1547 -0
- package/create-kimchi-app/README.md +44 -0
- package/create-kimchi-app/index.js +214 -0
- package/create-kimchi-app/package.json +22 -0
- package/editors/README.md +121 -0
- package/editors/sublime/KimchiLang.sublime-syntax +138 -0
- package/editors/vscode/README.md +90 -0
- package/editors/vscode/kimchilang-1.1.0.vsix +0 -0
- package/editors/vscode/language-configuration.json +37 -0
- package/editors/vscode/package.json +55 -0
- package/editors/vscode/src/extension.js +354 -0
- package/editors/vscode/syntaxes/kimchi.tmLanguage.json +215 -0
- package/examples/api/client.km +36 -0
- package/examples/async_pipe.km +58 -0
- package/examples/basic.kimchi +109 -0
- package/examples/cli_framework/README.md +92 -0
- package/examples/cli_framework/calculator.km +61 -0
- package/examples/cli_framework/deploy.km +126 -0
- package/examples/cli_framework/greeter.km +26 -0
- package/examples/config.static +27 -0
- package/examples/config.static.js +10 -0
- package/examples/env_test.km +37 -0
- package/examples/fibonacci.kimchi +17 -0
- package/examples/greeter.km +15 -0
- package/examples/hello.js +1 -0
- package/examples/hello.kimchi +3 -0
- package/examples/js_interop.km +42 -0
- package/examples/logger_example.km +34 -0
- package/examples/memo_fibonacci.km +17 -0
- package/examples/myapp/lib/http.js +14 -0
- package/examples/myapp/lib/http.km +16 -0
- package/examples/myapp/main.km +16 -0
- package/examples/myapp/main_with_mock.km +42 -0
- package/examples/myapp/services/api.js +18 -0
- package/examples/myapp/services/api.km +18 -0
- package/examples/new_features.kimchi +52 -0
- package/examples/project_example.static +20 -0
- package/examples/readme_examples.km +240 -0
- package/examples/reduce_pattern_match.km +85 -0
- package/examples/regex_match.km +46 -0
- package/examples/sample.js +45 -0
- package/examples/sample.km +39 -0
- package/examples/secrets.static +35 -0
- package/examples/secrets.static.js +30 -0
- package/examples/shell-example.mjs +144 -0
- package/examples/shell_example.km +19 -0
- package/examples/stdlib_test.km +22 -0
- package/examples/test_example.km +69 -0
- package/examples/testing/README.md +88 -0
- package/examples/testing/http_client.km +18 -0
- package/examples/testing/math.km +48 -0
- package/examples/testing/math.test.km +93 -0
- package/examples/testing/user_service.km +29 -0
- package/examples/testing/user_service.test.km +72 -0
- package/examples/use-config.mjs +141 -0
- package/examples/use_config.km +13 -0
- package/install.sh +59 -0
- package/package.json +29 -0
- package/pantry/acorn/index.km +1 -0
- package/pantry/is_number/index.km +1 -0
- package/pantry/is_odd/index.km +2 -0
- package/project.static +6 -0
- package/src/cli.js +1245 -0
- package/src/generator.js +1241 -0
- package/src/index.js +141 -0
- package/src/js2km.js +568 -0
- package/src/lexer.js +822 -0
- package/src/linter.js +810 -0
- package/src/package-manager.js +307 -0
- package/src/parser.js +1876 -0
- package/src/static-parser.js +500 -0
- package/src/typechecker.js +950 -0
- package/stdlib/array.km +0 -0
- package/stdlib/bitwise.km +38 -0
- package/stdlib/console.km +49 -0
- package/stdlib/date.km +97 -0
- package/stdlib/function.km +44 -0
- package/stdlib/http.km +197 -0
- package/stdlib/http.md +333 -0
- package/stdlib/index.km +26 -0
- package/stdlib/json.km +17 -0
- package/stdlib/logger.js +114 -0
- package/stdlib/logger.km +104 -0
- package/stdlib/math.km +120 -0
- package/stdlib/object.km +41 -0
- package/stdlib/promise.km +33 -0
- package/stdlib/string.km +93 -0
- package/stdlib/testing.md +265 -0
- package/test/test.js +599 -0
package/stdlib/http.md
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# HTTP Module
|
|
2
|
+
|
|
3
|
+
A promise-based HTTP client wrapper for Node.js http/https modules.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```kimchi
|
|
8
|
+
as http dep stdlib.http
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Functions
|
|
12
|
+
|
|
13
|
+
### `get(url, options)`
|
|
14
|
+
|
|
15
|
+
Make an HTTP GET request.
|
|
16
|
+
|
|
17
|
+
```kimchi
|
|
18
|
+
dec response = await http.get("https://api.example.com/users")
|
|
19
|
+
print response.body
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Parameters:**
|
|
23
|
+
- `url` - The URL to request
|
|
24
|
+
- `options` - Optional request options (headers, timeout)
|
|
25
|
+
|
|
26
|
+
**Returns:** Response object
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### `post(url, body, options)`
|
|
31
|
+
|
|
32
|
+
Make an HTTP POST request.
|
|
33
|
+
|
|
34
|
+
```kimchi
|
|
35
|
+
dec response = await http.post("https://api.example.com/users", {
|
|
36
|
+
name: "Alice",
|
|
37
|
+
email: "alice@example.com"
|
|
38
|
+
})
|
|
39
|
+
print response.body
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Parameters:**
|
|
43
|
+
- `url` - The URL to request
|
|
44
|
+
- `body` - Request body (automatically JSON stringified if object)
|
|
45
|
+
- `options` - Optional request options
|
|
46
|
+
|
|
47
|
+
**Returns:** Response object
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### `put(url, body, options)`
|
|
52
|
+
|
|
53
|
+
Make an HTTP PUT request.
|
|
54
|
+
|
|
55
|
+
```kimchi
|
|
56
|
+
dec response = await http.put("https://api.example.com/users/123", {
|
|
57
|
+
name: "Alice Updated"
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Parameters:**
|
|
62
|
+
- `url` - The URL to request
|
|
63
|
+
- `body` - Request body
|
|
64
|
+
- `options` - Optional request options
|
|
65
|
+
|
|
66
|
+
**Returns:** Response object
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### `patch(url, body, options)`
|
|
71
|
+
|
|
72
|
+
Make an HTTP PATCH request.
|
|
73
|
+
|
|
74
|
+
```kimchi
|
|
75
|
+
dec response = await http.patch("https://api.example.com/users/123", {
|
|
76
|
+
email: "newemail@example.com"
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Parameters:**
|
|
81
|
+
- `url` - The URL to request
|
|
82
|
+
- `body` - Request body
|
|
83
|
+
- `options` - Optional request options
|
|
84
|
+
|
|
85
|
+
**Returns:** Response object
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### `del(url, options)`
|
|
90
|
+
|
|
91
|
+
Make an HTTP DELETE request.
|
|
92
|
+
|
|
93
|
+
```kimchi
|
|
94
|
+
dec response = await http.del("https://api.example.com/users/123")
|
|
95
|
+
if response.ok {
|
|
96
|
+
print "User deleted"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Parameters:**
|
|
101
|
+
- `url` - The URL to request
|
|
102
|
+
- `options` - Optional request options
|
|
103
|
+
|
|
104
|
+
**Returns:** Response object
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### `request(url, options)`
|
|
109
|
+
|
|
110
|
+
Generic HTTP request function. All other methods use this internally.
|
|
111
|
+
|
|
112
|
+
```kimchi
|
|
113
|
+
dec response = await http.request("https://api.example.com/data", {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
"Authorization": "Bearer token123"
|
|
118
|
+
},
|
|
119
|
+
body: { key: "value" },
|
|
120
|
+
timeout: 5000
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Parameters:**
|
|
125
|
+
- `url` - The URL to request
|
|
126
|
+
- `options` - Request options:
|
|
127
|
+
- `method` - HTTP method (GET, POST, PUT, PATCH, DELETE)
|
|
128
|
+
- `headers` - Request headers object
|
|
129
|
+
- `body` - Request body
|
|
130
|
+
- `timeout` - Request timeout in milliseconds (default: 30000)
|
|
131
|
+
|
|
132
|
+
**Returns:** Response object
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### `queryString(params)`
|
|
137
|
+
|
|
138
|
+
Build a URL query string from an object.
|
|
139
|
+
|
|
140
|
+
```kimchi
|
|
141
|
+
dec qs = http.queryString({ page: 1, limit: 10, search: "hello world" })
|
|
142
|
+
// Returns: "page=1&limit=10&search=hello%20world"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Parameters:**
|
|
146
|
+
- `params` - Object with key-value pairs
|
|
147
|
+
|
|
148
|
+
**Returns:** URL-encoded query string
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### `buildUrl(baseUrl, params)`
|
|
153
|
+
|
|
154
|
+
Build a complete URL with query parameters.
|
|
155
|
+
|
|
156
|
+
```kimchi
|
|
157
|
+
dec url = http.buildUrl("https://api.example.com/search", {
|
|
158
|
+
q: "kimchi",
|
|
159
|
+
page: 1
|
|
160
|
+
})
|
|
161
|
+
// Returns: "https://api.example.com/search?q=kimchi&page=1"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Parameters:**
|
|
165
|
+
- `baseUrl` - Base URL
|
|
166
|
+
- `params` - Query parameters object
|
|
167
|
+
|
|
168
|
+
**Returns:** Complete URL with query string
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `createClient(baseOptions)`
|
|
173
|
+
|
|
174
|
+
Create a reusable HTTP client with default options.
|
|
175
|
+
|
|
176
|
+
```kimchi
|
|
177
|
+
dec api = http.createClient({
|
|
178
|
+
baseUrl: "https://api.example.com",
|
|
179
|
+
headers: {
|
|
180
|
+
"Authorization": "Bearer token123",
|
|
181
|
+
"Accept": "application/json"
|
|
182
|
+
},
|
|
183
|
+
timeout: 10000
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
// Now use the client - paths are relative to baseUrl
|
|
187
|
+
dec users = await api.get("/users")
|
|
188
|
+
dec user = await api.post("/users", { name: "Bob" })
|
|
189
|
+
dec updated = await api.put("/users/123", { name: "Bob Updated" })
|
|
190
|
+
dec patched = await api.patch("/users/123", { email: "bob@new.com" })
|
|
191
|
+
dec deleted = await api.del("/users/123")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Parameters:**
|
|
195
|
+
- `baseOptions` - Client configuration:
|
|
196
|
+
- `baseUrl` - Base URL prepended to all requests
|
|
197
|
+
- `headers` - Default headers for all requests
|
|
198
|
+
- `timeout` - Default timeout for all requests
|
|
199
|
+
|
|
200
|
+
**Returns:** Client object with `get`, `post`, `put`, `patch`, `del` methods
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Response Object
|
|
205
|
+
|
|
206
|
+
All request functions return a response object with the following properties:
|
|
207
|
+
|
|
208
|
+
```kimchi
|
|
209
|
+
{
|
|
210
|
+
status: 200, // HTTP status code
|
|
211
|
+
statusText: "OK", // HTTP status message
|
|
212
|
+
headers: {...}, // Response headers
|
|
213
|
+
body: {...}, // Response body (auto-parsed if JSON)
|
|
214
|
+
ok: true // true if status is 200-299
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Checking Response Status
|
|
219
|
+
|
|
220
|
+
```kimchi
|
|
221
|
+
dec response = await http.get("https://api.example.com/users")
|
|
222
|
+
|
|
223
|
+
if response.ok {
|
|
224
|
+
print "Success: ${response.body}"
|
|
225
|
+
} else {
|
|
226
|
+
print "Error ${response.status}: ${response.statusText}"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Examples
|
|
233
|
+
|
|
234
|
+
### Simple GET Request
|
|
235
|
+
|
|
236
|
+
```kimchi
|
|
237
|
+
as http dep stdlib.http
|
|
238
|
+
|
|
239
|
+
dec response = await http.get("https://jsonplaceholder.typicode.com/posts/1")
|
|
240
|
+
print "Title: ${response.body.title}"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### POST with JSON Body
|
|
244
|
+
|
|
245
|
+
```kimchi
|
|
246
|
+
as http dep stdlib.http
|
|
247
|
+
|
|
248
|
+
dec response = await http.post("https://jsonplaceholder.typicode.com/posts", {
|
|
249
|
+
title: "My Post",
|
|
250
|
+
body: "This is the content",
|
|
251
|
+
userId: 1
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
print "Created post with ID: ${response.body.id}"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Using Custom Headers
|
|
258
|
+
|
|
259
|
+
```kimchi
|
|
260
|
+
as http dep stdlib.http
|
|
261
|
+
|
|
262
|
+
dec response = await http.get("https://api.example.com/protected", {
|
|
263
|
+
headers: {
|
|
264
|
+
"Authorization": "Bearer my-token",
|
|
265
|
+
"X-Custom-Header": "custom-value"
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Creating an API Client
|
|
271
|
+
|
|
272
|
+
```kimchi
|
|
273
|
+
as http dep stdlib.http
|
|
274
|
+
|
|
275
|
+
// Create a client for a specific API
|
|
276
|
+
dec github = http.createClient({
|
|
277
|
+
baseUrl: "https://api.github.com",
|
|
278
|
+
headers: {
|
|
279
|
+
"Accept": "application/vnd.github.v3+json",
|
|
280
|
+
"Authorization": "token ghp_xxxxxxxxxxxx"
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// Fetch user info
|
|
285
|
+
dec user = await github.get("/user")
|
|
286
|
+
print "Logged in as: ${user.body.login}"
|
|
287
|
+
|
|
288
|
+
// List repositories
|
|
289
|
+
dec repos = await github.get("/user/repos")
|
|
290
|
+
for repo in repos.body {
|
|
291
|
+
print "- ${repo.name}"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Error Handling
|
|
296
|
+
|
|
297
|
+
```kimchi
|
|
298
|
+
as http dep stdlib.http
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
dec response = await http.get("https://api.example.com/users/999")
|
|
302
|
+
|
|
303
|
+
|response.status == 404| => {
|
|
304
|
+
print "User not found"
|
|
305
|
+
}
|
|
306
|
+
|response.status == 401| => {
|
|
307
|
+
print "Unauthorized - please log in"
|
|
308
|
+
}
|
|
309
|
+
|response.ok| => {
|
|
310
|
+
print "User: ${response.body.name}"
|
|
311
|
+
}
|
|
312
|
+
|true| => {
|
|
313
|
+
print "Unexpected error: ${response.status}"
|
|
314
|
+
}
|
|
315
|
+
} catch(e) {
|
|
316
|
+
print "Network error: ${e.message}"
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Building URLs with Query Parameters
|
|
321
|
+
|
|
322
|
+
```kimchi
|
|
323
|
+
as http dep stdlib.http
|
|
324
|
+
|
|
325
|
+
dec url = http.buildUrl("https://api.example.com/search", {
|
|
326
|
+
q: "kimchi lang",
|
|
327
|
+
page: 1,
|
|
328
|
+
limit: 20
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
dec results = await http.get(url)
|
|
332
|
+
print "Found ${results.body.total} results"
|
|
333
|
+
```
|
package/stdlib/index.km
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// KimchiLang Standard Library
|
|
2
|
+
// Main entry point
|
|
3
|
+
|
|
4
|
+
expose fn _describe() {
|
|
5
|
+
return "KimchiLang Standard Library - functional utilities for arrays, strings, objects, math, and more"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
expose fn _help() {
|
|
9
|
+
return "
|
|
10
|
+
Available modules:
|
|
11
|
+
stdlib.array - Array utilities (map, filter, reduce, etc.)
|
|
12
|
+
stdlib.string - String utilities (split, trim, replace, etc.)
|
|
13
|
+
stdlib.object - Object utilities (keys, values, entries, etc.)
|
|
14
|
+
stdlib.math - Math utilities (abs, round, trig, etc.)
|
|
15
|
+
stdlib.json - JSON utilities (parse, stringify, pretty)
|
|
16
|
+
stdlib.console - Console/IO utilities (log, error, table, etc.)
|
|
17
|
+
stdlib.promise - Promise utilities (all, race, delay, etc.)
|
|
18
|
+
stdlib.date - Date/time utilities (now, format, diff, etc.)
|
|
19
|
+
stdlib.function - Function utilities (compose, pipe, etc.)
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
as arr dep stdlib.array
|
|
23
|
+
as str dep stdlib.string
|
|
24
|
+
as math dep stdlib.math
|
|
25
|
+
"
|
|
26
|
+
}
|
package/stdlib/json.km
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// KimchiLang Standard Library - JSON Functions
|
|
2
|
+
|
|
3
|
+
expose fn _describe() {
|
|
4
|
+
return "JSON utilities: parse, stringify, pretty print"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
expose fn parse(str) {
|
|
8
|
+
return JSON.parse(str)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
expose fn stringify(obj) {
|
|
12
|
+
return JSON.stringify(obj)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
expose fn pretty(obj, indent) {
|
|
16
|
+
return JSON.stringify(obj, null, indent || 2)
|
|
17
|
+
}
|
package/stdlib/logger.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// KimchiLang Logger Module
|
|
2
|
+
// Provides structured JSON logging with log levels
|
|
3
|
+
|
|
4
|
+
const LOG_LEVELS = {
|
|
5
|
+
debug: 0,
|
|
6
|
+
info: 1,
|
|
7
|
+
warn: 2,
|
|
8
|
+
error: 3,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function getLogLevel() {
|
|
12
|
+
const level = (process.env.LOG_LEVEL || 'info').toLowerCase();
|
|
13
|
+
return LOG_LEVELS[level] ?? LOG_LEVELS.info;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getCallerInfo() {
|
|
17
|
+
const err = new Error();
|
|
18
|
+
const stack = err.stack.split('\n');
|
|
19
|
+
|
|
20
|
+
// Find the first stack frame outside of logger.js
|
|
21
|
+
for (let i = 2; i < stack.length; i++) {
|
|
22
|
+
const line = stack[i];
|
|
23
|
+
if (!line.includes('logger.js') && !line.includes('node:internal')) {
|
|
24
|
+
// Parse stack frame: " at functionName (file:line:col)" or " at file:line:col"
|
|
25
|
+
const match = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
26
|
+
if (match) {
|
|
27
|
+
const functionName = match[1] || '<anonymous>';
|
|
28
|
+
const filePath = match[2];
|
|
29
|
+
const lineNumber = parseInt(match[3], 10);
|
|
30
|
+
|
|
31
|
+
// Extract module path from file path
|
|
32
|
+
const modulePath = filePath
|
|
33
|
+
.replace(/^file:\/\//, '')
|
|
34
|
+
.replace(/.*\//, '')
|
|
35
|
+
.replace(/\.(js|km)$/, '');
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
function: functionName,
|
|
39
|
+
module: modulePath,
|
|
40
|
+
line: lineNumber,
|
|
41
|
+
file: filePath,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
function: '<unknown>',
|
|
49
|
+
module: '<unknown>',
|
|
50
|
+
line: 0,
|
|
51
|
+
file: '<unknown>',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function formatLog(level, message, data = {}) {
|
|
56
|
+
const caller = getCallerInfo();
|
|
57
|
+
const timestamp = new Date().toISOString();
|
|
58
|
+
|
|
59
|
+
const logEntry = {
|
|
60
|
+
timestamp,
|
|
61
|
+
level,
|
|
62
|
+
module: caller.module,
|
|
63
|
+
function: caller.function,
|
|
64
|
+
line: caller.line,
|
|
65
|
+
message,
|
|
66
|
+
...data,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return JSON.stringify(logEntry);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function shouldLog(level) {
|
|
73
|
+
const currentLevel = getLogLevel();
|
|
74
|
+
return LOG_LEVELS[level] >= currentLevel;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const logger = {
|
|
78
|
+
debug(message, data) {
|
|
79
|
+
if (shouldLog('debug')) {
|
|
80
|
+
console.log(formatLog('debug', message, data));
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
info(message, data) {
|
|
85
|
+
if (shouldLog('info')) {
|
|
86
|
+
console.log(formatLog('info', message, data));
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
warn(message, data) {
|
|
91
|
+
if (shouldLog('warn')) {
|
|
92
|
+
console.warn(formatLog('warn', message, data));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
error(message, data) {
|
|
97
|
+
if (shouldLog('error')) {
|
|
98
|
+
console.error(formatLog('error', message, data));
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Create a child logger with additional context
|
|
103
|
+
child(context) {
|
|
104
|
+
return {
|
|
105
|
+
debug: (message, data) => logger.debug(message, { ...context, ...data }),
|
|
106
|
+
info: (message, data) => logger.info(message, { ...context, ...data }),
|
|
107
|
+
warn: (message, data) => logger.warn(message, { ...context, ...data }),
|
|
108
|
+
error: (message, data) => logger.error(message, { ...context, ...data }),
|
|
109
|
+
child: (moreContext) => logger.child({ ...context, ...moreContext }),
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default logger;
|
package/stdlib/logger.km
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// KimchiLang Logger
|
|
2
|
+
// Usage: as log dep stdlib.logger
|
|
3
|
+
// Then: log.info("message"), log.debug("message"), etc.
|
|
4
|
+
// Set LOG_LEVEL environment variable to control output (debug, info, warn, error)
|
|
5
|
+
|
|
6
|
+
env LOG_LEVEL = "info"
|
|
7
|
+
|
|
8
|
+
dec LOG_LEVELS = {
|
|
9
|
+
debug: 0,
|
|
10
|
+
info: 1,
|
|
11
|
+
warn: 2,
|
|
12
|
+
error: 3
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn getLogLevel() {
|
|
16
|
+
dec level = LOG_LEVEL.toLowerCase()
|
|
17
|
+
dec numLevel = LOG_LEVELS[level]
|
|
18
|
+
|numLevel == null| => return 1
|
|
19
|
+
return numLevel
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn shouldLog(level) {
|
|
23
|
+
return LOG_LEVELS[level] >= getLogLevel()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fn getCallerInfo() {
|
|
27
|
+
return js {
|
|
28
|
+
const err = new Error();
|
|
29
|
+
const stack = err.stack.split('\n');
|
|
30
|
+
for (let i = 3; i < stack.length; i++) {
|
|
31
|
+
const line = stack[i];
|
|
32
|
+
if (!line.includes('logger') && !line.includes('node:internal')) {
|
|
33
|
+
const match = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
34
|
+
if (match) {
|
|
35
|
+
return {
|
|
36
|
+
fn: match[1] || '<anonymous>',
|
|
37
|
+
module: match[2].replace(/^file:\/\//, '').replace(/.*\//, '').replace(/\.(js|km)$/, ''),
|
|
38
|
+
line: parseInt(match[3], 10),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { fn: '<unknown>', module: '<unknown>', line: 0 };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn formatLog(level, message, data) {
|
|
48
|
+
dec caller = getCallerInfo()
|
|
49
|
+
dec timestamp = js { return new Date().toISOString(); }
|
|
50
|
+
|
|
51
|
+
dec entry = {
|
|
52
|
+
timestamp: timestamp,
|
|
53
|
+
level: level,
|
|
54
|
+
module: caller.module,
|
|
55
|
+
function: caller.fn,
|
|
56
|
+
line: caller.line,
|
|
57
|
+
message: message
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|data != null| => {
|
|
61
|
+
js(entry, data) {
|
|
62
|
+
Object.assign(entry, data);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return js(entry) { return JSON.stringify(entry); }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expose fn debug(message, data) {
|
|
70
|
+
|shouldLog("debug")| => {
|
|
71
|
+
dec output = formatLog("debug", message, data)
|
|
72
|
+
js(output) { console.log(output); }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expose fn info(message, data) {
|
|
77
|
+
|shouldLog("info")| => {
|
|
78
|
+
dec output = formatLog("info", message, data)
|
|
79
|
+
js(output) { console.log(output); }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
expose fn warn(message, data) {
|
|
84
|
+
|shouldLog("warn")| => {
|
|
85
|
+
dec output = formatLog("warn", message, data)
|
|
86
|
+
js(output) { console.warn(output); }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
expose fn error(message, data) {
|
|
91
|
+
|shouldLog("error")| => {
|
|
92
|
+
dec output = formatLog("error", message, data)
|
|
93
|
+
js(output) { console.error(output); }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
expose fn child(context) {
|
|
98
|
+
return {
|
|
99
|
+
debug: (msg, data) => debug(msg, { ...context, ...data }),
|
|
100
|
+
info: (msg, data) => info(msg, { ...context, ...data }),
|
|
101
|
+
warn: (msg, data) => warn(msg, { ...context, ...data }),
|
|
102
|
+
error: (msg, data) => error(msg, { ...context, ...data })
|
|
103
|
+
}
|
|
104
|
+
}
|
package/stdlib/math.km
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// KimchiLang Standard Library - Math Functions
|
|
2
|
+
|
|
3
|
+
expose fn _describe() {
|
|
4
|
+
return "Math utilities: abs, round, floor, ceil, trigonometry, etc."
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
expose dec PI = Math.PI
|
|
8
|
+
expose dec E = Math.E
|
|
9
|
+
|
|
10
|
+
expose fn abs(x) {
|
|
11
|
+
return Math.abs(x)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
expose fn sign(x) {
|
|
15
|
+
return Math.sign(x)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
expose fn round(x) {
|
|
19
|
+
return Math.round(x)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
expose fn floor(x) {
|
|
23
|
+
return Math.floor(x)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
expose fn ceil(x) {
|
|
27
|
+
return Math.ceil(x)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
expose fn trunc(x) {
|
|
31
|
+
return Math.trunc(x)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
expose fn pow(base, exponent) {
|
|
35
|
+
return Math.pow(base, exponent)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
expose fn sqrt(x) {
|
|
39
|
+
return Math.sqrt(x)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
expose fn cbrt(x) {
|
|
43
|
+
return Math.cbrt(x)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
expose fn exp(x) {
|
|
47
|
+
return Math.exp(x)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expose fn log(x) {
|
|
51
|
+
return Math.log(x)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expose fn log10(x) {
|
|
55
|
+
return Math.log10(x)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
expose fn log2(x) {
|
|
59
|
+
return Math.log2(x)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
expose fn sin(x) {
|
|
63
|
+
return Math.sin(x)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expose fn cos(x) {
|
|
67
|
+
return Math.cos(x)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
expose fn tan(x) {
|
|
71
|
+
return Math.tan(x)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
expose fn asin(x) {
|
|
75
|
+
return Math.asin(x)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
expose fn acos(x) {
|
|
79
|
+
return Math.acos(x)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
expose fn atan(x) {
|
|
83
|
+
return Math.atan(x)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
expose fn atan2(y, x) {
|
|
87
|
+
return Math.atan2(y, x)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
expose fn random() {
|
|
91
|
+
return Math.random()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
expose fn randomInt(minVal, maxVal) {
|
|
95
|
+
return Math.floor(Math.random() * (maxVal - minVal + 1)) + minVal
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
expose fn clamp(value, minVal, maxVal) {
|
|
99
|
+
return Math.min(Math.max(value, minVal), maxVal)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
expose fn lerp(start, end, t) {
|
|
103
|
+
return start + (end - start) * t
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
expose fn degrees(radians) {
|
|
107
|
+
return radians * 180 / Math.PI
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
expose fn radians(deg) {
|
|
111
|
+
return deg * Math.PI / 180
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expose fn isEven(n) {
|
|
115
|
+
return n % 2 == 0
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
expose fn isOdd(n) {
|
|
119
|
+
return n % 2 != 0
|
|
120
|
+
}
|