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
125 changes: 107 additions & 18 deletions src/codegen/expressions/method-calls/string-dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
handleMatch,
} from "./string-methods.js";

export function dispatchStringMethod(
function dispatchStringBasicOps(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
Expand All @@ -46,14 +46,60 @@ export function dispatchStringMethod(
)
return handleConcat(ctx, expr, params);
if (method === "repeat") return handleRepeat(ctx, expr, params);
return null;
}

function dispatchStringPadSplit(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "padStart") return handlePadStart(ctx, expr, params);
if (method === "padEnd") return handlePadEnd(ctx, expr, params);
if (method === "split") return handleSplit(ctx, expr, params);
if (method === "startsWith") return handleStartsWith(ctx, expr, params);
return null;
}

function dispatchStringTrimOps(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "endsWith") return handleEndsWith(ctx, expr, params);
if (method === "trim") return handleTrim(ctx, expr, params);
if (method === "trimStart") return handleTrimStart(ctx, expr, params);
if (method === "trimEnd") return handleTrimEnd(ctx, expr, params);
return null;
}

function dispatchStringReplaceCase(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "replace") return handleReplace(ctx, expr, params);
if (method === "replaceAll") return handleReplaceAll(ctx, expr, params);
if (method === "charAt") return handleCharAt(ctx, expr, params);
if (method === "charCodeAt") return handleCharCodeAt(ctx, expr, params);
return null;
}

