using compiler class DispatcherDsl : DslPlugin { new make(Compiler c) : super(c) {} override Expr compile(DslExpr dsl) { loc := dsl.loc type := findType(findUnit(dsl.loc)) name := dsl.src if(type == null) { throw err("Invalid use of Dispatcher DSL", loc) //should not happen } methods := type.methodDefs.findAll |m| { m.facet(fq) != null && m.name.startsWith(name) } dispField := createDispatcherField(loc, type, name) initExpr := createInit(loc, methods, name) dispField.init = initExpr type.addSlot(dispField); //modify instance init block MethodDef? iiDef := type.methodDef("static\$init") if(iiDef == null) { throw err("Type does not have instance init method", loc) } iiDef.code.stmts.insert(-1, BinaryExpr.makeAssign(fieldRef(dispField, loc), initExpr).toStmt) return fieldRef(dispField, loc) } private Expr createInit(Loc loc, MethodDef[] methods, Str name) { funcList := createFuncs(methods, loc) typeRef := StaticTargetExpr.make(loc, ns.resolveType(dq)) retDef := LiteralExpr.make(loc, ExprId.typeLiteral, ns.resolveType("sys::Type"), methods.first.returnType) nameExpr := Expr.makeForLiteral(loc, ns, name) return CallExpr.makeWithMethod(loc, typeRef, ns.resolveType(dq).method("makeExplicit"), [nameExpr, retDef, funcList]) } private FieldDef createDispatcherField(Loc loc, TypeDef parent, Str name) { name = "${name}Dispatcher" result := FieldDef.make(loc, parent, name, FConst.Const + FConst.Private + FConst.Storage + FConst.Static) result.fieldType = ns.resolveType(dq) return result } private ListLiteralExpr createFuncs(MethodDef[] methods, Loc loc) { funcMethod := ns.resolveType("sys::Method").method("func") result := ListLiteralExpr.make(loc, ListType.make(ns.resolveType("sys::Func"))) result.vals = methods.map |m| { CallExpr.makeWithMethod( loc, createSlotLiteral(loc, m), funcMethod) } result.ctype = result.explicitType return result } private Expr fieldRef(FieldDef f, Loc l) { FieldExpr(l, StaticTargetExpr.make(l, f.parentDef), f) } private SlotLiteralExpr createSlotLiteral(Loc loc, MethodDef m) { result := SlotLiteralExpr.make(loc, m.parentDef, m.name) result.slot = m result.ctype = ns.resolveType("sys::Method") return result; } private static const Str fq := "mdispatch::Dispatch"; private static const Str dq := "mdispatch::Dispatcher"; private TypeDef? findType(CompilationUnit unit) { unit.types.find { it.methodDefs.any { it.facet(fq) != null } } } private CompilationUnit findUnit(Loc loc) { units.find |unit| { unit.loc.file == loc.file } } }