Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions c_bridges/regex-bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include <stdlib.h>
#include <string.h>

extern void *GC_malloc(size_t);
extern void *GC_malloc_atomic(size_t);

void *cs_regex_alloc(void) {
return malloc(sizeof(regex_t));
}
Expand Down Expand Up @@ -32,3 +35,39 @@ void cs_regex_free(void *preg) {
free(preg);
}
}

char *cs_regex_exec_dyn(void *preg, const char *str, int max_groups) {
regex_t *regex = (regex_t *)preg;
int total = (int)regex->re_nsub + 1;
if (total > max_groups) total = max_groups;

regmatch_t *pmatch = (regmatch_t *)calloc((size_t)total, sizeof(regmatch_t));
if (!pmatch) return NULL;

if (regexec(regex, str, (size_t)total, pmatch, 0) != 0) {
free(pmatch);
return NULL;
}

char **strings = (char **)GC_malloc((size_t)total * sizeof(char *));
for (int i = 0; i < total; i++) {
if (pmatch[i].rm_so >= 0) {
int slen = pmatch[i].rm_eo - pmatch[i].rm_so;
char *s = (char *)GC_malloc_atomic((size_t)(slen + 1));
if (slen > 0) strncpy(s, str + pmatch[i].rm_so, (size_t)slen);
s[slen] = '\0';
strings[i] = s;
} else {
char *s = (char *)GC_malloc_atomic(1);
s[0] = '\0';
strings[i] = s;
}
}
free(pmatch);

char *arr = (char *)GC_malloc(16);
*((char ***)arr) = strings;
*((int *)(arr + 8)) = total;
*((int *)(arr + 12)) = total;
return arr;
}
26 changes: 26 additions & 0 deletions chadscript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,31 @@ interface HttpRequest {
path: string;
body: string;
contentType: string;
headers: string;
bodyLen: number;
}

interface HttpResponse {
status: number;
body: string;
headers: string;
}

interface WsEvent {
data: string;
event: string;
connId: string;
}

declare function httpServe(port: number, handler: (req: HttpRequest) => HttpResponse): void;
declare function httpServe(
port: number,
handler: (req: HttpRequest) => HttpResponse,
wsHandler: (event: WsEvent) => string,
): void;

declare function wsBroadcast(message: string): void;
declare function wsSend(connId: string, message: string): void;

// ============================================================================
// Async / Timers
Expand Down Expand Up @@ -291,4 +308,13 @@ declare namespace ChadScript {
function embedFile(path: string): string;
function embedDir(path: string): void;
function getEmbeddedFile(key: string): string;
function parseMultipart(req: HttpRequest): MultipartPart[];
}

