From eee739d61dd350c609253d3c07d1678de098ff45 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:06:29 -0800 Subject: [PATCH 01/13] add canonicalTypeToLlvm and rewrite existing type mappers as thin wrappers --- src/codegen/infrastructure/type-system.ts | 112 ++++++++++++---------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/codegen/infrastructure/type-system.ts b/src/codegen/infrastructure/type-system.ts index 2ee416e5..640d97b5 100644 --- a/src/codegen/infrastructure/type-system.ts +++ b/src/codegen/infrastructure/type-system.ts @@ -122,27 +122,7 @@ export function createFloatType(): ResolvedType { } export function tsTypeToLlvm(tsType: string): string { - if (tsType === null || tsType === undefined || tsType === '') return 'i8*'; - if (tsType === 'string') return 'i8*'; - if (tsType === 'number') return 'double'; - if (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*'; - if (tsType.startsWith('Map<')) return '%StringMap*'; - if (tsType.startsWith("'") || tsType.startsWith('"')) return 'i8*'; - if (tsType.indexOf(' | ') !== -1) { - const parts = tsType.split(' | '); - for (let i = 0; i < parts.length; i++) { - const part = parts[i].trim(); - if (part === 'null' || part === 'undefined') continue; - return tsTypeToLlvm(part); - } - } - return 'i8*'; + return canonicalTypeToLlvm(tsType, { mode: 'default' }); } export function resolvedTypeToLlvm(rt: ResolvedType): string { @@ -163,16 +143,71 @@ export function resolvedTypeToLlvm(rt: ResolvedType): string { return 'i8*'; } -export function tsTypeToLlvmJson(tsType: string): string { - if (tsType === null || tsType === undefined || tsType === '') return 'i8*'; +export type TypeMappingMode = 'default' | 'param' | 'return' | 'struct_field' | 'json'; + +export interface TypeMappingOptions { + mode: TypeMappingMode; + isEnum?: boolean; + isInterface?: boolean; + fieldName?: string; +} + +export function canonicalTypeToLlvm(tsType: string, options: TypeMappingOptions): string { + if (tsType === null || tsType === undefined || tsType === '') { + if (options.mode === 'return') return 'double'; + return 'i8*'; + } + + if (options.fieldName === 'nodePtr' || options.fieldName === 'treePtr') return 'i8*'; + + if (options.mode === 'param') { + if (tsType === 'any' || tsType === 'unknown') { + throw new Error(`Parameter type '${tsType}' is not allowed — add explicit type annotations or fix the parser`); + } + } + + if (options.isEnum) return 'double'; + if (tsType === 'string') return 'i8*'; - if (tsType === 'number') return 'double'; - if (tsType === 'boolean') return 'double'; + if (tsType === 'number' || tsType === 'boolean') return 'double'; + if (tsType === 'void') return 'void'; if (tsType === 'string[]') return '%StringArray*'; - if (tsType === 'number[]') return '%Array*'; + if (tsType === 'number[]' || tsType === 'boolean[]') return '%Array*'; + if (tsType === 'Uint8Array') return '%Uint8Array*'; + if (tsType.endsWith('[]')) return '%ObjectArray*'; + if (tsType.startsWith('Set<')) return '%StringSet*'; + if (tsType.startsWith('Map<')) return '%StringMap*'; + if (tsType.startsWith("'") || tsType.startsWith('"')) return 'i8*'; + + if (tsType.indexOf(' | ') !== -1) { + const parts = tsType.split(' | '); + for (let i = 0; i < parts.length; i++) { + const part = parts[i].trim(); + if (part === 'null' || part === 'undefined') continue; + return canonicalTypeToLlvm(part, options); + } + } + + if (options.isInterface && (options.mode === 'param' || options.mode === 'struct_field')) { + return `%${tsType}*`; + } + + if (options.mode === 'return') { + if (tsType !== 'number' && tsType !== 'boolean') return 'i8*'; + return 'double'; + } + + if (options.mode === 'json') { + return 'i8*'; + } + return 'i8*'; } +export function tsTypeToLlvmJson(tsType: string): string { + return canonicalTypeToLlvm(tsType, { mode: 'json' }); +} + export function checkUnsafeUnionType(typeStr: string): string | null { if (!typeStr || typeStr.indexOf(' | ') === -1) return null; @@ -308,35 +343,14 @@ export function mapParamTypeToLLVM( paramIsEnum: boolean, paramIsInterface: boolean ): string { - if (paramName === 'nodePtr' || paramName === 'treePtr') return 'i8*'; - if (paramType === 'any' || paramType === 'unknown') { - throw new Error(`Parameter type '${paramType}' is not allowed — add explicit type annotations or fix the parser`); - } - if (paramIsEnum) return 'double'; - if (paramType === 'string') return 'i8*'; - if (paramType === 'number' || paramType === 'boolean') return 'double'; - if (paramType === 'string[]') return '%StringArray*'; - if (paramType === 'number[]' || paramType === 'boolean[]') return '%Array*'; - if (paramType === 'Uint8Array') return '%Uint8Array*'; - if (paramType.endsWith('[]')) return '%ObjectArray*'; - if (paramType.startsWith('Set<')) return '%StringSet*'; - if (paramType.startsWith('Map<')) return '%StringMap*'; - if (paramIsInterface) return `%${paramType}*`; - return 'i8*'; + return canonicalTypeToLlvm(paramType, { mode: 'param', isEnum: paramIsEnum, isInterface: paramIsInterface, fieldName: paramName }); } export function mapReturnTypeToLLVM( returnType: string, returnIsEnum: boolean ): string { - if (returnType === 'string') return 'i8*'; - if (returnType === 'void') return 'void'; - if (returnType === 'string[]') return '%StringArray*'; - if (returnType === 'number[]' || returnType === 'boolean[]') return '%Array*'; - if (returnType === 'Uint8Array') return '%Uint8Array*'; - if (returnType.endsWith('[]')) return '%ObjectArray*'; - if (returnType !== '' && returnType !== 'number' && returnType !== 'boolean' && !returnIsEnum) return 'i8*'; - return 'double'; + return canonicalTypeToLlvm(returnType, { mode: 'return', isEnum: returnIsEnum }); } function parseGenericTypeString(s: string): { base: string; params: string } | null { From b102091398f9240da9b3287e152ad13678421bd9 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:08:00 -0800 Subject: [PATCH 02/13] fix paramIsInterface hardcoded false in calls.ts to use real interface check --- src/codegen/expressions/calls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index 513049c3..f34fbeb3 100644 --- a/src/codegen/expressions/calls.ts +++ b/src/codegen/expressions/calls.ts @@ -524,7 +524,7 @@ export class CallExpressionGenerator { for (let i = 0; i < func.paramTypes.length; i++) { const p = func.paramTypes[i] as string; const paramName = func.params[i] || ''; - paramTypes.push(mapParamTypeToLLVM(p, paramName, this.ctx.isEnumType(stripNullable(p)), false)); + paramTypes.push(mapParamTypeToLLVM(p, paramName, this.ctx.isEnumType(stripNullable(p)), this.ctx.interfaceStructGenHasInterface(stripNullable(p)))); } } else { const funcNode = this.getFunctionFromAST(expr.name); From 0f9b61bb7e053cc5a39a7f47f55dcc9109b94cb5 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:09:02 -0800 Subject: [PATCH 03/13] fix getClassType returning i32* instead of i8* --- src/codegen/infrastructure/type-context.ts | 2 +- tests/unit/type-context.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/infrastructure/type-context.ts b/src/codegen/infrastructure/type-context.ts index 2777cc4a..05a1a75c 100644 --- a/src/codegen/infrastructure/type-context.ts +++ b/src/codegen/infrastructure/type-context.ts @@ -84,7 +84,7 @@ export class TypeContext { } getClassType(name: string): ResolvedType { - return this.intern(name, 'i32*'); + return this.intern(name, 'i8*'); } getNullableType(base: ResolvedType): ResolvedType { diff --git a/tests/unit/type-context.test.ts b/tests/unit/type-context.test.ts index dfb5c50f..936abb2b 100644 --- a/tests/unit/type-context.test.ts +++ b/tests/unit/type-context.test.ts @@ -82,7 +82,7 @@ describe('TypeContext', () => { const cls1 = ctx.getClassType('MyClass'); const cls2 = ctx.getClassType('MyClass'); assert.strictEqual(cls1, cls2); - assert.strictEqual(cls1.cachedLlvmType, 'i32*'); + assert.strictEqual(cls1.cachedLlvmType, 'i8*'); }); it('should resolve type strings', () => { From 0eed6d99e219168b574bd42da08d6ac1a528a469 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:18:19 -0800 Subject: [PATCH 04/13] replace member access if/else dispatch chain with handler registry --- src/codegen/expressions/access/member.ts | 190 ++++++++++++----------- 1 file changed, 103 insertions(+), 87 deletions(-) diff --git a/src/codegen/expressions/access/member.ts b/src/codegen/expressions/access/member.ts index 236df401..8cff0b53 100644 --- a/src/codegen/expressions/access/member.ts +++ b/src/codegen/expressions/access/member.ts @@ -157,6 +157,8 @@ export interface MemberAccessGeneratorContext { getTargetArch(): string; } +export type MemberAccessHandlerFn = (expr: MemberAccessNode, ctx: MemberAccessGeneratorContext, params: string[]) => string | null; + /** * MemberAccessGenerator * @@ -170,7 +172,104 @@ export interface MemberAccessGeneratorContext { * - TypeScript interface-based property access */ export class MemberAccessGenerator { - constructor(private ctx: MemberAccessGeneratorContext) {} + private handlers: MemberAccessHandlerFn[]; + + constructor(private ctx: MemberAccessGeneratorContext) { + this.handlers = this.buildHandlers(); + } + + private buildHandlers(): MemberAccessHandlerFn[] { + const handlers: MemberAccessHandlerFn[] = []; + + handlers.push((expr, ctx, _params) => this.handleEnumMemberAccess(expr)); + + handlers.push((expr, ctx, _params) => this.handleTypedJsonStructAccess(expr)); + + handlers.push((expr, ctx, _params) => { + if (isProcessArgv(expr)) return this.handleProcessArgv(); + return null; + }); + + handlers.push((expr, ctx, _params) => { + if (isProcessPlatform(expr)) { + const platformStr = ctx.getTargetOS() || process.platform; + return ctx.stringGen.doCreateStringConstant(platformStr); + } + return null; + }); + + handlers.push((expr, ctx, _params) => { + if (isProcessEnvAccess(expr)) return this.handleProcessEnvAccess(expr); + return null; + }); + + handlers.push((expr, ctx, _params) => handleProcessSimpleProperty(ctx, expr)); + + handlers.push((expr, ctx, params) => this.handleClassPropertyAccess(expr, params)); + + handlers.push((expr, ctx, params) => { + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === null || exprObjType === undefined) return '0.0'; + if (exprObjType === 'variable' && ctx.symbolTable.isJSON((expr.object as VariableNode).name)) { + return this.handleJsonPropertyAccess(expr, params); + } + return null; + }); + + handlers.push((expr, ctx, params) => { + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === 'member_access') { + const chainedResult = this.handleChainedInterfaceAccess(expr, params); + if (chainedResult !== null) return chainedResult; + const classFieldChainResult = this.handleClassFieldChainedAccess(expr, params); + if (classFieldChainResult !== null) return classFieldChainResult; + const nestedResult = this.handleNestedJsonAccess(expr, params); + if (nestedResult !== null) return nestedResult; + } + return null; + }); + + handlers.push((expr, ctx, params) => { + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === 'index_access') return this.handleIndexAccessPropertyAccess(expr, params); + return null; + }); + + handlers.push((expr, ctx, params) => { + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === 'type_assertion') return this.handleTypeAssertionPropertyAccess(expr, params); + return null; + }); + + handlers.push((expr, ctx, params) => { + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === 'method_call') return this.handleMethodCallResultPropertyAccess(expr, params); + return null; + }); + + handlers.push((expr, ctx, params) => this.handleObjectPropertyAccess(expr, params)); + + handlers.push((expr, ctx, params) => { + if (expr.property === 'length') return this.handleLengthProperty(expr, params); + return null; + }); + + handlers.push((expr, ctx, params) => { + if (expr.property === 'size') return this.handleSizeProperty(expr, params); + return null; + }); + + handlers.push((expr, ctx, _params) => this.handleResponseProperty(expr)); + + handlers.push((expr, ctx, _params) => this.handleStatProperty(expr)); + + return handlers; + } private hasObjectInfo(name: string): boolean { if (!this.ctx.symbolTable.isObject(name) && !this.ctx.symbolTable.isJSON(name)) return false; @@ -314,94 +413,11 @@ export class MemberAccessGenerator { return this.ctx.generateExpression(expr.object, params); } - const enumResult = this.handleEnumMemberAccess(expr); - if (enumResult !== null) return enumResult; - - const typedJsonResult = this.handleTypedJsonStructAccess(expr); - if (typedJsonResult !== null) return typedJsonResult; - - if (this.isProcessArgv(expr)) { - return this.handleProcessArgv(); - } - - if (this.isProcessPlatform(expr)) { - const platformStr = this.ctx.getTargetOS() || process.platform; - return this.ctx.stringGen.doCreateStringConstant(platformStr); - } - - if (this.isProcessEnvAccess(expr)) { - return this.handleProcessEnvAccess(expr); - } - - const processSimple = this.handleProcessSimpleProperty(expr); - if (processSimple !== null) return processSimple; - - const classResult = this.handleClassPropertyAccess(expr, params); - if (classResult !== null) return classResult; - - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === null || exprObjType === undefined) { - return '0.0'; - } - - if (exprObjType === 'variable' && this.ctx.symbolTable.isJSON((expr.object as VariableNode).name)) { - return this.handleJsonPropertyAccess(expr, params); - } - - // Handle nested JSON object access - if (exprObjType === 'member_access') { - const chainedResult = this.handleChainedInterfaceAccess(expr, params); - if (chainedResult !== null) return chainedResult; - - const classFieldChainResult = this.handleClassFieldChainedAccess(expr, params); - if (classFieldChainResult !== null) return classFieldChainResult; - - const nestedResult = this.handleNestedJsonAccess(expr, params); - if (nestedResult !== null) return nestedResult; - } - - // Handle indexed access to object array elements (e.g., arr[i].property) - if (exprObjType === 'index_access') { - const indexResult = this.handleIndexAccessPropertyAccess(expr, params); - if (indexResult !== null) return indexResult; - } - - // Handle type assertion property access (e.g., (expr as Type).property) - if (exprObjType === 'type_assertion') { - const assertResult = this.handleTypeAssertionPropertyAccess(expr, params); - if (assertResult !== null) return assertResult; - } - - // Handle method call result property access (e.g., map.get(key)?.property) - if (exprObjType === 'method_call') { - const methodResult = this.handleMethodCallResultPropertyAccess(expr, params); - if (methodResult !== null) return methodResult; - } - - // Handle regular object property access - const objResult = this.handleObjectPropertyAccess(expr, params); - if (objResult !== null) return objResult; - - // Handle .length property - if (expr.property === 'length') { - return this.handleLengthProperty(expr, params); - } - - // Handle .size property (Map/Set) - if (expr.property === 'size') { - const sizeResult = this.handleSizeProperty(expr, params); - if (sizeResult !== null) return sizeResult; + for (let i = 0; i < this.handlers.length; i++) { + const result = this.handlers[i](expr, this.ctx, params); + if (result !== null) return result; } - // Handle Response properties - const responseResult = this.handleResponseProperty(expr); - if (responseResult !== null) return responseResult; - - const statResult = this.handleStatProperty(expr); - if (statResult !== null) return statResult; - - // Handle TypeScript parameter property access return this.handleParameterPropertyAccess(expr, params); } From 3079351e0367e503dc479f641e4f1b29e4fceb0b Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:25:46 -0800 Subject: [PATCH 05/13] add sourceFile field to FunctionNode and ClassNode in AST and parsers --- src/ast/types.ts | 4 +++- src/native-compiler-lib.ts | 2 +- src/parser-native/transformer.ts | 18 +++++++++++------- src/parser-ts/transformer.ts | 4 ++++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/ast/types.ts b/src/ast/types.ts index 1fd5f9af..4042f7f5 100644 --- a/src/ast/types.ts +++ b/src/ast/types.ts @@ -343,6 +343,7 @@ export interface FunctionNode { async?: boolean; parameters?: FunctionParameter[]; loc?: SourceLocation; + sourceFile?: string; } export interface ClassMethod { @@ -367,9 +368,10 @@ export interface ClassNode { name: string; extends?: string; implements?: string[]; - fields: ClassField[]; // Explicit field declarations + fields: ClassField[]; methods: ClassMethod[]; loc?: SourceLocation; + sourceFile?: string; } export interface ImportSpecifier { diff --git a/src/native-compiler-lib.ts b/src/native-compiler-lib.ts index 8aadfbb5..7a611a30 100644 --- a/src/native-compiler-lib.ts +++ b/src/native-compiler-lib.ts @@ -220,7 +220,7 @@ export function compileMultiFile(entryFile: string, compiledFiles: string[]): AS if (verbose) { console.log('Parsing: ' + absPath); } const code = fs.readFileSync(absPath); const tree = parseSource(code); - const ast = transformTree(tree); + const ast = transformTree(tree, absPath); const mergedAST: AST = { imports: [], diff --git a/src/parser-native/transformer.ts b/src/parser-native/transformer.ts index 2cfc5450..81d2d852 100644 --- a/src/parser-native/transformer.ts +++ b/src/parser-native/transformer.ts @@ -52,11 +52,11 @@ function getExprType(expr: Expression | null | undefined): string { return (expr as ExprBase).type; } -export function transformTree(tree: TreeSitterTree): AST { - return transformProgram(tree.rootNode); +export function transformTree(tree: TreeSitterTree, sourceFile?: string): AST { + return transformProgram(tree.rootNode, sourceFile || ''); } -function transformProgram(node: TreeSitterNode): AST { +function transformProgram(node: TreeSitterNode, sourceFile: string): AST { const ast: AST = { imports: [], functions: [], @@ -79,14 +79,14 @@ function transformProgram(node: TreeSitterNode): AST { i = i + 1; continue; } - transformTopLevelNode(child, ast); + transformTopLevelNode(child, ast, sourceFile); i = i + 1; } return ast; } -function transformTopLevelNode(node: TreeSitterNode, ast: AST): void { +function transformTopLevelNode(node: TreeSitterNode, ast: AST, sourceFile: string): void { switch (node.type) { case 'import_statement': const importDecl = transformImportStatement(node); @@ -98,6 +98,7 @@ function transformTopLevelNode(node: TreeSitterNode, ast: AST): void { case 'function_declaration': const func = transformFunctionDeclaration(node); if (func) { + func.sourceFile = sourceFile; ast.functions.push(func); } break; @@ -105,6 +106,7 @@ function transformTopLevelNode(node: TreeSitterNode, ast: AST): void { case 'class_declaration': const cls = transformClassDeclaration(node); if (cls) { + cls.sourceFile = sourceFile; ast.classes.push(cls); } break; @@ -199,7 +201,7 @@ function transformTopLevelNode(node: TreeSitterNode, ast: AST): void { break; case 'export_statement': - handleExportStatement(node, ast); + handleExportStatement(node, ast, sourceFile); break; } } @@ -242,7 +244,7 @@ function handleExpressionStatement(node: TreeSitterNode, ast: AST): void { } } -function handleExportStatement(node: TreeSitterNode, ast: AST): void { +function handleExportStatement(node: TreeSitterNode, ast: AST, sourceFile: string): void { const nodeText = (node as NodeBase).text; const isTypeOnly = nodeText.startsWith('export type ') || nodeText.startsWith('export type{'); @@ -257,12 +259,14 @@ function handleExportStatement(node: TreeSitterNode, ast: AST): void { if (c.type === 'function_declaration') { const func = transformFunctionDeclaration(child); if (func) { + func.sourceFile = sourceFile; ast.functions.push(func); ast.exports.push({ type: 'export', declaration: func }); } } else if (c.type === 'class_declaration') { const cls = transformClassDeclaration(child); if (cls) { + cls.sourceFile = sourceFile; ast.classes.push(cls); ast.exports.push({ type: 'export', declaration: cls }); } diff --git a/src/parser-ts/transformer.ts b/src/parser-ts/transformer.ts index 75f49aa9..5c6d573a 100644 --- a/src/parser-ts/transformer.ts +++ b/src/parser-ts/transformer.ts @@ -74,6 +74,7 @@ function transformTopLevelStatement(node: ts.Statement, ast: AST, checker: ts.Ty } const func = transformFunctionDeclaration(funcDecl, checker); if (func) { + func.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.functions.push(func); } break; @@ -83,6 +84,7 @@ function transformTopLevelStatement(node: ts.Statement, ast: AST, checker: ts.Ty const classDecl = node as ts.ClassDeclaration; const cls = transformClassDeclaration(classDecl, checker); if (cls) { + cls.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.classes.push(cls); } break; @@ -234,11 +236,13 @@ function handleExportedDeclaration(node: ts.Statement, ast: AST, checker: ts.Typ if (ts.isFunctionDeclaration(node) && hasExportModifier(node)) { const func = transformFunctionDeclaration(node, checker); if (func) { + func.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.exports.push({ type: 'export', declaration: func }); } } else if (ts.isClassDeclaration(node) && hasExportModifier(node)) { const cls = transformClassDeclaration(node, checker); if (cls) { + cls.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.exports.push({ type: 'export', declaration: cls }); } } From ac893d9b0f0a08db04c892ba207ff4455f2782c3 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:28:40 -0800 Subject: [PATCH 06/13] build exportedNames set and add optional sourceFile param to mangleUserName --- .../infrastructure/function-generator.ts | 2 +- .../infrastructure/generator-context.ts | 4 +- src/codegen/llvm-generator.ts | 42 ++++++++++++++++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/codegen/infrastructure/function-generator.ts b/src/codegen/infrastructure/function-generator.ts index ca1f40b0..fb761537 100644 --- a/src/codegen/infrastructure/function-generator.ts +++ b/src/codegen/infrastructure/function-generator.ts @@ -50,7 +50,7 @@ export interface FunctionGeneratorContext { pushOutput(line: string): void; lastInstructionIsTerminator(): boolean; createEmptyStringConstant(): string; - mangleUserName(name: string): string; + mangleUserName(name: string, sourceFile?: string): string; getSubprogramDbgRef(): string; getUsesTestRunner(): boolean; ensureDouble(value: string): string; diff --git a/src/codegen/infrastructure/generator-context.ts b/src/codegen/infrastructure/generator-context.ts index 034fc72d..6d06f614 100644 --- a/src/codegen/infrastructure/generator-context.ts +++ b/src/codegen/infrastructure/generator-context.ts @@ -719,7 +719,7 @@ export interface IGeneratorContext { */ resolveImportAlias(localName: string): string; - mangleUserName(name: string): string; + mangleUserName(name: string, sourceFile?: string): string; /** * Access to class generator for field type lookups @@ -1570,7 +1570,7 @@ export class MockGeneratorContext implements IGeneratorContext { return value; } - mangleUserName(name: string): string { + mangleUserName(name: string, _sourceFile?: string): string { if (name.startsWith('__')) return name; return `_cs_${name}`; } diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 36463563..30795781 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -928,6 +928,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { private usesTreeSitter: boolean = false; public sourceCode: string = ''; public filename: string = ''; + private exportedNames: Set = new Set(); constructor(ast: AST, typeChecker: TypeChecker | null, options: LLVMGeneratorOptions) { super(); @@ -954,6 +955,16 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { this.ast = ast; + this.exportedNames = new Set(); + if (ast.exports) { + for (let ei = 0; ei < ast.exports.length; ei++) { + const exp = ast.exports[ei]; + if (exp && exp.declaration && exp.declaration.name) { + this.exportedNames.add(exp.declaration.name); + } + } + } + // Cache all counts BEFORE storing - empty arrays become garbage after assignment this.topLevelStatementsCount = ast.topLevelStatements.length; this.topLevelExpressionsCount = ast.topLevelExpressions.length; @@ -1182,11 +1193,40 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { return original || localName; } - mangleUserName(name: string): string { + mangleUserName(name: string, sourceFile?: string): string { if (name.startsWith('__')) return name; + if (sourceFile && sourceFile.length > 0 && !this.exportedNames.has(name)) { + const slug = this.fileSlug(sourceFile); + return `_cs_${slug}_${name}`; + } return `_cs_${name}`; } + private fileSlug(sourceFile: string): string { + let start = 0; + for (let i = 0; i < sourceFile.length; i++) { + if (sourceFile[i] === '/' || sourceFile[i] === '\\') { + start = i + 1; + } + } + let end = sourceFile.length; + const dotIdx = sourceFile.lastIndexOf('.'); + if (dotIdx > start) { + end = dotIdx; + } + let slug = sourceFile.substring(start, end); + let result = ''; + for (let i = 0; i < slug.length; i++) { + const ch = slug[i]; + if (ch === '-' || ch === '.') { + result += '_'; + } else { + result += ch; + } + } + return result; + } + createEmptyStringConstant(): string { return this.stringGen.doCreateStringConstant(''); } From 939f644eab6ffb3ad67046528a9d3ded82b0f5d6 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:32:12 -0800 Subject: [PATCH 07/13] thread sourceFile through function definition and call sites --- src/codegen/expressions/calls.ts | 6 ++++-- src/codegen/infrastructure/function-generator.ts | 2 +- src/parser-ts/transformer.ts | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index f34fbeb3..82599c6c 100644 --- a/src/codegen/expressions/calls.ts +++ b/src/codegen/expressions/calls.ts @@ -582,13 +582,15 @@ export class CallExpressionGenerator { } } + const calleeSourceFile = funcResult ? func.sourceFile : undefined; + if (returnType === 'void') { - this.ctx.emit(`call void @${this.ctx.mangleUserName(resolvedFuncName)}(${argsList.join(', ')})`); + this.ctx.emit(`call void @${this.ctx.mangleUserName(resolvedFuncName, calleeSourceFile)}(${argsList.join(', ')})`); return '0'; } const temp = this.ctx.nextTemp(); - this.ctx.emit(`${temp} = call ${returnType} @${this.ctx.mangleUserName(resolvedFuncName)}(${argsList.join(', ')})`); + this.ctx.emit(`${temp} = call ${returnType} @${this.ctx.mangleUserName(resolvedFuncName, calleeSourceFile)}(${argsList.join(', ')})`); this.ctx.setVariableType(temp, returnType); return temp; diff --git a/src/codegen/infrastructure/function-generator.ts b/src/codegen/infrastructure/function-generator.ts index fb761537..aeee12d0 100644 --- a/src/codegen/infrastructure/function-generator.ts +++ b/src/codegen/infrastructure/function-generator.ts @@ -200,7 +200,7 @@ export class FunctionGenerator { } } - let ir = `define ${returnType} @${this.ctx.mangleUserName(funcName)}(`; + let ir = `define ${returnType} @${this.ctx.mangleUserName(funcName, func.sourceFile)}(`; const paramStrings: string[] = []; if (hasClosure) { paramStrings.push('i8* %__env'); diff --git a/src/parser-ts/transformer.ts b/src/parser-ts/transformer.ts index 5c6d573a..75f49aa9 100644 --- a/src/parser-ts/transformer.ts +++ b/src/parser-ts/transformer.ts @@ -74,7 +74,6 @@ function transformTopLevelStatement(node: ts.Statement, ast: AST, checker: ts.Ty } const func = transformFunctionDeclaration(funcDecl, checker); if (func) { - func.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.functions.push(func); } break; @@ -84,7 +83,6 @@ function transformTopLevelStatement(node: ts.Statement, ast: AST, checker: ts.Ty const classDecl = node as ts.ClassDeclaration; const cls = transformClassDeclaration(classDecl, checker); if (cls) { - cls.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.classes.push(cls); } break; @@ -236,13 +234,11 @@ function handleExportedDeclaration(node: ts.Statement, ast: AST, checker: ts.Typ if (ts.isFunctionDeclaration(node) && hasExportModifier(node)) { const func = transformFunctionDeclaration(node, checker); if (func) { - func.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.exports.push({ type: 'export', declaration: func }); } } else if (ts.isClassDeclaration(node) && hasExportModifier(node)) { const cls = transformClassDeclaration(node, checker); if (cls) { - cls.sourceFile = currentSourceFile ? currentSourceFile.fileName : ''; ast.exports.push({ type: 'export', declaration: cls }); } } From 3068850793a31583107a81a6fcf698591d99668d Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:33:53 -0800 Subject: [PATCH 08/13] refactor handler registry to use dispatchHandlers method for stage 0 compat --- src/codegen/expressions/access/member.ts | 167 +++++++++++------------ 1 file changed, 77 insertions(+), 90 deletions(-) diff --git a/src/codegen/expressions/access/member.ts b/src/codegen/expressions/access/member.ts index 8cff0b53..1bfe6632 100644 --- a/src/codegen/expressions/access/member.ts +++ b/src/codegen/expressions/access/member.ts @@ -162,113 +162,102 @@ export type MemberAccessHandlerFn = (expr: MemberAccessNode, ctx: MemberAccessGe /** * MemberAccessGenerator * - * Handles property access expressions: - * - process.argv (special case) - * - Class instance properties (this.field, instance.field) - * - JSON object properties - * - Regular object properties - * - Array/String .length property - * - Map/Set .size property - * - TypeScript interface-based property access + * Handles property access expressions via a priority-ordered dispatch chain. + * Each handler returns string | null (null = didn't handle, try next). + * + * Handler order (priority): + * 1. Enum member access + * 2. Typed JSON struct access + * 3. process.argv / process.platform / process.env / process.simple + * 4. Class property access + * 5. JSON property access (variable flagged as JSON) + * 6. Chained access (member_access: interface, class field, nested JSON) + * 7. Index access (arr[i].property) + * 8. Type assertion access ((expr as Type).property) + * 9. Method call result access (map.get(key).property) + * 10. Object property access + * 11. .length property + * 12. .size property (Map/Set) + * 13. Response properties + * 14. Stat properties + * 15. Parameter property access (fallback) */ export class MemberAccessGenerator { - private handlers: MemberAccessHandlerFn[]; + constructor(private ctx: MemberAccessGeneratorContext) {} - constructor(private ctx: MemberAccessGeneratorContext) { - this.handlers = this.buildHandlers(); - } + private dispatchHandlers(expr: MemberAccessNode, params: string[]): string | null { + let result: string | null; - private buildHandlers(): MemberAccessHandlerFn[] { - const handlers: MemberAccessHandlerFn[] = []; + result = this.handleEnumMemberAccess(expr); + if (result !== null) return result; - handlers.push((expr, ctx, _params) => this.handleEnumMemberAccess(expr)); + result = this.handleTypedJsonStructAccess(expr); + if (result !== null) return result; - handlers.push((expr, ctx, _params) => this.handleTypedJsonStructAccess(expr)); + if (isProcessArgv(expr)) return this.handleProcessArgv(); - handlers.push((expr, ctx, _params) => { - if (isProcessArgv(expr)) return this.handleProcessArgv(); - return null; - }); + if (isProcessPlatform(expr)) { + const platformStr = this.ctx.getTargetOS() || process.platform; + return this.ctx.stringGen.doCreateStringConstant(platformStr); + } - handlers.push((expr, ctx, _params) => { - if (isProcessPlatform(expr)) { - const platformStr = ctx.getTargetOS() || process.platform; - return ctx.stringGen.doCreateStringConstant(platformStr); - } - return null; - }); + if (isProcessEnvAccess(expr)) return this.handleProcessEnvAccess(expr); - handlers.push((expr, ctx, _params) => { - if (isProcessEnvAccess(expr)) return this.handleProcessEnvAccess(expr); - return null; - }); + result = handleProcessSimpleProperty(this.ctx, expr); + if (result !== null) return result; - handlers.push((expr, ctx, _params) => handleProcessSimpleProperty(ctx, expr)); + result = this.handleClassPropertyAccess(expr, params); + if (result !== null) return result; - handlers.push((expr, ctx, params) => this.handleClassPropertyAccess(expr, params)); + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === null || exprObjType === undefined) return '0.0'; - handlers.push((expr, ctx, params) => { - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === null || exprObjType === undefined) return '0.0'; - if (exprObjType === 'variable' && ctx.symbolTable.isJSON((expr.object as VariableNode).name)) { - return this.handleJsonPropertyAccess(expr, params); - } - return null; - }); - - handlers.push((expr, ctx, params) => { - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === 'member_access') { - const chainedResult = this.handleChainedInterfaceAccess(expr, params); - if (chainedResult !== null) return chainedResult; - const classFieldChainResult = this.handleClassFieldChainedAccess(expr, params); - if (classFieldChainResult !== null) return classFieldChainResult; - const nestedResult = this.handleNestedJsonAccess(expr, params); - if (nestedResult !== null) return nestedResult; - } - return null; - }); + if (exprObjType === 'variable' && this.ctx.symbolTable.isJSON((expr.object as VariableNode).name)) { + return this.handleJsonPropertyAccess(expr, params); + } - handlers.push((expr, ctx, params) => { - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === 'index_access') return this.handleIndexAccessPropertyAccess(expr, params); - return null; - }); + if (exprObjType === 'member_access') { + result = this.handleChainedInterfaceAccess(expr, params); + if (result !== null) return result; + result = this.handleClassFieldChainedAccess(expr, params); + if (result !== null) return result; + result = this.handleNestedJsonAccess(expr, params); + if (result !== null) return result; + } - handlers.push((expr, ctx, params) => { - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === 'type_assertion') return this.handleTypeAssertionPropertyAccess(expr, params); - return null; - }); + if (exprObjType === 'index_access') { + result = this.handleIndexAccessPropertyAccess(expr, params); + if (result !== null) return result; + } - handlers.push((expr, ctx, params) => { - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === 'method_call') return this.handleMethodCallResultPropertyAccess(expr, params); - return null; - }); + if (exprObjType === 'type_assertion') { + result = this.handleTypeAssertionPropertyAccess(expr, params); + if (result !== null) return result; + } - handlers.push((expr, ctx, params) => this.handleObjectPropertyAccess(expr, params)); + if (exprObjType === 'method_call') { + result = this.handleMethodCallResultPropertyAccess(expr, params); + if (result !== null) return result; + } - handlers.push((expr, ctx, params) => { - if (expr.property === 'length') return this.handleLengthProperty(expr, params); - return null; - }); + result = this.handleObjectPropertyAccess(expr, params); + if (result !== null) return result; - handlers.push((expr, ctx, params) => { - if (expr.property === 'size') return this.handleSizeProperty(expr, params); - return null; - }); + if (expr.property === 'length') return this.handleLengthProperty(expr, params); + + if (expr.property === 'size') { + result = this.handleSizeProperty(expr, params); + if (result !== null) return result; + } - handlers.push((expr, ctx, _params) => this.handleResponseProperty(expr)); + result = this.handleResponseProperty(expr); + if (result !== null) return result; - handlers.push((expr, ctx, _params) => this.handleStatProperty(expr)); + result = this.handleStatProperty(expr); + if (result !== null) return result; - return handlers; + return null; } private hasObjectInfo(name: string): boolean { @@ -413,10 +402,8 @@ export class MemberAccessGenerator { return this.ctx.generateExpression(expr.object, params); } - for (let i = 0; i < this.handlers.length; i++) { - const result = this.handlers[i](expr, this.ctx, params); - if (result !== null) return result; - } + const result = this.dispatchHandlers(expr, params); + if (result !== null) return result; return this.handleParameterPropertyAccess(expr, params); } From 437b16b7dcf1dbcfd9b092a3c1b3b059b7f8264e Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 08:54:33 -0800 Subject: [PATCH 09/13] refactor canonicalTypeToLlvm to use plain parameters for stage 0 compat --- src/codegen/infrastructure/type-system.ts | 33 +++++++++-------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/codegen/infrastructure/type-system.ts b/src/codegen/infrastructure/type-system.ts index 640d97b5..a99b291a 100644 --- a/src/codegen/infrastructure/type-system.ts +++ b/src/codegen/infrastructure/type-system.ts @@ -122,7 +122,7 @@ export function createFloatType(): ResolvedType { } export function tsTypeToLlvm(tsType: string): string { - return canonicalTypeToLlvm(tsType, { mode: 'default' }); + return canonicalTypeToLlvm(tsType, 'default', false, false, ''); } export function resolvedTypeToLlvm(rt: ResolvedType): string { @@ -145,28 +145,21 @@ export function resolvedTypeToLlvm(rt: ResolvedType): string { export type TypeMappingMode = 'default' | 'param' | 'return' | 'struct_field' | 'json'; -export interface TypeMappingOptions { - mode: TypeMappingMode; - isEnum?: boolean; - isInterface?: boolean; - fieldName?: string; -} - -export function canonicalTypeToLlvm(tsType: string, options: TypeMappingOptions): string { +export function canonicalTypeToLlvm(tsType: string, mode: string, isEnum: boolean, isInterface: boolean, fieldName: string): string { if (tsType === null || tsType === undefined || tsType === '') { - if (options.mode === 'return') return 'double'; + if (mode === 'return') return 'double'; return 'i8*'; } - if (options.fieldName === 'nodePtr' || options.fieldName === 'treePtr') return 'i8*'; + if (fieldName === 'nodePtr' || fieldName === 'treePtr') return 'i8*'; - if (options.mode === 'param') { + if (mode === 'param') { if (tsType === 'any' || tsType === 'unknown') { throw new Error(`Parameter type '${tsType}' is not allowed — add explicit type annotations or fix the parser`); } } - if (options.isEnum) return 'double'; + if (isEnum) return 'double'; if (tsType === 'string') return 'i8*'; if (tsType === 'number' || tsType === 'boolean') return 'double'; @@ -184,20 +177,20 @@ export function canonicalTypeToLlvm(tsType: string, options: TypeMappingOptions) for (let i = 0; i < parts.length; i++) { const part = parts[i].trim(); if (part === 'null' || part === 'undefined') continue; - return canonicalTypeToLlvm(part, options); + return canonicalTypeToLlvm(part, mode, isEnum, isInterface, fieldName); } } - if (options.isInterface && (options.mode === 'param' || options.mode === 'struct_field')) { + if (isInterface && (mode === 'param' || mode === 'struct_field')) { return `%${tsType}*`; } - if (options.mode === 'return') { + if (mode === 'return') { if (tsType !== 'number' && tsType !== 'boolean') return 'i8*'; return 'double'; } - if (options.mode === 'json') { + if (mode === 'json') { return 'i8*'; } @@ -205,7 +198,7 @@ export function canonicalTypeToLlvm(tsType: string, options: TypeMappingOptions) } export function tsTypeToLlvmJson(tsType: string): string { - return canonicalTypeToLlvm(tsType, { mode: 'json' }); + return canonicalTypeToLlvm(tsType, 'json', false, false, ''); } export function checkUnsafeUnionType(typeStr: string): string | null { @@ -343,14 +336,14 @@ export function mapParamTypeToLLVM( paramIsEnum: boolean, paramIsInterface: boolean ): string { - return canonicalTypeToLlvm(paramType, { mode: 'param', isEnum: paramIsEnum, isInterface: paramIsInterface, fieldName: paramName }); + return canonicalTypeToLlvm(paramType, 'param', paramIsEnum, paramIsInterface, paramName); } export function mapReturnTypeToLLVM( returnType: string, returnIsEnum: boolean ): string { - return canonicalTypeToLlvm(returnType, { mode: 'return', isEnum: returnIsEnum }); + return canonicalTypeToLlvm(returnType, 'return', returnIsEnum, false, ''); } function parseGenericTypeString(s: string): { base: string; params: string } | null { From 81220466af6d213a69d75fdb17718445e8387fcb Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 15:18:10 -0800 Subject: [PATCH 10/13] update test scripts to use chad instead of chadc --- scripts/test.js | 10 +++++----- tests/compiler.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/test.js b/scripts/test.js index e20734aa..92573849 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -15,11 +15,11 @@ try { process.exit(1); } -const chadc = path.join(projectRoot, '.build', 'chadc'); -if (!fs.existsSync(chadc)) { - console.log('Building native compiler (.build/chadc)...'); +const chad = path.join(projectRoot, '.build', 'chad'); +if (!fs.existsSync(chad)) { + console.log('Building native compiler (.build/chad)...'); try { - execSync('node dist/chadc-node.js src/chadc-native.ts -o .build/chadc', { cwd: projectRoot, stdio: 'inherit' }); + execSync('node dist/chad-node.js build src/chad-native.ts -o .build/chad', { cwd: projectRoot, stdio: 'inherit' }); } catch (error) { console.error('Native compiler build failed'); process.exit(1); @@ -54,7 +54,7 @@ child.on('exit', (code) => { const child2 = spawn('node', ['--import', 'tsx', '--test', 'tests/compiler.test.ts'], { stdio: 'inherit', shell: false, - env: { ...process.env, CHADC_COMPILER: 'node dist/chadc-node.js' } + env: { ...process.env, CHAD_COMPILER: 'node dist/chad-node.js build' } }); child2.on('exit', (code2) => { process.exit(code2); diff --git a/tests/compiler.test.ts b/tests/compiler.test.ts index cf6caa64..e54b395a 100644 --- a/tests/compiler.test.ts +++ b/tests/compiler.test.ts @@ -10,8 +10,8 @@ import { testCases } from './test-fixtures'; const execAsync = promisify(exec); -const compiler = fsSync.existsSync('.build/chad') ? '.build/chad build' : 'node dist/chad-node.js build'; -const compilerLabel = fsSync.existsSync('.build/chad') ? 'native' : 'node'; +const compiler = process.env.CHAD_COMPILER || (fsSync.existsSync('.build/chad') ? '.build/chad build' : 'node dist/chad-node.js build'); +const compilerLabel = process.env.CHAD_COMPILER ? 'node' : (fsSync.existsSync('.build/chad') ? 'native' : 'node'); describe(`ChadScript Compiler (${compilerLabel})`, () => { describe('Compilation and Execution', { concurrency: 32 }, () => { From 6d118968f9c67c64b468334c543d30d6ea438784 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 10:09:46 -0800 Subject: [PATCH 11/13] split dispatchHandlers into 3-phase dispatch pipeline --- src/codegen/expressions/access/member.ts | 40 ++++++++++++++----- .../expressions/access/process-access.ts | 5 +++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/codegen/expressions/access/member.ts b/src/codegen/expressions/access/member.ts index 1bfe6632..e46320da 100644 --- a/src/codegen/expressions/access/member.ts +++ b/src/codegen/expressions/access/member.ts @@ -28,6 +28,7 @@ import { handleProcessEnvAccess, handleProcessSimpleProperty, handleProcessArgv, + handleProcessPlatform, } from './process-access.js'; import { handleLengthProperty, @@ -188,6 +189,25 @@ export class MemberAccessGenerator { private dispatchHandlers(expr: MemberAccessNode, params: string[]): string | null { let result: string | null; + result = this.dispatchSpecialValues(expr, params); + if (result !== null) return result; + + result = this.handleClassPropertyAccess(expr, params); + if (result !== null) return result; + + const exprObjBase = expr.object as ExprBase; + const exprObjType = exprObjBase ? exprObjBase.type : null; + if (exprObjType === null || exprObjType === undefined) return '0.0'; + + result = this.dispatchByExpressionType(expr, params, exprObjType); + if (result !== null) return result; + + return this.dispatchPropertyHandlers(expr, params); + } + + private dispatchSpecialValues(expr: MemberAccessNode, params: string[]): string | null { + let result: string | null; + result = this.handleEnumMemberAccess(expr); if (result !== null) return result; @@ -196,22 +216,18 @@ export class MemberAccessGenerator { if (isProcessArgv(expr)) return this.handleProcessArgv(); - if (isProcessPlatform(expr)) { - const platformStr = this.ctx.getTargetOS() || process.platform; - return this.ctx.stringGen.doCreateStringConstant(platformStr); - } + if (isProcessPlatform(expr)) return handleProcessPlatform(this.ctx); if (isProcessEnvAccess(expr)) return this.handleProcessEnvAccess(expr); result = handleProcessSimpleProperty(this.ctx, expr); if (result !== null) return result; - result = this.handleClassPropertyAccess(expr, params); - if (result !== null) return result; + return null; + } - const exprObjBase = expr.object as ExprBase; - const exprObjType = exprObjBase ? exprObjBase.type : null; - if (exprObjType === null || exprObjType === undefined) return '0.0'; + private dispatchByExpressionType(expr: MemberAccessNode, params: string[], exprObjType: string): string | null { + let result: string | null; if (exprObjType === 'variable' && this.ctx.symbolTable.isJSON((expr.object as VariableNode).name)) { return this.handleJsonPropertyAccess(expr, params); @@ -244,6 +260,12 @@ export class MemberAccessGenerator { result = this.handleObjectPropertyAccess(expr, params); if (result !== null) return result; + return null; + } + + private dispatchPropertyHandlers(expr: MemberAccessNode, params: string[]): string | null { + let result: string | null; + if (expr.property === 'length') return this.handleLengthProperty(expr, params); if (expr.property === 'size') { diff --git a/src/codegen/expressions/access/process-access.ts b/src/codegen/expressions/access/process-access.ts index 1441a229..101b9e9b 100644 --- a/src/codegen/expressions/access/process-access.ts +++ b/src/codegen/expressions/access/process-access.ts @@ -76,6 +76,11 @@ export function handleProcessSimpleProperty(ctx: MemberAccessGeneratorContext, e return null; } +export function handleProcessPlatform(ctx: MemberAccessGeneratorContext): string { + const platformStr = ctx.getTargetOS() || process.platform; + return ctx.stringGen.doCreateStringConstant(platformStr); +} + export function handleProcessArgv(ctx: MemberAccessGeneratorContext): string { const sizePtr = ctx.nextTemp(); ctx.emit(`${sizePtr} = getelementptr %StringArray, %StringArray* null, i32 1`); From b4b84ec0826d0c567419b530f9d20b6c02bca412 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 10:16:35 -0800 Subject: [PATCH 12/13] thread sourceFile to all remaining mangleUserName call sites --- src/codegen/expressions/calls.ts | 30 ++++++++++++++++++++++++------ src/codegen/llvm-generator.ts | 17 ++++++++++++++--- src/codegen/types/objects/class.ts | 17 +++++++++++------ 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index 82599c6c..7d16138a 100644 --- a/src/codegen/expressions/calls.ts +++ b/src/codegen/expressions/calls.ts @@ -26,6 +26,17 @@ export class CallExpressionGenerator { return null; } + private getClassSourceFile(className: string): string | undefined { + const ast = this.ctx.getAst(); + if (!ast || !ast.classes) return undefined; + for (let i = 0; i < ast.classes.length; i++) { + const c = ast.classes[i] as ClassNode; + if (!c || !c.name) continue; + if (c.name === className) return c.sourceFile; + } + return undefined; + } + /** * Generate function call expression * @param expr - Call expression node @@ -641,12 +652,13 @@ export class CallExpressionGenerator { return this.ctx.emitError('setTimeout() callback must be a function reference', expr.loc); } const callbackName = (callbackArg as VariableNode).name; + const callbackFunc = this.getFunctionFromAST(callbackName); const delayValue = this.ctx.generateExpression(expr.args[1], params); const dblDelay = this.ctx.ensureDouble(delayValue); const callbackPtr = this.ctx.nextTemp(); - this.ctx.emit(`${callbackPtr} = bitcast void ()* @${this.ctx.mangleUserName(callbackName)} to void ()*`); + this.ctx.emit(`${callbackPtr} = bitcast void ()* @${this.ctx.mangleUserName(callbackName, callbackFunc ? callbackFunc.sourceFile : undefined)} to void ()*`); const result = this.ctx.nextTemp(); this.ctx.emit(`${result} = call i8* @__setTimeout(void ()* ${callbackPtr}, double ${dblDelay})`); @@ -667,12 +679,13 @@ export class CallExpressionGenerator { return this.ctx.emitError('setInterval() callback must be a function reference', expr.loc); } const callbackName = (callbackArg as VariableNode).name; + const callbackFunc = this.getFunctionFromAST(callbackName); const intervalValue = this.ctx.generateExpression(expr.args[1], params); const dblInterval = this.ctx.ensureDouble(intervalValue); const callbackPtr = this.ctx.nextTemp(); - this.ctx.emit(`${callbackPtr} = bitcast void ()* @${this.ctx.mangleUserName(callbackName)} to void ()*`); + this.ctx.emit(`${callbackPtr} = bitcast void ()* @${this.ctx.mangleUserName(callbackName, callbackFunc ? callbackFunc.sourceFile : undefined)} to void ()*`); const result = this.ctx.nextTemp(); this.ctx.emit(`${result} = call i8* @__setInterval(void ()* ${callbackPtr}, double ${dblInterval})`); @@ -735,7 +748,9 @@ export class CallExpressionGenerator { let callbackFn: string; if (callbackArg.type === 'variable') { - callbackFn = this.ctx.mangleUserName((callbackArg as VariableNode).name); + const cbName = (callbackArg as VariableNode).name; + const cbFunc = this.getFunctionFromAST(cbName); + callbackFn = this.ctx.mangleUserName(cbName, cbFunc ? cbFunc.sourceFile : undefined); } else if (callbackArg.type === 'arrow_function') { callbackFn = this.ctx.generateExpression(callbackArg, params); } else { @@ -806,7 +821,9 @@ export class CallExpressionGenerator { let callbackFn: string; if (callbackArg.type === 'variable') { - callbackFn = this.ctx.mangleUserName((callbackArg as VariableNode).name); + const cbName = (callbackArg as VariableNode).name; + const cbFunc = this.getFunctionFromAST(cbName); + callbackFn = this.ctx.mangleUserName(cbName, cbFunc ? cbFunc.sourceFile : undefined); } else if (callbackArg.type === 'arrow_function') { callbackFn = this.ctx.generateExpression(callbackArg, params); } else { @@ -1041,11 +1058,12 @@ export class CallExpressionGenerator { argsWithTypesParts.push('i8* ' + argValues[ai]); } const argsWithTypes = argsWithTypesParts.join(', '); + const parentSourceFile = this.getClassSourceFile(parentClassName); const parentObj = this.ctx.nextTemp(); if (argValues.length === 0) { - this.ctx.emit(`${parentObj} = call ${parentStructType} @${this.ctx.mangleUserName(parentClassName)}_constructor()`); + this.ctx.emit(`${parentObj} = call ${parentStructType} @${this.ctx.mangleUserName(parentClassName, parentSourceFile)}_constructor()`); } else { - this.ctx.emit(`${parentObj} = call ${parentStructType} @${this.ctx.mangleUserName(parentClassName)}_constructor(${argsWithTypes})`); + this.ctx.emit(`${parentObj} = call ${parentStructType} @${this.ctx.mangleUserName(parentClassName, parentSourceFile)}_constructor(${argsWithTypes})`); } const parentFields = this.ctx.classGenGetClassFields(parentClassName); diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 30795781..cde6da46 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -1227,6 +1227,15 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { return result; } + private getFunctionSourceFile(name: string): string | undefined { + if (!this.ast || !this.ast.functions) return undefined; + for (let i = 0; i < this.ast.functions.length; i++) { + const fn = this.ast.functions[i] as { name: string; sourceFile?: string }; + if (fn.name === name) return fn.sourceFile; + } + return undefined; + } + createEmptyStringConstant(): string { return this.stringGen.doCreateStringConstant(''); } @@ -1762,8 +1771,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { if (this.httpHandlers.length > 0) { irParts.push('\n'); const wsHandler = this.wsHandlers.length > 0 ? this.wsHandlers[0] : undefined; - const mangledHttpHandler = this.mangleUserName(this.httpHandlers[0]); - const mangledWsHandler = wsHandler ? this.mangleUserName(wsHandler) : undefined; + const httpHandlerSourceFile = this.getFunctionSourceFile(this.httpHandlers[0]); + const mangledHttpHandler = this.mangleUserName(this.httpHandlers[0], httpHandlerSourceFile); + const wsHandlerSourceFile = wsHandler ? this.getFunctionSourceFile(wsHandler) : undefined; + const mangledWsHandler = wsHandler ? this.mangleUserName(wsHandler, wsHandlerSourceFile) : undefined; const httpServe = this.httpServerGen.generateHttpServeFunction(mangledWsHandler); if (httpServe) { irParts.push(httpServe); } irParts.push('\n'); @@ -2682,7 +2693,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { // Call the runtime http_serve function // Handler now takes a single Request object (i8*) and returns Response object (i8*) const temp = this.nextTemp(); - this.emit(`${temp} = call i32 @http_serve(i32 ${portI32}, i8* (i8*)* @${this.mangleUserName(handlerName)})`); + this.emit(`${temp} = call i32 @http_serve(i32 ${portI32}, i8* (i8*)* @${this.mangleUserName(handlerName, this.getFunctionSourceFile(handlerName))})`); return temp; } diff --git a/src/codegen/types/objects/class.ts b/src/codegen/types/objects/class.ts index 32500cbe..6f4a76b6 100644 --- a/src/codegen/types/objects/class.ts +++ b/src/codegen/types/objects/class.ts @@ -29,6 +29,11 @@ export class ClassGenerator { return null; } + private getClassSourceFile(className: string): string | undefined { + const classNode = this.findClassNode(className); + return classNode ? classNode.sourceFile : undefined; + } + private getAllFieldsIncludingInherited(classNode: ClassNode): ClassField[] { const allFields: ClassField[] = []; if (classNode.extends) { @@ -393,7 +398,7 @@ export class ClassGenerator { const fieldsFromMap = this.classFields.get(className); const fields = fieldsFromMap || []; const structType = `%${className}_struct*`; - let ir = `define ${structType} @${this.ctx.mangleUserName(className)}_constructor(`; + let ir = `define ${structType} @${this.ctx.mangleUserName(className, this.getClassSourceFile(className))}_constructor(`; const paramLLVMTypes: string[] = []; let paramTsTypes: string[]; if (constructor.paramTypes) { @@ -584,7 +589,7 @@ export class ClassGenerator { private generateDefaultConstructorFromTypes(className: string, fieldLlvmTypes: string[], classFields?: ClassField[]): string { const structType = `%${className}_struct*`; - let ir = `define ${structType} @${this.ctx.mangleUserName(className)}_constructor() {` + '\n'; + let ir = `define ${structType} @${this.ctx.mangleUserName(className, this.getClassSourceFile(className))}_constructor() {` + '\n'; ir += 'entry:\n'; this.ctx.clearOutput(); @@ -671,7 +676,7 @@ export class ClassGenerator { } const thisType = `%${className}_struct*`; - let ir = `define ${returnLLVMType} @${this.ctx.mangleUserName(className)}_${method.name}(${thisType} %this`; + let ir = `define ${returnLLVMType} @${this.ctx.mangleUserName(className, this.getClassSourceFile(className))}_${method.name}(${thisType} %this`; const paramLLVMTypes: string[] = []; const paramTsTypes: string[] = method.paramTypes || []; @@ -866,7 +871,7 @@ export class ClassGenerator { const returnType = `%${className}_struct*`; const instance = this.nextTemp(); - this.emit(`${instance} = call ${returnType} @${this.ctx.mangleUserName(className)}_constructor(${argValues})`); + this.emit(`${instance} = call ${returnType} @${this.ctx.mangleUserName(className, this.getClassSourceFile(className))}_constructor(${argValues})`); return instance; } @@ -952,11 +957,11 @@ export class ClassGenerator { if (returnLLVMType === 'void') { // Void methods don't return a value - this.emit(`call void @${this.ctx.mangleUserName(methodOwnerClass)}_${methodName}(${thisType} ${actualInstancePtr}${argList})`); + this.emit(`call void @${this.ctx.mangleUserName(methodOwnerClass, this.getClassSourceFile(methodOwnerClass))}_${methodName}(${thisType} ${actualInstancePtr}${argList})`); return '0'; // Return dummy value for void calls } else { const result = this.nextTemp(); - this.emit(`${result} = call ${returnLLVMType} @${this.ctx.mangleUserName(methodOwnerClass)}_${methodName}(${thisType} ${actualInstancePtr}${argList})`); + this.emit(`${result} = call ${returnLLVMType} @${this.ctx.mangleUserName(methodOwnerClass, this.getClassSourceFile(methodOwnerClass))}_${methodName}(${thisType} ${actualInstancePtr}${argList})`); this.ctx.setVariableType(result, returnLLVMType); return result; } From 99c0fefd044757abe187b614af797da08a80ddc4 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 19 Feb 2026 10:23:45 -0800 Subject: [PATCH 13/13] eliminate wrapper methods around canonicalTypeToLlvm --- src/codegen/expressions/access/member.ts | 36 +++++++------------ .../infrastructure/function-generator.ts | 23 ++---------- .../type-resolver/type-resolver.ts | 21 +++-------- .../types/interface-struct-generator.ts | 16 +++++---- src/codegen/types/objects/class.ts | 7 ++-- 5 files changed, 32 insertions(+), 71 deletions(-) diff --git a/src/codegen/expressions/access/member.ts b/src/codegen/expressions/access/member.ts index e46320da..f73a3cf7 100644 --- a/src/codegen/expressions/access/member.ts +++ b/src/codegen/expressions/access/member.ts @@ -19,7 +19,7 @@ import { import type { SymbolTable } from '../../infrastructure/symbol-table.js'; import type { TypeChecker } from '../../../typescript/type-checker.js'; import type { InterfaceStructGenerator, InterfaceFieldInfo, InterfaceStructInfo } from '../../types/interface-struct-generator.js'; -import { stripOptional, stripNullable, tsTypeToLlvm, parseMapTypeString } from '../../infrastructure/type-system.js'; +import { stripOptional, stripNullable, tsTypeToLlvm, parseMapTypeString, canonicalTypeToLlvm } from '../../infrastructure/type-system.js'; import type { IStringGenerator, IMapGenerator, ISetGenerator, IResponseGenerator } from '../../infrastructure/generator-context.js'; import { isProcessArgv, @@ -635,7 +635,7 @@ export class MemberAccessGenerator { for (let pi = 0; pi < nestedProps.length; pi++) { const p = nestedProps[pi] as { name: string; type: string }; keys.push(stripOptional(p.name)); - types.push(this.convertTsType(p.type)); + types.push(tsTypeToLlvm(p.type)); tsTypes.push(p.type); } this.ctx.setJsonObjectMetadata(value, { keys, types, tsTypes, interfaceType: undefined }); @@ -913,7 +913,7 @@ export class MemberAccessGenerator { const f = interfaceDef.fields[i] as { name: string; type: string }; keys.push(stripOptional(f.name)); tsTypes.push(f.type); - types.push(this.convertTsType(f.type)); + types.push(tsTypeToLlvm(f.type)); } } this.ctx.setJsonObjectMetadata(register, { keys, types, tsTypes, interfaceType: undefined }); @@ -938,7 +938,7 @@ export class MemberAccessGenerator { const f = inlineFields[i] as InterfaceField; keys.push(f.name); tsTypes.push(f.type); - types.push(this.convertTsType(f.type)); + types.push(tsTypeToLlvm(f.type)); } this.ctx.setJsonObjectMetadata(register, { keys, types, tsTypes, interfaceType: undefined }); } @@ -954,21 +954,11 @@ export class MemberAccessGenerator { return handleNestedInterfaceField(this.ctx, fieldItem, tsType); } - private convertTsType(t: string): string { - return tsTypeToLlvm(t); - } - private interfaceTsTypeToLlvm(t: string): string { - if (t === 'string') return 'i8*'; - if (t === 'number') return 'double'; - if (t === 'boolean') return 'double'; - if (t === 'string[]') return '%StringArray*'; - if (t === 'number[]' || t === 'boolean[]') return '%Array*'; - if (t.endsWith('[]')) return '%ObjectArray*'; const baseName = this.extractBaseTypeName(t); const props = this.ctx.getInterfaceProperties(baseName); - if (props && props.keys.length > 0) return `%${baseName}*`; - return 'i8*'; + const isInterface = props !== null && props.keys.length > 0; + return canonicalTypeToLlvm(t, 'struct_field', false, isInterface, ''); } private extractJsonFieldValue(fieldItem: string): string { @@ -1333,7 +1323,7 @@ export class MemberAccessGenerator { const innerPtr = this.ctx.generateExpression(expr.object, params); const propField = ifInfoProps[propIndex] as InterfaceProperty; const propType = propField.type; - const llvmType = this.convertTsType(propType); + const llvmType = tsTypeToLlvm(propType); const fieldPtr = this.ctx.nextTemp(); this.ctx.emit(`${fieldPtr} = getelementptr inbounds %${fieldInfo.tsType}, %${fieldInfo.tsType}* ${innerPtr}, i32 0, i32 ${propIndex}`); @@ -1375,7 +1365,7 @@ export class MemberAccessGenerator { if (propIndex === -1) return null; const innerPtr = this.ctx.generateExpression(expr.object, params); - const llvmType = this.convertTsType(propTsType || 'i8*'); + const llvmType = tsTypeToLlvm(propTsType || 'i8*'); const fieldPtr = this.ctx.nextTemp(); this.ctx.emit(`${fieldPtr} = getelementptr inbounds %${fieldInfo.tsType}, %${fieldInfo.tsType}* ${innerPtr}, i32 0, i32 ${propIndex}`); @@ -1629,7 +1619,7 @@ export class MemberAccessGenerator { const p = taProps[i] as InterfaceProperty; keys.push(stripOptional(p.name)); tsTypes.push(p.type); - types.push(this.convertTsType(p.type)); + types.push(tsTypeToLlvm(p.type)); } return { keys, types, tsTypes }; } @@ -1997,11 +1987,11 @@ export class MemberAccessGenerator { } const propField = interfaceDef.fields[propIndex] as { name: string; type: string }; - const propType = this.convertTsType(propField.type); + const propType = tsTypeToLlvm(propField.type); const structTypes: string[] = []; for (let i = 0; i < interfaceDef.fields.length; i++) { const field = interfaceDef.fields[i] as { name: string; type: string }; - structTypes.push(this.convertTsType(field.type)); + structTypes.push(tsTypeToLlvm(field.type)); } const structType = `{ ${structTypes.join(', ')} }`; @@ -2501,12 +2491,12 @@ export class MemberAccessGenerator { if (fieldIndex === -1) return null; const field = fields[fieldIndex] as { name: string; type: string }; - const fieldLlvmType = this.convertTsType(field.type); + const fieldLlvmType = tsTypeToLlvm(field.type); const types: string[] = []; for (let i = 0; i < fields.length; i++) { const f = fields[i] as { name: string; type: string }; - types.push(this.convertTsType(f.type)); + types.push(tsTypeToLlvm(f.type)); } const structType = `{ ${types.join(', ')} }`; diff --git a/src/codegen/infrastructure/function-generator.ts b/src/codegen/infrastructure/function-generator.ts index aeee12d0..dc10afa1 100644 --- a/src/codegen/infrastructure/function-generator.ts +++ b/src/codegen/infrastructure/function-generator.ts @@ -5,7 +5,7 @@ import type { TypeChecker } from '../../typescript/type-checker.js'; import type { StringGenerator } from '../types/collections/string.js'; import type { ControlFlowGenerator } from '../statements/control-flow.js'; import type { InterfaceStructGenerator } from '../types/interface-struct-generator.js'; -import { stripOptional, tsTypeToLlvm, mapParamTypeToLLVM } from './type-system.js'; +import { stripOptional, tsTypeToLlvm, mapParamTypeToLLVM, canonicalTypeToLlvm } from './type-system.js'; import { findI64EligibleVariables } from './integer-analysis.js'; interface LiftedFunction extends FunctionNode { @@ -271,7 +271,7 @@ export class FunctionGenerator { if (!field || !field.name) continue; const fieldName = stripOptional(field.name); keys.push(fieldName); - types.push(this.convertTsTypeForField(fieldName, field.type)); + types.push(canonicalTypeToLlvm(field.type, 'default', this.ctx.isEnumType(field.type), false, fieldName)); } this.ctx.defineVariableWithMetadata(paramName, allocaReg, 'i8*', SymbolKind.Object, 'local', createObjectMetadataWithInterface({ keys, types }, paramTypes[i])); } else if (typeAliasCommonProps && typeAliasCommonProps.keys.length > 0) { @@ -571,23 +571,6 @@ export class FunctionGenerator { return this.ctx.isEnumType(typeName); } - private convertTsType(tsType: string): string { - if (this.isEnumType(tsType)) { - return 'double'; - } - return tsTypeToLlvm(tsType); - } - - private convertTsTypeForField(fieldName: string, tsType: string): string { - if (fieldName === 'nodePtr' || fieldName === 'treePtr') { - return 'i8*'; - } - if (this.isEnumType(tsType)) { - return 'double'; - } - return tsTypeToLlvm(tsType); - } - private llvmTypeToSymbolKind(llvmType: string): number { if (llvmType === 'double') return SymbolKind.Number; if (llvmType === 'i8*') return SymbolKind.String; @@ -659,7 +642,7 @@ export class FunctionGenerator { for (let i = 0; i < commonFields.length; i++) { const cf = commonFields[i] as CommonField; keys.push(stripOptional(cf.name)); - types.push(this.convertTsType(cf.type)); + types.push(canonicalTypeToLlvm(cf.type, 'default', this.ctx.isEnumType(cf.type), false, '')); } return { keys, types }; diff --git a/src/codegen/infrastructure/type-resolver/type-resolver.ts b/src/codegen/infrastructure/type-resolver/type-resolver.ts index 5ebbd28c..095c58ed 100644 --- a/src/codegen/infrastructure/type-resolver/type-resolver.ts +++ b/src/codegen/infrastructure/type-resolver/type-resolver.ts @@ -2,7 +2,7 @@ import { AST, InterfaceDeclaration, InterfaceField, TypeAliasDeclaration, Expres import { SymbolTable, ObjectMetadata, SymbolKind, Symbol as SymbolEntry, MapMetadata, ObjectArrayMetadata } from '../symbol-table.js'; import type { TypeChecker } from '../../../typescript/type-checker.js'; import { FieldInfo, MapTypeInfo, SetTypeInfo, TypeGuardInfo, UnionCommonFields, ThisFieldMapInfo, ThisFieldSetInfo } from './types.js'; -import { ResolvedType, createResolvedType, parseTypeString, stripOptional, tsTypeToLlvm, tsTypeToLlvmJson, parseMapTypeString, parseSetTypeString, parseArrayTypeString } from '../type-system.js'; +import { ResolvedType, createResolvedType, parseTypeString, stripOptional, parseMapTypeString, parseSetTypeString, parseArrayTypeString, canonicalTypeToLlvm } from '../type-system.js'; interface ExprBase { type: string; } @@ -337,7 +337,7 @@ export class TypeResolver { for (let i = 0; i < builtinType.fields.length; i++) { const f = builtinType.fields[i]; keys.push(stripOptional(f.name)); - types.push(this.convertTsType(f.type)); + types.push(canonicalTypeToLlvm(f.type, 'default', this.isEnumType(f.type), false, '')); tsTypes.push(f.type); } return { keys, types, tsTypes }; @@ -351,7 +351,7 @@ export class TypeResolver { for (let i = 0; i < iface.fields.length; i++) { const f = iface.fields[i] as { name: string; type: string }; keys.push(stripOptional(f.name)); - types.push(this.convertTsType(f.type)); + types.push(canonicalTypeToLlvm(f.type, 'default', this.isEnumType(f.type), false, '')); tsTypes.push(f.type); } return { keys, types, tsTypes }; @@ -548,23 +548,12 @@ export class TypeResolver { for (let i = 0; i < commonFields.length; i++) { const f = commonFields[i] as CommonField; keys.push(stripOptional(f.name)); - types.push(this.convertTsType(f.type)); + types.push(canonicalTypeToLlvm(f.type, 'default', this.isEnumType(f.type), false, '')); tsTypes.push(f.type); } return { keys, types, tsTypes }; } - convertTsType(tsType: string): string { - if (this.isEnumType(tsType)) { - return 'double'; - } - return tsTypeToLlvm(tsType); - } - - convertTsTypeJson(tsType: string): string { - return tsTypeToLlvmJson(tsType); - } - getClassFieldInfo(className: string, fieldName: string): FieldInfo | null { return this.ctx.classGenGetFieldInfo(className, fieldName); } @@ -584,7 +573,7 @@ export class TypeResolver { keyType, valueType, llvmKeyType: keyType === 'string' ? 'i8*' : 'double', - llvmValueType: this.convertTsType(valueType) + llvmValueType: canonicalTypeToLlvm(valueType, 'default', this.isEnumType(valueType), false, '') }; } diff --git a/src/codegen/types/interface-struct-generator.ts b/src/codegen/types/interface-struct-generator.ts index 140b55eb..23afc12b 100644 --- a/src/codegen/types/interface-struct-generator.ts +++ b/src/codegen/types/interface-struct-generator.ts @@ -1,5 +1,5 @@ import { InterfaceDeclaration } from '../../ast/types.js'; -import { tsTypeToLlvm } from '../infrastructure/type-system.js'; +import { canonicalTypeToLlvm } from '../infrastructure/type-system.js'; const BUILTIN_TYPES = [ 'Array', 'StringArray', 'Map', 'StringMap', 'Set', 'StringSet', @@ -146,10 +146,14 @@ export class InterfaceStructGenerator { } private tsTypeToLlvmForField(fieldName: string, tsType: string): string { - if (fieldName === 'nodePtr' || fieldName === 'treePtr') { + if (tsType === null || tsType === undefined || tsType === '') { return 'i8*'; } - return this.tsTypeToLlvm(tsType); + if (this.interfaceStructs.has(tsType)) { + return 'i8*'; + } + const isEnum = this.enumNames !== null && this.enumNames !== undefined && this.enumNames.has(tsType); + return canonicalTypeToLlvm(tsType, 'default', isEnum, false, fieldName); } private tsTypeToLlvm(tsType: string): string { @@ -159,10 +163,8 @@ export class InterfaceStructGenerator { if (this.interfaceStructs.has(tsType)) { return 'i8*'; } - if (this.enumNames !== null && this.enumNames !== undefined && this.enumNames.has(tsType)) { - return 'double'; - } - return tsTypeToLlvm(tsType); + const isEnum = this.enumNames !== null && this.enumNames !== undefined && this.enumNames.has(tsType); + return canonicalTypeToLlvm(tsType, 'default', isEnum, false, ''); } getInterfaceStruct(name: string): InterfaceStructInfo | undefined { diff --git a/src/codegen/types/objects/class.ts b/src/codegen/types/objects/class.ts index 6f4a76b6..7616507e 100644 --- a/src/codegen/types/objects/class.ts +++ b/src/codegen/types/objects/class.ts @@ -1,7 +1,7 @@ import { Expression, ClassNode, ClassMethod, ClassField, VariableNode, InterfaceDeclaration, CommonField } from '../../../ast/types.js'; import { IGeneratorContext } from '../../infrastructure/generator-context.js'; import { SymbolKind, createObjectMetadata, createObjectMetadataWithInterfaceAndPointerAlloca, createClassMetadata } from '../../infrastructure/symbol-table.js'; -import { stripOptional, tsTypeToLlvm } from '../../infrastructure/type-system.js'; +import { stripOptional, canonicalTypeToLlvm } from '../../infrastructure/type-system.js'; // ============================================ // CLASS GENERATOR - Class and instance operations @@ -971,10 +971,7 @@ export class ClassGenerator { if (!tsType || tsType.length === 0) { return 'double'; } - if (this.isEnumType(tsType)) { - return 'double'; - } - return tsTypeToLlvm(tsType); + return canonicalTypeToLlvm(tsType, 'default', this.isEnumType(tsType), false, ''); } private isEnumType(typeName: string): boolean {