Skip to content

Commit e34e621

Browse files
committed
feat: Add TNew handling and generic type support in reflaxe
- Add TNew expression handling to compile object instantiation (e.g., new StringMap<Int>() -> $StringMap$new()) - Call addTypeForCompilation() to request class compilation when TNew is encountered - Add Generic and Pointer type variants to AST.ZType for proper type representation - Transform instance method calls with proper function types (e.g., map.set(k,v) -> $StringMap$set(map, k, v)) - Add buildInstanceMethodType() and buildConstructorType() helpers - Handle Null<T> abstract type by unwrapping to inner type - Update Generator.hx and HirBuilder.hx to handle new type variants - Add MapTest.hx demonstrating StringMap<Int> and IntMap<String> usage
1 parent 21b3694 commit e34e621

6 files changed

Lines changed: 180 additions & 32 deletions

File tree

reflaxe.zyntax/src/zyntax/AST.hx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ typedef Param = {
4848
enum ZType {
4949
Primitive(name: String);
5050
Function(params: Array<ZType>, returnType: ZType);
51+
Generic(name: String, typeParams: Array<ZType>); // e.g., StringMap<Int>, Array<String>
52+
Pointer(inner: ZType); // Pointer to a type (for class instances)
5153
}
5254

5355
/**

reflaxe.zyntax/src/zyntax/Generator.hx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ function generateType(t: AST.ZType): Dynamic {
133133
nullability: "NonNull"
134134
}
135135
};
136+
case Generic(name, typeParams): {
137+
Generic: {
138+
name: name,
139+
type_params: typeParams.map(p -> generateType(p))
140+
}
141+
};
142+
case Pointer(inner): {
143+
Pointer: generateType(inner)
144+
};
136145
}
137146
}
138147

reflaxe.zyntax/src/zyntax/HirBuilder.hx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,12 @@ class HirBuilder {
381381
// For function types, we'd need a proper representation
382382
// For now, just use a pointer
383383
HirType.Ptr(HirType.I8);
384+
case zyntax.AST.ZType.Generic(name, typeParams):
385+
// Generic types like StringMap<Int> - treat as opaque pointer
386+
HirType.Ptr(HirType.I8);
387+
case zyntax.AST.ZType.Pointer(inner):
388+
// Pointer to a type
389+
HirType.Ptr(convertType(inner));
384390
case zyntax.AST.ZType.Primitive(_):
385391
trace("Unknown primitive type: " + ty);
386392
HirType.Void;

reflaxe.zyntax/src/zyntax/ZyntaxCompiler.hx

Lines changed: 125 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -153,36 +153,55 @@ class ZyntaxCompiler extends GenericCompiler<AST.Module, AST.Enum, AST.Expr> {
153153
// Check if this is a method call on a standard library type
154154
switch(callee.expr) {
155155
case TField(obj, fa):
156-
// Get the method name
157-
var methodName = switch(fa) {
158-
case FInstance(_, _, cf): cf.get().name;
159-
case FAnon(cf): cf.get().name;
160-
case FClosure(_, cf): cf.get().name;
161-
default: null;
162-
};
163-
164-
// Get the object type
165-
var objType = switch(obj.t) {
166-
case TInst(t, _): t.get().name;
167-
default: null;
168-
};
169-
170-
// Transform stdlib method calls: arr.push(x) -> $Array$push(arr, x)
171-
if (methodName != null && objType != null) {
172-
var stdlibFunc = "$" + objType + "$" + methodName; // e.g., "$Array$push"
173-
var stdlibCallee: AST.ExprWithPos = {
174-
expr: AST.Expr.Variable(stdlibFunc),
175-
ty: convertType(expr.t),
176-
pos: callee.pos
177-
};
178-
var objExpr = wrapExpr(obj);
179-
var argExprs = args.map(a -> wrapExpr(a));
180-
AST.Expr.Call(stdlibCallee, [objExpr].concat(argExprs));
181-
} else {
182-
// Not a stdlib method call, use regular call
183-
var calleeExpr = wrapExpr(callee);
184-
var argExprs = args.map(a -> wrapExpr(a));
185-
AST.Expr.Call(calleeExpr, argExprs);
156+
// Handle static method calls (like Math.sqrt)
157+
switch(fa) {
158+
case FStatic(classRef, cf):
159+
// Static method call: Math.sqrt(x) -> $Math$sqrt(x)
160+
var className = classRef.get().name;
161+
var methodName = cf.get().name;
162+
var stdlibFunc = "$" + className + "$" + methodName;
163+
// Use the callee's type (function type) not the call result type
164+
var stdlibCallee: AST.ExprWithPos = {
165+
expr: AST.Expr.Variable(stdlibFunc),
166+
ty: convertType(callee.t),
167+
pos: callee.pos
168+
};
169+
var argExprs = args.map(a -> wrapExpr(a));
170+
AST.Expr.Call(stdlibCallee, argExprs);
171+
172+
case FInstance(_, _, cf) | FAnon(cf) | FClosure(_, cf):
173+
// Instance method call: arr.push(x) -> $Array$push(arr, x)
174+
var methodName = cf.get().name;
175+
var objType = switch(obj.t) {
176+
case TInst(t, _): t.get().name;
177+
default: null;
178+
};
179+
180+
if (methodName != null && objType != null) {
181+
var stdlibFunc = "$" + objType + "$" + methodName;
182+
// Build function type: (self, original_args...) -> return_type
183+
// We need to prepend the object type to the function params
184+
var funcType = buildInstanceMethodType(obj.t, callee.t);
185+
var stdlibCallee: AST.ExprWithPos = {
186+
expr: AST.Expr.Variable(stdlibFunc),
187+
ty: funcType,
188+
pos: callee.pos
189+
};
190+
var objExpr = wrapExpr(obj);
191+
var argExprs = args.map(a -> wrapExpr(a));
192+
AST.Expr.Call(stdlibCallee, [objExpr].concat(argExprs));
193+
} else {
194+
// Fallback to regular call
195+
var calleeExpr = wrapExpr(callee);
196+
var argExprs = args.map(a -> wrapExpr(a));
197+
AST.Expr.Call(calleeExpr, argExprs);
198+
}
199+
200+
default:
201+
// Other field types - regular call
202+
var calleeExpr = wrapExpr(callee);
203+
var argExprs = args.map(a -> wrapExpr(a));
204+
AST.Expr.Call(calleeExpr, argExprs);
186205
}
187206

188207
default:
@@ -250,6 +269,27 @@ class ZyntaxCompiler extends GenericCompiler<AST.Module, AST.Enum, AST.Expr> {
250269
// Parentheses are just for grouping, compile the inner expression
251270
compileExpressionImpl(e, topLevel);
252271

272+
case TNew(clsRef, params, args):
273+
// Object instantiation: new StringMap<Int>() -> $StringMap$new()
274+
var cls = clsRef.get();
275+
var className = cls.name;
276+
277+
// Request the class to be compiled (adds to dynamicTypeStack)
278+
addTypeForCompilation(TInst(clsRef, params));
279+
280+
// Generate constructor call: $ClassName$new(args...)
281+
var constructorFunc = "$" + className + "$new";
282+
// Build function type: (arg_types...) -> instance_type
283+
var argTypes = args.map(a -> a.t);
284+
var constructorType = buildConstructorType(expr.t, argTypes);
285+
var constructorCallee: AST.ExprWithPos = {
286+
expr: AST.Expr.Variable(constructorFunc),
287+
ty: constructorType,
288+
pos: expr.pos
289+
};
290+
var argExprs = args.map(a -> wrapExpr(a));
291+
AST.Expr.Call(constructorCallee, argExprs);
292+
253293
default:
254294
// Debug: Print unsupported expression type
255295
#if macro
@@ -300,11 +340,21 @@ class ZyntaxCompiler extends GenericCompiler<AST.Module, AST.Enum, AST.Expr> {
300340
return switch(t) {
301341
case TInst(_.get() => c, params):
302342
switch(c.name) {
343+
// Primitive types
303344
case "Int": Primitive("I32");
304345
case "Float": Primitive("F64");
305346
case "Bool": Primitive("Bool");
306347
case "String": Primitive("String");
307-
default: Primitive("I32");
348+
// Generic class types - convert with type parameters
349+
default:
350+
if (params.length > 0) {
351+
// Generic type like StringMap<Int>, Array<String>
352+
var typeParams = params.map(p -> convertType(p));
353+
Generic(c.name, typeParams);
354+
} else {
355+
// Non-generic class - treat as pointer to class instance
356+
Pointer(Primitive(c.name));
357+
}
308358
}
309359

310360
case TAbstract(_.get() => a, params):
@@ -313,7 +363,21 @@ class ZyntaxCompiler extends GenericCompiler<AST.Module, AST.Enum, AST.Expr> {
313363
case "Float": Primitive("F64");
314364
case "Bool": Primitive("Bool");
315365
case "Void": Primitive("Unit");
316-
default: Primitive("I32");
366+
case "Null":
367+
// Null<T> - the inner type is nullable
368+
if (params.length > 0) {
369+
// For now, just use the inner type (nullable handling TBD)
370+
convertType(params[0]);
371+
} else {
372+
Primitive("I32");
373+
}
374+
default:
375+
if (params.length > 0) {
376+
var typeParams = params.map(p -> convertType(p));
377+
Generic(a.name, typeParams);
378+
} else {
379+
Primitive("I32");
380+
}
317381
}
318382

319383
case TType(_.get() => dt, params):
@@ -356,6 +420,35 @@ class ZyntaxCompiler extends GenericCompiler<AST.Module, AST.Enum, AST.Expr> {
356420
default: "Add";
357421
}
358422
}
423+
424+
/**
425+
* Build a function type for an instance method call.
426+
* Transforms (args...) -> ret into (self, args...) -> ret
427+
* where self is the object type.
428+
*/
429+
function buildInstanceMethodType(objType: Type, methodType: Type): AST.ZType {
430+
switch(methodType) {
431+
case TFun(args, ret):
432+
// Prepend the object type as first parameter (self)
433+
var selfType = convertType(objType);
434+
var paramTypes = [selfType].concat(args.map(arg -> convertType(arg.t)));
435+
var returnType = convertType(ret);
436+
return Function(paramTypes, returnType);
437+
default:
438+
// Fallback: just return the method type
439+
return convertType(methodType);
440+
}
441+
}
442+
443+
/**
444+
* Build a function type for a constructor call.
445+
* Returns (args...) -> InstanceType
446+
*/
447+
function buildConstructorType(instanceType: Type, argTypes: Array<Type>): AST.ZType {
448+
var paramTypes = argTypes.map(t -> convertType(t));
449+
var returnType = convertType(instanceType);
450+
return Function(paramTypes, returnType);
451+
}
359452
}
360453

