@shd101wyy/yo 0.0.31 → 0.0.33

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shd101wyy/yo",
3
3
  "displayName": "Yo",
4
- "version": "0.0.31",
4
+ "version": "0.0.33",
5
5
  "main": "./out/cjs/index.cjs",
6
6
  "module": "./out/esm/index.mjs",
7
7
  "types": "./out/types/src/index.d.ts",
package/std/build.yo CHANGED
@@ -6,7 +6,7 @@
6
6
  //
7
7
  // Usage in build.yo:
8
8
  // build :: import "std/build";
9
- // build.project({ name: "my-app", root: "./src/lib.yo" });
9
+ // mod :: build.module({ name: "my-app", root: "./src/lib.yo" });
10
10
  // exe :: build.executable({ name: "my-app", root: "./src/main.yo" });
11
11
  // install :: build.step("install", "Build all artifacts");
12
12
  // install.depend_on(exe);
@@ -61,12 +61,6 @@ export target_host;
61
61
  // Usage: build.executable({ name: "app", root: "./src/main.yo" })
62
62
  // Unspecified fields get their default values.
63
63
 
64
- Project :: struct(
65
- name : comptime_string,
66
- (root : comptime_string) ?= "./src/lib.yo"
67
- );
68
- export Project;
69
-
70
64
  Executable :: struct(
71
65
  name : comptime_string,
72
66
  root : comptime_string,
@@ -100,6 +94,26 @@ TestSuite :: struct(
100
94
  );
101
95
  export TestSuite;
102
96
 
97
+ // ── Build module type ────────────────────────────────────────────────
98
+ // Represents a named source module that declares system library requirements.
99
+ // Modules are the unit of reuse across Yo dependencies.
100
+
101
+ BuildModule :: struct(
102
+ name : comptime_string,
103
+ root : comptime_string,
104
+ _dep : comptime_string // dependency name, empty for local modules
105
+ );
106
+ export BuildModule;
107
+
108
+ // ── Import entry type ────────────────────────────────────────────────
109
+ // Pairs an import name with a module for use with add_import/add_import_list.
110
+
111
+ ImportEntry :: struct(
112
+ name : comptime_string,
113
+ module : BuildModule
114
+ );
115
+ export ImportEntry;
116
+
103
117
  // ── Step type ────────────────────────────────────────────────────────
104
118
  // Returned by all build functions. Used to wire dependencies between steps.
105
119
 
@@ -118,6 +132,17 @@ impl(Step,
118
132
  }),
119
133
  link : (fn(comptime(self) : Self, comptime(library) : Step) -> comptime(unit))({
120
134
  __yo_build_link(self.name, library.name);
135
+ }),
136
+ add_import : (fn(comptime(self) : Self, comptime(entry) : ImportEntry) -> comptime(unit))({
137
+ __yo_build_add_import(self.name, entry.name, entry.module.name, entry.module._dep);
138
+ })
139
+ );
140
+
141
+ // ── Build module methods ─────────────────────────────────────────────
142
+
143
+ impl(BuildModule,
144
+ link : (fn(comptime(self) : Self, comptime(library) : Step) -> comptime(unit))({
145
+ __yo_build_module_link(self.name, library.name);
121
146
  })
122
147
  );
123
148
 
@@ -150,10 +175,10 @@ export PathDependency;
150
175
 
151
176
  SystemLibrary :: struct(
152
177
  name : comptime_string,
153
- pkg_config : comptime_string,
154
178
  (fallback_include : comptime_string) ?= "",
155
179
  (fallback_lib : comptime_string) ?= "",
156
- (fallback_link : comptime_string) ?= ""
180
+ (fallback_link : comptime_string) ?= "",
181
+ (defines : comptime_string) ?= ""
157
182
  );
158
183
  export SystemLibrary;
159
184
 
@@ -172,6 +197,12 @@ impl(Dependency,
172
197
  artifact : (fn(comptime(self) : Self, comptime(artifact_name) : comptime_string) -> comptime(Step))({
173
198
  __yo_build_dep_artifact(self.name, artifact_name);
174
199
  Step(name: artifact_name, kind: StepKind.StaticLibrary)
200
+ }),
201
+ // Get a module from the dependency's build.yo.
202
+ // Empty module_name defaults to the sole module if exactly one exists.
203
+ module : (fn(comptime(self) : Self, comptime(module_name) : comptime_string) -> comptime(BuildModule))({
204
+ _encoded :: __yo_build_dep_module(self.name, module_name);
205
+ BuildModule(name: module_name, root: "", _dep: self.name)
175
206
  })
176
207
  );
