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/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/codegen/expressions/access/member.ts b/src/codegen/expressions/access/member.ts index 236df401..1bfe6632 100644 --- a/src/codegen/expressions/access/member.ts +++ b/src/codegen/expressions/access/member.ts @@ -157,21 +157,109 @@ export interface MemberAccessGeneratorContext { getTargetArch(): string; } +export type MemberAccessHandlerFn = (expr: MemberAccessNode, ctx: MemberAccessGeneratorContext, params: string[]) => string | null; + /** * 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 { constructor(private ctx: MemberAccessGeneratorContext) {} + private dispatchHandlers(expr: MemberAccessNode, params: string[]): string | null { + let result: string | null; + + result = this.handleEnumMemberAccess(expr); + if (result !== null) return result; + + result = this.handleTypedJsonStructAccess(expr); + if (result !== null) return result; + + if (isProcessArgv(expr)) return this.handleProcessArgv(); + + if (isProcessPlatform(expr)) { + const platformStr = this.ctx.getTargetOS() || process.platform; + return this.ctx.stringGen.doCreateStringConstant(platformStr); + } + + 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; + + 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); + } + + 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; + } + + if (exprObjType === 'index_access') { + result = this.handleIndexAccessPropertyAccess(expr, params); + if (result !== null) return result; + } + + if (exprObjType === 'type_assertion') { + result = this.handleTypeAssertionPropertyAccess(expr, params); + if (result !== null) return result; + } + + if (exprObjType === 'method_call') { + result = this.handleMethodCallResultPropertyAccess(expr, params); + if (result !== null) return result; + } + + result = this.handleObjectPropertyAccess(expr, params); + if (result !== null) return result; + + if (expr.property === 'length') return this.handleLengthProperty(expr, params); + + if (expr.property === 'size') { + result = this.handleSizeProperty(expr, params); + if (result !== null) return result; + } + + result = this.handleResponseProperty(expr); + if (result !== null) return result; + + result = this.handleStatProperty(expr); + if (result !== null) return result; + + return null; + } + private hasObjectInfo(name: string): boolean { if (!this.ctx.symbolTable.isObject(name) && !this.ctx.symbolTable.isJSON(name)) return false; return this.ctx.symbolTable.getObjectMetadataKeys(name) !== undefined; @@ -314,94 +402,9 @@ 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; - } - - // Handle Response properties - const responseResult = this.handleResponseProperty(expr); - if (responseResult !== null) return responseResult; - - const statResult = this.handleStatProperty(expr); - if (statResult !== null) return statResult; + const result = this.dispatchHandlers(expr, params); + if (result !== null) return result; - // Handle TypeScript parameter property access return this.handleParameterPropertyAccess(expr, params); } diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index 513049c3..82599c6c 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); @@ -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 ca1f40b0..aeee12d0 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; @@ -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/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/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/src/codegen/infrastructure/type-system.ts b/src/codegen/infrastructure/type-system.ts index 2ee416e5..a99b291a 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, 'default', false, false, ''); } export function resolvedTypeToLlvm(rt: ResolvedType): string { @@ -163,16 +143,64 @@ 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 function canonicalTypeToLlvm(tsType: string, mode: string, isEnum: boolean, isInterface: boolean, fieldName: string): string { + if (tsType === null || tsType === undefined || tsType === '') { + if (mode === 'return') return 'double'; + return 'i8*'; + } + + if (fieldName === 'nodePtr' || fieldName === 'treePtr') return 'i8*'; + + 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 (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, mode, isEnum, isInterface, fieldName); + } + } + + if (isInterface && (mode === 'param' || mode === 'struct_field')) { + return `%${tsType}*`; + } + + if (mode === 'return') { + if (tsType !== 'number' && tsType !== 'boolean') return 'i8*'; + return 'double'; + } + + if (mode === 'json') { + return 'i8*'; + } + return 'i8*'; } +export function tsTypeToLlvmJson(tsType: string): string { + return canonicalTypeToLlvm(tsType, 'json', false, false, ''); +} + export function checkUnsafeUnionType(typeStr: string): string | null { if (!typeStr || typeStr.indexOf(' | ') === -1) return null; @@ -308,35 +336,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, 'param', paramIsEnum, paramIsInterface, 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, 'return', returnIsEnum, false, ''); } function parseGenericTypeString(s: string): { base: string; params: string } | null { 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(''); } 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/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 }, () => { 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', () => {