361454
#end

reflaxe.zyntax/test/MapTest.hx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import haxe.ds.StringMap;
2+
import haxe.ds.IntMap;
3+
4+
class MapTest {
5+
public static function main():Void {
6+
// Test StringMap
7+
var strMap = new StringMap<Int>();
8+
strMap.set("one", 1);
9+
strMap.set("two", 2);
10+
strMap.set("three", 3);
11+
12+
var val1 = strMap.get("one");
13+
var val2 = strMap.get("two");
14+
15+
// Test IntMap
16+
var intMap = new IntMap<String>();
17+
intMap.set(1, "one");
18+
intMap.set(2, "two");
19+
20+
var str1 = intMap.get(1);
21+
var str2 = intMap.get(2);
22+
23+
// Test exists
24+
var hasOne = strMap.exists("one");
25+
var hasFour = strMap.exists("four");
26+
27+
trace("StringMap test:");
28+
trace(" get('one') = " + val1);
29+
trace(" get('two') = " + val2);
30+
trace("IntMap test:");
31+
trace(" get(1) = " + str1);
32+
trace(" get(2) = " + str2);
33+
}
34+
}

reflaxe.zyntax/test/build_map.hxml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-lib reflaxe.zyntax
2+
-main MapTest
3+
-D zyntax-output=output
4+
--macro exclude('haxe.Log')

0 commit comments

Comments
 (0)