177
208
 
@@ -179,12 +210,6 @@ impl(Dependency,
179
210
  // All registration functions return a Step value, allowing steps to be
180
211
  // wired together as dependencies via step.depend_on(dep).
181
212
 
182
- // Register project metadata.
183
- project :: (fn(comptime(config) : Project) -> comptime(unit)) {
184
- __yo_build_project(config.name, config.root);
185
- };
186
- export project;
187
-
188
213
  // Register an executable artifact. Returns a Step for dependency wiring.
189
214
  executable :: (fn(comptime(config) : Executable) -> comptime(Step)) {
190
215
  opt_str :: match(config.optimize,
@@ -273,11 +298,27 @@ export path_dependency;
273
298
 
274
299
  // Register a system C library discovered via pkg-config. Returns a Step for linking.
275
300
  system_library :: (fn(comptime(config) : SystemLibrary) -> comptime(Step)) {
276
- __yo_build_system_library(config.name, config.pkg_config, config.fallback_include, config.fallback_lib, config.fallback_link);
301
+ __yo_build_system_library(config.name, config.fallback_include, config.fallback_lib, config.fallback_link, config.defines);
277
302
  Step(name: config.name, kind: StepKind.SystemLibrary)
278
303
  };
279
304
  export system_library;
280
305
 
306
+ // ── Module config type ───────────────────────────────────────────────
307
+
308
+ ModuleConfig :: struct(
309
+ name : comptime_string,
310
+ root : comptime_string
311
+ );
312
+ export ModuleConfig;
313
+
314
+ // Register a named module. Returns a BuildModule for linking system libraries
315
+ // and importing into executables/libraries.
316
+ _module :: (fn(comptime(config) : ModuleConfig) -> comptime(BuildModule)) {
317
+ __yo_build_module(config.name, config.root);
318
+ BuildModule(name: config.name, root: config.root, _dep: "")
319
+ };
320
+ export module : _module;
321
+
281
322
  // Declare a user-configurable build option.
282
323
  // Returns the option value (from CLI -Dname=value, or the default).
283
324
  // Usage: strip :: build.option({ name: "strip", description: "Strip debug symbols", default: "false" });
@@ -1,7 +1,12 @@
1
- // std/toml/toml.yo - Basic TOML parser
1
+ // std/encoding/toml.yo - Basic TOML parser
2
2
  //
3
3
  // Parses a subset of TOML into a TomlValue tree.
4
4
  // Supports: strings, integers, booleans, table sections, comments.
5
+ //
6
+ // Example:
7
+ // { toml_parse, TomlValue } :: import "std/encoding/toml";
8
+ // // Or via the encoding index:
9
+ // { toml_parse, TomlValue } :: import "std/encoding";
5
10
 
6
11
  open import "../string";
7
12
  { ArrayList } :: import "../collections/array_list";
@@ -0,0 +1,359 @@
1
+ // std/http/client.yo - Async HTTP client using TCP
2
+ //
3
+ // Provides an async HTTP client and a high-level `fetch` function
4
+ // similar to JavaScript's fetch API.
5
+ //
6
+ // Example:
7
+ // { fetch, HttpResponse } :: import "std/http";
8
+ // { Exception } :: import "std/error";
9
+ //
10
+ // main :: (fn(using(io : IO)) -> unit)({
11
+ // given(exn) := Exception(throw : ((err) -> {
12
+ // println(err.message());
13
+ // escape ();
14
+ // }));
15
+ // resp := io.await(fetch(`http://example.com/api/data`, using(io)));
16
+ // println(resp.body);
17
+ // });
18
+
19
+ open import "../string";
20
+ { ArrayList } :: import "../collections/array_list";
21
+ { GlobalAllocator } :: import "../allocator";
22
+ { malloc, free } :: GlobalAllocator;
23
+ open import "../fmt";
24
+ { Error, AnyError, Exception } :: import "../error";
25
+ { TcpStream } :: import "../net/tcp";
26
+ { IpAddr, SocketAddr } :: import "../net/addr";
27
+ { lookup_host } :: import "../net/dns";
28
+ { Url, UrlError } :: import "../url";
29
+ {
30
+ HttpMethod, HttpHeader, HttpRequest, HttpResponse,
31
+ parse_response, http_status_text
32
+ } :: import "./http.yo";
33
+
34
+ // ============================================================================
35
+ // HttpError
36
+ // ============================================================================
37
+
38
+ HttpError :: enum(
39
+ ConnectionFailed(msg: String),
40
+ InvalidUrl(msg: String),
41
+ Timeout,
42
+ TooManyRedirects,
43
+ UnsupportedScheme(scheme: String),
44
+ ResponseTooLarge,
45
+ Other(msg: String)
46
+ );
47
+
48
+ impl(HttpError, ToString(
49
+ to_string : (self ->
50
+ match(self,
51
+ .ConnectionFailed(msg) => `HTTP connection failed: ${msg}`,
52
+ .InvalidUrl(msg) => `Invalid URL: ${msg}`,
53
+ .Timeout => `HTTP request timed out`,
54
+ .TooManyRedirects => `Too many redirects`,
55
+ .UnsupportedScheme(scheme) => `Unsupported scheme: ${scheme}`,
56
+ .ResponseTooLarge => `Response too large`,
57
+ .Other(msg) => `HTTP error: ${msg}`
58
+ )
59
+ )
60
+ ));
61
+
62
+ impl(HttpError, Error());
63
+
64
+ export HttpError;
65
+
66
+ // ============================================================================
67
+ // FetchOptions
68
+ // ============================================================================
69
+
70
+ FetchOptions :: object(
71
+ method : HttpMethod,
72
+ headers : ArrayList(HttpHeader),
73
+ body : String
74
+ );
75
+
76
+ impl(FetchOptions,
77
+ new : (fn() -> Self)(
78
+ Self(
79
+ method: .GET,
80
+ headers: ArrayList(HttpHeader).new(),
81
+ body: ``
82
+ )
83
+ ),
84
+
85
+ with_method : (fn(self: Self, method: HttpMethod) -> Self)({
86
+ self.method = method;
87
+ self
88
+ }),
89
+
90
+ with_header : (fn(self: Self, name: String, value: String) -> Self)({
91
+ self.headers.push(HttpHeader.new(name, value));
92
+ self
93
+ }),
94
+
95
+ with_body : (fn(self: Self, body: String) -> Self)({
96
+ self.body = body;
97
+ self
98
+ })
99
+ );
100
+
101
+ export FetchOptions;
102
+
103
+ // ============================================================================
104
+ // Internal helpers
105
+ // ============================================================================
106
+
107
+ // Find the position of \r\n\r\n (end of HTTP headers)
108
+ _find_header_end :: (fn(data: ArrayList(u8)) -> usize)({
109
+ len := data.len();
110
+ cond(
111
+ (len < usize(4)) => { return usize(0); },
112
+ true => ()
113
+ );
114
+ i := usize(0);
115
+ limit := (len - usize(3));
116
+ while runtime((i < limit)), {
117
+ cond(
118
+ ((data.get(i).unwrap() == u8(13)) &&
119
+ ((data.get((i + usize(1))).unwrap() == u8(10)) &&
120
+ ((data.get((i + usize(2))).unwrap() == u8(13)) &&
121
+ (data.get((i + usize(3))).unwrap() == u8(10))))) => {
122
+ return i;
123
+ },
124
+ true => ()
125
+ );
126
+ i = (i + usize(1));
127
+ };
128
+ usize(0)
129
+ });
130
+
131
+ // Parse Content-Length from headers; returns -1 if not found.
132
+ _find_content_length :: (fn(data: ArrayList(u8), header_end: usize) -> i32)({
133
+ // Build header string up to header_end
134
+ header_bytes := ArrayList(u8).new();
135
+ i := usize(0);
136
+ while runtime((i < header_end)), {
137
+ header_bytes.push(data.get(i).unwrap());
138
+ i = (i + usize(1));
139
+ };
140
+ header_str := String.from_bytes(header_bytes).to_lowercase();
141
+ match(header_str.index_of(`content-length:`),
142
+ .Some(pos) => {
143
+ val_start := (pos + usize(16));
144
+ // Find end of value by looking for \r\n after val_start
145
+ rest := header_str.substring(val_start, header_str.len());
146
+ match(rest.index_of(`\r\n`),
147
+ .Some(eol) => {
148
+ val_str := rest.substring(usize(0), eol).trim();
149
+ match(val_str.parse_i32(),
150
+ .Some(n) => n,
151
+ .None => i32(-1)
152
+ )
153
+ },
154
+ .None => {
155
+ val_str := rest.trim();
156
+ match(val_str.parse_i32(),
157
+ .Some(n) => n,
158
+ .None => i32(-1)
159
+ )
160
+ }
161
+ )
162
+ },
163
+ .None => i32(-1)
164
+ )
165
+ });
166
+
167
+ // ============================================================================
168
+ // Internal: read full HTTP response from a TCP stream
169
+ // ============================================================================
170
+
171
+ _read_http_response :: (fn(stream: TcpStream, using(io : IO, exn : Exception)) -> String)({
172
+ buf_size := usize(8192);
173
+ buf := *(u8)(malloc(buf_size).unwrap());
174
+ result := ArrayList(u8).new();
175
+
176
+ // Read until connection closes or we've read the full response
177
+ done := false;
178
+ while runtime(!(done)), {
179
+ n := io.await(stream.read(buf, buf_size, using(io)));
180
+ cond(
181
+ (n <= i32(0)) => {
182
+ done = true;
183
+ },
184
+ true => {
185
+ i := usize(0);
186
+ while runtime((i < usize(n))), {
187
+ result.push((buf &+ i).*);
188
+ i = (i + usize(1));
189
+ };
190
+ // Check if we've received the full response by looking for
191
+ // Content-Length or end of chunked transfer
192
+ // For simplicity, check if the header section is complete
193
+ // and if Content-Length is satisfied
194
+ cond(
195
+ (result.len() > usize(4)) => {
196
+ // Check if we have a complete header section (\r\n\r\n)
197
+ header_end := _find_header_end(result);
198
+ cond(
199
+ (header_end > usize(0)) => {
200
+ // Check Content-Length
201
+ cl := _find_content_length(result, header_end);
202
+ cond(
203
+ (cl >= i32(0)) => {
204
+ body_start := (header_end + usize(4));
205
+ body_len := (result.len() - body_start);
206
+ cond(
207
+ (body_len >= usize(cl)) => {
208
+ done = true;
209
+ },
210
+ true => ()
211
+ );
212
+ },
213
+ true => ()
214
+ );
215
+ },
216
+ true => ()
217
+ );
218
+ },
219
+ true => ()
220
+ );
221
+ }
222
+ );
223
+ };
224
+ free(.Some(*(void)(buf)));
225
+ String.from_bytes(result)
226
+ });
227
+
228
+ // ============================================================================
229
+ // fetch — High-level async HTTP request (like JavaScript's fetch)
230
+ // ============================================================================
231
+
232
+ // Perform an HTTP request with custom options.
233
+ //
234
+ // Example:
235
+ // opts := FetchOptions.new().with_method(.POST).with_body(`{"key": "value"}`);
236
+ // resp := io.await(fetch_with(`http://example.com/api`, opts, using(io)));
237
+ fetch_with :: (fn(url_str: String, opts: FetchOptions, using(io : IO)) ->
238
+ Impl(Future(HttpResponse, IO, Exception))
239
+ )(
240
+ io.async((using(io : IO, exn : Exception)) => {
241
+ // Parse URL
242
+ url := Url.parse(url_str.as_str(), using(exn));
243
+
244
+ // Validate scheme
245
+ scheme := url.scheme();
246
+ is_http := (scheme == `http`);
247
+ is_https := (scheme == `https`);
248
+ cond(
249
+ (is_http || is_https) => (),
250
+ true => {
251
+ exn.throw(dyn HttpError.UnsupportedScheme(scheme));
252
+ }
253
+ );
254
+
255
+ // Extract host
256
+ host := match(url.host(),
257
+ .Some(h) => h,
258
+ .None => {
259
+ exn.throw(dyn HttpError.InvalidUrl(`missing host`));
260
+ `_` // unreachable
261
+ }
262
+ );
263
+
264
+ // Determine port (default: 80 for http, 443 for https)
265
+ port := match(url.port(),
266
+ .Some(p) => p,
267
+ .None => cond(
268
+ (scheme == `https`) => u16(443),
269
+ true => u16(80)
270
+ )
271
+ );
272
+
273
+ // Resolve hostname to IP
274
+ addrs := io.await(lookup_host(host, using(io)));
275
+ cond(
276
+ (addrs.len() == usize(0)) => {
277
+ exn.throw(dyn HttpError.ConnectionFailed(`DNS resolution failed for ${host}`));
278
+ },
279
+ true => ()
280
+ );
281
+ ip := addrs.get(usize(0)).unwrap();
282
+ addr := SocketAddr.new(ip, port);
283
+
284
+ // Connect
285
+ stream := io.await(TcpStream.connect(addr, using(io)));
286
+
287
+ // Build the request path (include query string if present)
288
+ req_path := url.path();
289
+ cond(
290
+ req_path.is_empty() => { req_path = `/`; },
291
+ true => ()
292
+ );
293
+ match(url.query(),
294
+ .Some(q) => { req_path = `${req_path}?${q}`; },
295
+ .None => ()
296
+ );
297
+
298
+ // Build HTTP request
299
+ req := HttpRequest.new(opts.method, req_path);
300
+ req.set_host(host);
301
+
302
+ // Add user-provided headers
303
+ hi := usize(0);
304
+ while runtime((hi < opts.headers.len())), {
305
+ h := opts.headers.get(hi).unwrap();
306
+ req.set_header(h.name, h.value);
307
+ hi = (hi + usize(1));
308
+ };
309
+
310
+ // Set Content-Length if body is provided
311
+ cond(
312
+ (!(opts.body.is_empty())) => {
313
+ req.set_body(opts.body);
314
+ req.set_header(`Content-Length`, `${opts.body.len()}`);
315
+ },
316
+ true => ()
317
+ );
318
+
319
+ // Set Connection: close to ensure the server closes after response
320
+ req.set_header(`Connection`, `close`);
321
+
322
+ // Send request
323
+ req_str := req.to_string();
324
+ io.await(stream.write_string(req_str, using(io)));
325
+
326
+ // Read response
327
+ raw_response := _read_http_response(stream, using(io, exn));
328
+
329
+ // Close connection
330
+ io.await(stream.close(using(io)));
331
+
332
+ // Parse response
333
+ match(parse_response(raw_response),
334
+ .Ok(resp) => resp,
335
+ .Err(e) => {
336
+ exn.throw(dyn HttpError.Other(e));
337
+ HttpResponse.new(i32(0), ``) // unreachable
338
+ }
339
+ )
340
+ })
341
+ );
342
+
343
+ export fetch_with;
344
+
345
+ // Perform an HTTP GET request to the given URL string.
346
+ // Returns the HttpResponse on success.
347
+ //
348
+ // Example:
349
+ // resp := io.await(fetch(`http://example.com`, using(io)));
350
+ // cond(resp.is_ok() => println(resp.body), true => println(`Error`));
351
+ fetch :: (fn(url_str: String, using(io : IO)) ->
352
+ Impl(Future(HttpResponse, IO, Exception))
353
+ )(
354
+ io.async((using(io : IO, exn : Exception)) => {
355
+ return io.await(fetch_with(url_str, FetchOptions.new(), using(io)));
356
+ })
357
+ );
358
+
359
+ export fetch;
@@ -0,0 +1,22 @@
1
+ // std/http - HTTP types, request/response builders, and async client.
2
+ //
3
+ // Example:
4
+ // { HttpRequest, HttpResponse, fetch } :: import "std/http";
5
+ // { Exception } :: import "std/error";
6
+ //
7
+ // main :: (fn(using(io : IO)) -> unit)({
8
+ // given(exn) := Exception(throw : ((err) -> {
9
+ // println(err.message());
10
+ // escape ();
11
+ // }));
12
+ // resp := io.await(fetch(`http://example.com`, using(io)));
13
+ // println(resp.body);
14
+ // });
15
+
16
+ _http :: import "./http.yo";
17
+ _client :: import "./client.yo";
18
+
19
+ export
20
+ ...(_http),
21
+ ...(_client)
22
+ ;
@@ -1,9 +1,9 @@
1
- // std/log/log.yo - Structured logging
1
+ // std/log - Structured logging
2
2
  //
3
3
  // Provides a global logger with configurable level and output destination.
4
4
  //
5
5
  // Example:
6
- // { info, warn, error, set_level, Level } :: import "std/log/log";
6
+ // { info, warn, error, set_level, Level } :: import "std/log";
7
7
  //
8
8
  // set_level(.Info);
9
9
  // info(String.from("application started"));
@@ -1,4 +1,4 @@
1
- // std/regex/regex.yo - Main Regex type
1
+ // std/regex - Regular expression engine
2
2
  //
3
3
  // High-level regex API similar to JavaScript's RegExp.
4
4
  //
@@ -1,9 +1,9 @@
1
- // std/url/url.yo - URL parsing and formatting
1
+ // std/url - URL parsing and formatting
2
2
  //
3
3
  // Parse, manipulate, and format URLs per RFC 3986 (simplified).
4
4
  //
5
5
  // Example:
6
- // { Url, UrlError } :: import "std/url/url";
6
+ // { Url, UrlError } :: import "std/url";
7
7
  //
8
8
  // url := Url.parse("https://example.com:8080/path?q=1#frag").unwrap();
9
9
  // assert(url.scheme() == `https`);
File without changes