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
53 changes: 53 additions & 0 deletions chadscript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,56 @@ interface MultipartPart {
data: string;
dataLen: number;
}

// ============================================================================
// stdlib modules (import { ... } from "chadscript/*")
// ============================================================================

declare module "chadscript/argparse" {
export class ArgumentParser {
constructor(programName: string, description: string);
addFlag(name: string, shortFlag: string, help: string): void;
addOption(name: string, shortFlag: string, help: string, defaultVal: string): void;
parse(argv: string[]): number;
getFlag(name: string): boolean;
getOption(name: string): string;
}
}

declare module "chadscript/router" {
export class RouterRequest {
method: string;
path: string;
body: string;
contentType: string;
headers: string;
param(name: string): string;
header(name: string): string;
}

export class Context {
req: RouterRequest;
status(code: number): Context;
header(name: string, value: string): Context;
text(body: string): HttpResponse;
json(data: string): HttpResponse;
html(body: string): HttpResponse;
redirect(url: string): HttpResponse;
}

export class Router {
get(pattern: string, handler: (c: Context) => HttpResponse): void;
post(pattern: string, handler: (c: Context) => HttpResponse): void;
put(pattern: string, handler: (c: Context) => HttpResponse): void;
delete(pattern: string, handler: (c: Context) => HttpResponse): void;
all(pattern: string, handler: (c: Context) => HttpResponse): void;
notFound(handler: (c: Context) => HttpResponse): void;
handle(req: HttpRequest): HttpResponse;
}
}

declare module "chadscript/http-utils" {
export function getHeader(headersRaw: string, name: string): string;
export function parseQueryString(qs: string): Map<string, string>;
export function parseCookies(cookieHeader: string): Map<string, string>;
}
6 changes: 1 addition & 5 deletions examples/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,4 @@ console.log(" curl -X POST -d 'hello' http://localhost:" + port + "/echo");
console.log(" curl -H 'Authorization: Bearer token' http://localhost:" + port + "/headers");
console.log("");

function handleRequest(req: HttpRequest): HttpResponse {
return app.handle(req);
}

httpServe(port, handleRequest);
httpServe(port, (req: HttpRequest) => app.handle(req));
84 changes: 70 additions & 14 deletions src/codegen/llvm-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2449,22 +2449,42 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
let hasHeaders = false;
let hasBodyLen = false;
const handlerName = this.httpHandlers[0];
let handlerReturnType: string | null = null;
for (let fi = 0; fi < this.ast.functions.length; fi++) {
const func = this.ast.functions[fi];
if (func && func.name === handlerName && func.returnType) {
const retIface = this.getInterfaceFromAST(func.returnType);
if (retIface) {
const fields = (retIface as { fields: { name: string }[] }).fields;
for (let fj = 0; fj < fields.length; fj++) {
if (fields[fj].name === "headers") {
hasHeaders = true;
}
if (fields[fj].name === "bodyLen") {
hasBodyLen = true;
}
handlerReturnType = func.returnType;
break;
}
}
if (!handlerReturnType) {
const liftedFuncs = this.exprGen.arrowFunctionGen.getLiftedFunctions();
for (let fi = 0; fi < liftedFuncs.length; fi++) {
const func = liftedFuncs[fi];
const lf = func as {
name: string;
params: string[];
body: BlockStatement;
returnType?: string;
};
if (lf.name === handlerName && lf.returnType) {
handlerReturnType = lf.returnType;
break;
}
}
}
if (handlerReturnType) {
const retIface = this.getInterfaceFromAST(handlerReturnType);
if (retIface) {
const fields = (retIface as { fields: { name: string }[] }).fields;
for (let fj = 0; fj < fields.length; fj++) {
if (fields[fj].name === "headers") {
hasHeaders = true;
}
if (fields[fj].name === "bodyLen") {
hasBodyLen = true;
}
}
break;
}
}

Expand Down Expand Up @@ -3558,6 +3578,30 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
return ir;
}

private inferArrowHandlerReturnType(arrow: ArrowFunctionNode): string | null {
const body = arrow.body as { type: string };
let expr: { type: string } | null = null;
if (body.type === "block") {
const stmts = (arrow.body as { statements: { type: string; value?: { type: string } }[] })
.statements;
for (let i = 0; i < stmts.length; i++) {
if (stmts[i].type === "return" && stmts[i].value) {
expr = stmts[i].value!;
break;
}
}
} else {
expr = body;
}
if (!expr || expr.type !== "method_call") return null;
const mc = expr as { type: string; object: Expression; method: string };
if ((mc.object as { type: string }).type !== "variable") return null;
const varName = (mc.object as VariableNode).name;
const className = this.symbolTable.getConcreteClass(varName);
if (!className) return null;
return this.getMethodReturnType(className, mc.method);
}

// Generate HTTP server - creates a TCP server that parses HTTP and calls handler
public generateHttpServe(expr: CallNode, params: string[]): string {
if (expr.args.length < 2) {
Expand All @@ -3569,10 +3613,22 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {

const portValue = this.generateExpression(expr.args[0], params);
const handlerArg = expr.args[1];
if (handlerArg.type !== "variable") {
return this.emitError("httpServe() handler must be a function reference", expr.loc);
let handlerName: string;
if (handlerArg.type === "variable") {
handlerName = (handlerArg as VariableNode).name;
} else if (handlerArg.type === "arrow_function") {
const arrowExpr = handlerArg as ArrowFunctionNode;
const returnTypeName = this.inferArrowHandlerReturnType(arrowExpr);
handlerName = this.exprGen.arrowFunctionGen.generateArrowFunction(arrowExpr, params, {
paramTypes: ["i8*"],
returnType: returnTypeName || "i8*",
});
} else {
return this.emitError(
"httpServe() handler must be a function reference or arrow function",
expr.loc,
);
}
const handlerName = (handlerArg as VariableNode).name;

// Track handler for http server event handler generation
this.httpHandlers.push(handlerName);
Expand Down
Loading