Generable generate a single random instance

This commit is contained in:
2022-03-30 23:13:30 -06:00
parent 95e12f62e1
commit fadc08339f
4 changed files with 116 additions and 19 deletions

View File

@@ -4,7 +4,8 @@
"description": "Generalized genetic generation for Haxe classes",
"classPath": "src/",
"dependencies": {
"kiss": ""
"kiss": "",
"tink_macro": ""
},
"url": "https://github.com/NQNStudios/kisslang",
"contributors": [

View File

@@ -2,36 +2,84 @@ package prokgen;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.ComplexTypeTools;
using haxe.macro.TypeTools;
using tink.MacroApi;
using tink.core.Outcome;
class Generable {
public static macro function build():Array<Field> {
var generatorClass:Ref<ClassType> = null;
function classIsGenerator(cls:ClassType) {
if (cls == null) return false;
if (cls.name == "Generator" && cls.pack.join(".") == "prokgen") {
return true;
}
if (cls.superClass != null) {
var scls = cls.superClass.t.get();
if (classIsGenerator(scls)) return true;
}
for (iface in cls.interfaces) {
var ref = iface.t;
generatorClass = ref;
var icls = ref.get();
if (classIsGenerator(icls)) return true;
}
return false;
}
function typeIsGenerator(t:Type) {
return classIsGenerator(t.getClass());
}
function typeFromField(c:ComplexType, e:Expr) {
if (c != null) return c.toType().sure();
return e.typeof().sure();
}
function checkFieldIsGenerator(name:String, c:ComplexType, e:Expr, generators:Map<String, Type>) {
var t = typeFromField(c, e);
if (typeIsGenerator(t)) {
generators[name] = t;
}
}
var fields = Context.getBuildFields();
var instanceFields:Map<String, ComplexType> = [];
var generatorFields:Map<ComplexType,String> = [];
var instanceFields:Map<String, Type> = [];
var generatorFields:Map<String, Type> = [];
var generatorsByFieldType:Map<Type, String> = [];
var hasGenScore = false;
var genTypes:Array<ComplexType> = [];
for (field in fields) {
switch (field) {
// TODO find all the fields that are generators
// Find all the fields that are generators and map them by type generated
case {
name: name,
doc: _,
access: access,
kind: FVar(type, _),
kind: FVar(type, expr),
pos: _,
meta: _
} if (access.indexOf(AStatic) != -1):
// TODO find all fields that are non-generator instance variables
checkFieldIsGenerator(name, type, expr, generatorFields);
// find all fields that are uninitialized, non-generator instance variables
case {
name: name,
doc: _,
access: access,
kind: FVar(type, _),
kind: FVar(type, null),
pos: _,
meta: _
} if (access.indexOf(AStatic) == -1):
// TODO make sure genScore() is defined and returns Float
instanceFields[name] = type.toType().sure();
// make sure genScore() is defined and returns Float
case {
name: "genScore",
doc: _,
@@ -50,19 +98,62 @@ class Generable {
}
}
var useLines = [for (generator => _ in generatorFields) {
macro $i{generator}.use(r);
}];
var genLines = [];
for (name => type in instanceFields) {
var generatorName = if (generatorsByFieldType.exists(type)) {
generatorsByFieldType[type];
} else {
for (genName => genType in generatorFields) {
var desiredType = TInst(generatorClass, [type]);
if (genType.unify(desiredType))
generatorsByFieldType[type] = genName;
}
generatorsByFieldType[type];
};
if (generatorName == null) {
throw 'No generator found for $name';
}
genLines.push(macro $i{name} = $i{generatorName}.makeRandom());
}
// TODO when generating fields, make sure they are handled alphabetically so seeded generation is deterministic, not dependent on Map iterator order
var generateSpecimenField = {
name: "_generate",
access: [APrivate],
kind: FFun({
ret: null,
args: [],
expr: macro $b{genLines}
}),
pos: Context.getLocalClass().get().pos
}
// TODO create a population and evolve it
var generateField = {
name: "generate",
access: [AStatic, APublic],
kind: FFun({
ret: null,
// TODO accept a ProkRandom arg
args: [],
expr: macro return 0
args: [{
name: "r"
}],
expr: macro {
var inst = Type.createEmptyInstance(HighNumbers);
$b{useLines}
inst._generate();
return inst;
}
}),
pos: Context.currentPos()
pos: Context.getLocalClass().get().pos
};
fields.push(generateSpecimenField);
fields.push(generateField);
return fields;

View File

@@ -8,7 +8,8 @@
(let [:Array<Dynamic> aList (for g generators (g.makeRandom))
:Array<Dynamic> bList (for g generators (g.makeRandom))
:Array<Dynamic> cList (for i (range generators.length) (.combine (nth generators i) (nth aList i) (nth bList i)))]
~aList
~bList
~cList)
~(HighNumbers.generate))
// TODO assert that no infinities or nans appear in any of the three lists, flattened:
aList
bList
cList)
~(HighNumbers.generate (new ProkRandom)))

View File

@@ -9,10 +9,14 @@ class HighNumbers {
var iList:Array<Int>;
var fList:Array<Float>;
static var iGen:IntGen = new IntGen(-1000, 1000);
static var fGen:FloatGen = new FloatGen(-1000, 100);
static var iListGen:ArrayGen<Int> = new ArrayGen<Int>(new IntGen(-1000, 1000), 0, 10);
static var fListGen:ArrayGen<Float> = new ArrayGen<Float>(new FloatGen(-1000, 1000), 0, 10);
static var iGen = new IntGen(-1000, 1000);
static var fGen = new FloatGen(-1000, 100);
static var iListGen = new ArrayGen<Int>(new IntGen(-1000, 1000), 0, 10);
static var fListGen = new ArrayGen<Float>(new FloatGen(-1000, 1000), 0, 10);
function toString() {
return '$i $f $iList $fList';
}
function genScore() {
var sum:Float = i + f;