export function dispatchStringMethod(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
const basic = dispatchStringBasicOps(ctx, method, expr, params);
if (basic) return basic;
const padSplit = dispatchStringPadSplit(ctx, method, expr, params);
if (padSplit) return padSplit;
const trim = dispatchStringTrimOps(ctx, method, expr, params);
if (trim) return trim;
if (method === "indexOf") {
if (ctx.isStringArrayExpression(expr.object))
return handleStringArrayIndexOf(ctx, expr, params);
Expand All @@ -74,10 +120,8 @@ export function dispatchStringMethod(
!ctx.isObjectArrayExpression(expr.object)
)
return handleSlice(ctx, expr, params);
if (method === "replace") return handleReplace(ctx, expr, params);
if (method === "replaceAll") return handleReplaceAll(ctx, expr, params);
if (method === "charAt") return handleCharAt(ctx, expr, params);
if (method === "charCodeAt") return handleCharCodeAt(ctx, expr, params);
const replaceCase = dispatchStringReplaceCase(ctx, method, expr, params);
if (replaceCase) return replaceCase;
if (method === "toUpperCase") return handleToUpperCase(ctx, expr, params);
if (method === "toLowerCase") return handleToLowerCase(ctx, expr, params);
if (method === "toString") {
Expand All @@ -97,7 +141,7 @@ export function dispatchStringMethod(
return null;
}

export function dispatchArrayMethod(
function dispatchArrayMutators(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
Expand All @@ -113,19 +157,68 @@ export function dispatchArrayMethod(
return ctx.arrayGen.generateStringArrayMap(expr, params);
return ctx.arrayGen.generateArrayMap(expr, params);
}
return null;
}

function dispatchArrayIterators(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "find") return ctx.arrayGen.generateArrayFind(expr, params);
if (method === "some") return ctx.arrayGen.generateArraySome(expr, params);
if (method === "every") return ctx.arrayGen.generateArrayEvery(expr, params);
if (method === "filter") return ctx.arrayGen.generateArrayFilter(expr, params);
return null;
}

function dispatchArrayTransforms(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "forEach") return ctx.arrayGen.generateArrayForEach(expr, params);
if (method === "reduce") return ctx.arrayGen.generateArrayReduce(expr, params);
if (method === "reverse") return ctx.arrayGen.generateArrayReverse(expr, params);
if (method === "shift") return ctx.arrayGen.generateArrayShift(expr, params);
return null;
}

function dispatchArrayReorder(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
): string | null {
if (method === "unshift") return ctx.arrayGen.generateArrayUnshift(expr, params);
if (method === "findIndex") return ctx.arrayGen.generateArrayFindIndex(expr, params);
if (method === "sort") return ctx.arrayGen.generateArraySort(expr, params);
if (method === "splice") return ctx.arrayGen.generateArraySplice(expr, params);
return null;
}

export function dispatchArrayMethod(
ctx: MethodCallGeneratorContext,
method: string,
expr: MethodCallNode,
params: string[],
isClassInstance: boolean,
): string | null {
const mutator = dispatchArrayMutators(ctx, method, expr, params, isClassInstance);
if (mutator) return mutator;
if (
method === "join" &&
(ctx.isStringArrayExpression(expr.object) ||
ctx.isArrayExpression(expr.object) ||
ctx.isObjectArrayExpression(expr.object))
)
return ctx.arrayGen.generateArrayJoin(expr, params);
if (method === "find") return ctx.arrayGen.generateArrayFind(expr, params);
if (method === "some") return ctx.arrayGen.generateArraySome(expr, params);
if (method === "every") return ctx.arrayGen.generateArrayEvery(expr, params);
if (method === "filter") return ctx.arrayGen.generateArrayFilter(expr, params);
if (method === "forEach") return ctx.arrayGen.generateArrayForEach(expr, params);
if (method === "reduce") return ctx.arrayGen.generateArrayReduce(expr, params);
const iterator = dispatchArrayIterators(ctx, method, expr, params);
if (iterator) return iterator;
const transform = dispatchArrayTransforms(ctx, method, expr, params);
if (transform) return transform;
if (
method === "slice" &&
(ctx.isArrayExpression(expr.object) ||
Expand All @@ -140,11 +233,7 @@ export function dispatchArrayMethod(
ctx.isObjectArrayExpression(expr.object))
)
return ctx.arrayGen.generateArrayConcat(expr, params);
if (method === "reverse") return ctx.arrayGen.generateArrayReverse(expr, params);
if (method === "shift") return ctx.arrayGen.generateArrayShift(expr, params);
if (method === "unshift") return ctx.arrayGen.generateArrayUnshift(expr, params);
if (method === "findIndex") return ctx.arrayGen.generateArrayFindIndex(expr, params);
if (method === "sort") return ctx.arrayGen.generateArraySort(expr, params);
if (method === "splice") return ctx.arrayGen.generateArraySplice(expr, params);
const reorder = dispatchArrayReorder(ctx, method, expr, params);
if (reorder) return reorder;
return null;
}
73 changes: 49 additions & 24 deletions src/codegen/infrastructure/type-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,45 @@ export function resolvedTypeToLlvm(rt: ResolvedType): string {

export type TypeMappingMode = "default" | "param" | "return" | "struct_field" | "json";

function ffiTypeToLlvm1(tsType: string): string | null {
if (tsType === "i8") return "i8";
if (tsType === "i16") return "i16";
if (tsType === "i32") return "i32";
if (tsType === "i64") return "i64";
return null;
}

function ffiTypeToLlvm2(tsType: string): string | null {
if (tsType === "u8") return "i8";
if (tsType === "u16") return "i16";
if (tsType === "u32") return "i32";
if (tsType === "u64") return "i64";
return null;
}

function ffiTypeToLlvm3(tsType: string): string | null {
if (tsType === "f32") return "float";
if (tsType === "f64") return "double";
if (tsType === "i8_ptr" || tsType === "ptr") return "i8*";
return null;
}

function basicTypeToLlvm(tsType: string): string | null {
if (tsType === "string") return "i8*";
if (tsType === "number" || tsType === "boolean") return "double";
if (tsType === "void") return "void";
if (tsType === "string[]") return "%StringArray*";
return null;
}

function collectionTypeToLlvm(tsType: string): string | null {
if (tsType === "number[]" || tsType === "boolean[]") return "%Array*";
if (tsType === "Uint8Array") return "%Uint8Array*";
if (tsType.endsWith("[]")) return "%ObjectArray*";
if (tsType.startsWith("Set<")) return "%StringSet*";
return null;
}

export function canonicalTypeToLlvm(
tsType: string,
mode: string,
Expand All @@ -153,26 +192,16 @@ export function canonicalTypeToLlvm(
fieldName: string,
): string {
if (tsType === null || tsType === undefined || tsType === "") {
// Empty/null type: callers should provide a valid type. These defaults are
// intentional language semantics — untyped returns are double (number),
// untyped params/fields are i8* (pointer, the safer default).
if (mode === "return") return "double";
return "i8*";
}

// FFI type passthrough — zero-cost: maps directly to LLVM types with no
// double conversion. Used in `declare function` for calling C code.
if (tsType === "i8") return "i8";
if (tsType === "i16") return "i16";
if (tsType === "i32") return "i32";
if (tsType === "i64") return "i64";
if (tsType === "u8") return "i8";
if (tsType === "u16") return "i16";
if (tsType === "u32") return "i32";
if (tsType === "u64") return "i64";
if (tsType === "f32") return "float";
if (tsType === "f64") return "double";
if (tsType === "i8_ptr" || tsType === "ptr") return "i8*";
const ffi1 = ffiTypeToLlvm1(tsType);
if (ffi1) return ffi1;
const ffi2 = ffiTypeToLlvm2(tsType);
if (ffi2) return ffi2;
const ffi3 = ffiTypeToLlvm3(tsType);
if (ffi3) return ffi3;

if (fieldName === "nodePtr" || fieldName === "treePtr") return "i8*";

Expand All @@ -186,14 +215,10 @@ export function canonicalTypeToLlvm(

if (isEnum) return "double";

if (tsType === "string") return "i8*";
if (tsType === "number" || tsType === "boolean") return "double";
if (tsType === "void") return "void";
if (tsType === "string[]") return "%StringArray*";
if (tsType === "number[]" || tsType === "boolean[]") return "%Array*";
if (tsType === "Uint8Array") return "%Uint8Array*";
if (tsType.endsWith("[]")) return "%ObjectArray*";
if (tsType.startsWith("Set<")) return "%StringSet*";
const basic = basicTypeToLlvm(tsType);
if (basic) return basic;
const coll = collectionTypeToLlvm(tsType);
if (coll) return coll;
if (tsType.startsWith("Map<")) return "%StringMap*";
if (tsType.startsWith("'") || tsType.startsWith('"')) return "i8*";

Expand Down
Loading