I'm playing with Fantom DSL for a while. However I have a problem with injecting custom field to a type where DSL was declared. There is a test I have:
I initialized custom field with an empty string f.init = LiteralExpr.makeStr(loc, ns, ""). Without this line I will get Non-nullable field name must be assigned in constructor make as expected. But for some reason initialized field returns null. Am I missed something or is it a bug?
It looks like you are not actually adding the field to the type definition. Try adding a line like this:
parent.addSlot(f)
Yuri StrotSat 16 Jun 2012
I do. There is one from the bottom line of InjectFieldPlugin.compile.
brianSat 16 Jun 2012
Actually I don't understand what the problem is. You are saying reflection works for getting/setting the field. The problem is the initialization of the field in the constructor? That happens in Normalize step by running thru all the fields and adding init statements into your constructor.
Yuri StrotSun 17 Jun 2012
Oh, I see. DSL parsing happens in the ResolveExpr step which appears after Normalize step. I just added constructor initialization into my DSL plugin and it works correctly now. Thanks for help!
Yuri Strot Sat 16 Jun 2012
I'm playing with Fantom DSL for a while. However I have a problem with injecting custom field to a type where DSL was declared. There is a test I have:
class InjectFieldTest : Test { Str inject := InjectField<||> Void test1() { verifyNotNull(field) } Void test2() { field.set(this, "a") verifyEq("a", field.get(this)) } Void test3() { verifyEq("", field.get(this)) } private Field? field() { typeof.field("name") } }As you can see
InjectField<||>should injectStr name := "". But something goes wrong:test1passed. So new field was generated.test2passed. So I can set value to the field and get it.test3failed.field.get(this)return null.My DSL plugin:
class InjectFieldPlugin : DslPlugin { new make(Compiler c) : super(c) {} override Expr compile(DslExpr dsl) { loc := dsl.loc parent := types.find { it.loc.file == loc.file } type := ns.resolveType("sys::Str") f := FieldDef(loc, parent, "name") f.flags = FConst.Public f.fieldType = TypeRef(loc, type) f.init = LiteralExpr.makeStr(loc, ns, "") //generate synthetic getter/setter defGet(f) defSet(f) f.flags += FConst.Storage parent.addSlot(f) return LiteralExpr.makeStr(dsl.loc, ns, "") } ... }I initialized custom field with an empty string
f.init = LiteralExpr.makeStr(loc, ns, ""). Without this line I will getNon-nullable field name must be assigned in constructor makeas expected. But for some reason initialized field returns null. Am I missed something or is it a bug?There is full source I used: https://bitbucket.org/ystrot/play/src/8190f89645b4/fan/InjectFieldPlugin.fan
brian Sat 16 Jun 2012
It looks like you are not actually adding the field to the type definition. Try adding a line like this:
Yuri Strot Sat 16 Jun 2012
I do. There is one from the bottom line of
InjectFieldPlugin.compile.brian Sat 16 Jun 2012
Actually I don't understand what the problem is. You are saying reflection works for getting/setting the field. The problem is the initialization of the field in the constructor? That happens in Normalize step by running thru all the fields and adding init statements into your constructor.
Yuri Strot Sun 17 Jun 2012
Oh, I see. DSL parsing happens in the
ResolveExprstep which appears afterNormalizestep. I just added constructor initialization into my DSL plugin and it works correctly now. Thanks for help!