interface MultipartPart {
name: string;
filename: string;
contentType: string;
data: string;
dataLen: number;
}
36 changes: 23 additions & 13 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,42 @@ export default defineConfig({
},

nav: [
{ text: 'Why ChadScript?', link: '/why-chadscript' },
{ text: 'Get Started', link: '/getting-started/installation' },
{ text: 'API', link: '/stdlib/' },
{ text: 'API Reference', link: '/stdlib/' },
{ text: 'Benchmarks', link: '/benchmarks' },
{ text: 'GitHub', link: 'https://github.com/cs01/ChadScript' }
],

sidebar: [
{
text: 'Getting Started',
text: 'Why ChadScript?',
items: [
{ text: 'Why ChadScript?', link: '/why-chadscript' },
{ text: 'Benchmarks', link: '/benchmarks' },
{ text: 'FAQ', link: '/faq' },
]
},
{
text: 'Get Started',
items: [
{ text: 'About ChadScript', link: '/language/architecture' },
{ text: 'Installation', link: '/getting-started/installation' },
{ text: 'Examples', link: '/getting-started/quickstart' },
{ text: 'Quickstart', link: '/getting-started/quickstart' },
{ text: 'CLI Reference', link: '/getting-started/cli' },
{ text: 'Debugging', link: '/getting-started/debugging' },
]
},
{
text: 'Language',
items: [
{ text: 'Supported Features', link: '/language/features' },
{ text: 'Debugging', link: '/getting-started/debugging' }
{ text: 'Classes & Interfaces', link: '/language/classes' },
{ text: 'Type Mappings', link: '/language/type-mappings' },
{ text: 'How It Works', link: '/language/architecture' },
]
},
{
text: 'Standard Library',
text: 'API Reference',
items: [
{ text: 'Overview', link: '/stdlib/' },
{ text: 'Array Methods', link: '/stdlib/array' },
Expand All @@ -54,7 +70,7 @@ export default defineConfig({
{ text: 'Date', link: '/stdlib/date' },
{ text: 'fetch', link: '/stdlib/fetch' },
{ text: 'fs', link: '/stdlib/fs' },
{ text: 'httpServe', link: '/stdlib/http-server' },
{ text: 'HTTP Server / Router', link: '/stdlib/http-server' },
{ text: 'JSON', link: '/stdlib/json' },
{ text: 'Map', link: '/stdlib/map' },
{ text: 'Math', link: '/stdlib/math' },
Expand All @@ -71,12 +87,6 @@ export default defineConfig({
{ text: 'tty', link: '/stdlib/tty' }
]
},
{
text: 'Performance',
items: [
{ text: 'Benchmarks', link: '/benchmarks' }
]
},
],

socialLinks: [
Expand Down
108 changes: 107 additions & 1 deletion docs/stdlib/http-server.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,113 @@
# httpServe
# HTTP Server

Built-in HTTP server with websocket support compiled to native code via libuv TCP + picohttpparser.

## Router (recommended)

For most servers, use the `Router` class from `src/router.ts`. It provides an Express/Hono-style API with URL parameter extraction, method matching, and chainable response helpers.

```typescript
import { Router, Context } from "./src/router";

const app = new Router();

app.get("/", (c: Context) => c.json('{"status":"ok"}'));

app.get("/api/users/:id", (c: Context) => {
const id = c.req.param("id");
return c.json('{"id":"' + id + '"}');
});

app.post("/api/users", (c: Context) => {
c.status(201);
return c.text("Created");
});

app.notFound((c: Context) => c.status(404).text("Not Found"));

httpServe(3000, (req) => app.handle(req));
```

### Router methods

| Method | Description |
|--------|-------------|
| `app.get(pattern, handler)` | Register GET route |
| `app.post(pattern, handler)` | Register POST route |
| `app.put(pattern, handler)` | Register PUT route |
| `app.delete(pattern, handler)` | Register DELETE route |
| `app.all(pattern, handler)` | Match any HTTP method |
| `app.notFound(handler)` | Custom 404 handler |
| `app.handle(req)` | Dispatch an `HttpRequest` → `HttpResponse` |
| `app.compile()` | Pre-compile the combined regex (called automatically on first `handle`) |

Route patterns support `:param` segments and `*` wildcards:

```typescript
app.get("/users/:id", ...); // /users/42 → param("id") == "42"
app.get("/users/:name/posts/:pid", ...); // multiple params
app.all("/static/*", ...); // wildcard
```

### Context API

The handler receives a `Context` (aliased as `c` by convention):

```typescript
app.get("/example", (c: Context) => {
// Read request
const id = c.req.param("id"); // URL param
const auth = c.req.header("Authorization"); // request header
const body = c.req.body; // raw body
const method = c.req.method; // "GET", "POST", …

// Build response (chainable)
c.status(201);
c.header("X-Custom", "value");
return c.json('{"ok":true}');
});
```

**Response methods** — call one to return the `HttpResponse`:

| Method | Content-Type | Notes |
|--------|--------------|-------|
| `c.text(body)` | `text/plain` | |
| `c.json(body)` | `application/json` | pass a JSON string |
| `c.html(body)` | `text/html` | |
| `c.redirect(url)` | — | 302, sets `Location` header |

**Chainable setters** (return `Context`, call before a response method):

| Method | Effect |
|--------|--------|
| `c.status(code)` | Set HTTP status code |
| `c.header(name, value)` | Add a response header |

### HTTP utility functions

`src/http-utils.ts` provides helpers for parsing common request data:

```typescript
import { getHeader, parseQueryString, parseCookies } from "./src/http-utils";

// Parse a single request header by name (case-insensitive)
const auth = getHeader(req.headers, "Authorization"); // "Bearer abc"

// Parse a query string into a Map
const qs = parseQueryString("page=2&limit=10");
qs.get("page"); // "2"
qs.get("limit"); // "10"

// Parse the Cookie header into a Map
const cookies = parseCookies(getHeader(req.headers, "Cookie"));
cookies.get("session"); // "abc123"
```

---

## `httpServe(port, handler)`

## `httpServe(port, handler)`

Start an HTTP server on the given port. The handler function receives an `HttpRequest` and returns an `HttpResponse`.
Expand Down
2 changes: 1 addition & 1 deletion docs/stdlib/regexp.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const result = "hello world".match(/(\w+)/);
// ["hello", "hello"]
```

Returns `null` if no match.
Returns `null` if no match. `exec` works for both literal patterns (`/foo/`) and runtime-constructed patterns (`new RegExp(str)`) — the group count is read from the compiled regex at runtime.

## Example

Expand Down
Loading
Loading