1 //
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 16 Apr 06 Brian Frank Creation
7 // 22 Sep 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** InitEnum is used to auto-generate EnumDefs into abstract
12 ** syntax tree representation of the fields and method.
13 **
14 **
15 class InitEnum : CompilerStep
16 {
17
18 //////////////////////////////////////////////////////////////////////////
19 // Constructor
20 //////////////////////////////////////////////////////////////////////////
21
22 new make(Compiler compiler)
23 : super(compiler)
24 {
25 }
26
27 //////////////////////////////////////////////////////////////////////////
28 // Run
29 //////////////////////////////////////////////////////////////////////////
30
31 override Void run()
32 {
33 log.debug("InitEnum")
34 walk(types, VisitDepth.typeDef)
35 bombIfErr
36 }
37
38 override Void visitTypeDef(TypeDef t)
39 {
40 if (!t.isEnum) return
41
42 try
43 {
44 addCtor
45 addFromStr
46 t.addFacet(this, "simple", true)
47
48 fields := FieldDef[,]
49 t.enumDefs.each |EnumDef e| { fields.add(makeField(e)) }
50 fields.add(makeValuesField)
51
52 // add enum fields to beginning of type
53 fields.each |FieldDef f, Int i| { t.addSlot(f, i) }
54 }
55 catch (CompilerErr e)
56 {
57 }
58 }
59
60 //////////////////////////////////////////////////////////////////////////
61 // Make Ctor
62 //////////////////////////////////////////////////////////////////////////
63
64 **
65 ** Add constructor or enhance existing constructor.
66 **
67 Void addCtor()
68 {
69 // our constructor definition
70 MethodDef m := null
71
72 // check if there are any existing constructors - there
73 // can only be zero or one called make
74 ctors := curType.methodDefs.findAll |MethodDef x->Bool| { return x.isCtor }
75 ctors.each |MethodDef ctor|
76 {
77 if (ctor.name == "make")
78 m = ctor
79 else
80 throw err("Enum constructor must be named 'make'", ctor.location)
81 }
82
83 // if we found an existing constructor, then error check it
84 if (m != null)
85 {
86 if (!m.isPrivate)
87 err("Enum constructor must be private", m.location)
88
89 if (m.ctorChain != null)
90 err("Enum constructor cannot call super constructor", m.location)
91 }
92
93 // if we didn't find an existing constructor, then
94 // add a synthetic one
95 if (m == null)
96 {
97 m = MethodDef.make(curType.location, curType)
98 m.name = "make"
99 m.flags = FConst.Ctor | FConst.Private | FConst.Synthetic
100 m.ret = TypeRef.make(curType.location, ns.voidType)
101 m.code = Block.make(curType.location)
102 m.code.stmts.add(ReturnStmt.make(curType.location))
103 curType.addSlot(m)
104 }
105
106 // Enum.make call
107 loc := m.location
108 m.ctorChain = CallExpr.make(loc, SuperExpr.make(loc), "make")
109 m.ctorChain.isCtorChain = true
110 m.ctorChain.args.add(UnknownVarExpr.make(loc, null, "\$ordinal"))
111 m.ctorChain.args.add(UnknownVarExpr.make(loc, null, "\$name"))
112
113 // insert ordinal, name params
114 m.params.insert(0, ParamDef.make(loc, ns.intType, "\$ordinal"))
115 m.params.insert(1, ParamDef.make(loc, ns.strType, "\$name"))
116 }
117
118 //////////////////////////////////////////////////////////////////////////
119 // Make FromStr
120 //////////////////////////////////////////////////////////////////////////
121
122 **
123 ** Add fromStr method.
124 **
125 Void addFromStr()
126 {
127 // static CurType fromStr(Str name, Bool checked := true)
128 loc := curType.location
129 m := MethodDef.make(loc, curType)
130 m.name = "fromStr"
131 m.flags = FConst.Static | FConst.Public
132 m.params.add(ParamDef.make(loc, ns.strType, "name"))
133 m.params.add(ParamDef.make(loc, ns.boolType, "checked", LiteralExpr.make(loc, ExprId.trueLiteral, ns.boolType, true)))
134 m.ret = TypeRef.make(loc, curType)
135 m.code = Block.make(loc)
136 m.doc = ["Return the $curType.name instance for the specified name. If not a",
137 "valid name and checked is false return null, otherwise throw ParseErr."]
138 curType.addSlot(m)
139
140 // return (CurType)doParse(name, checked)
141 doFromStr := CallExpr.make(loc, null, "doFromStr")
142 doFromStr.args.add(LiteralExpr.make(loc, ExprId.typeLiteral, ns.typeType, curType))
143 doFromStr.args.add(UnknownVarExpr.make(loc, null, "name"))
144 doFromStr.args.add(UnknownVarExpr.make(loc, null, "checked"))
145 cast := TypeCheckExpr.make(loc, ExprId.cast, doFromStr, curType)
146 m.code.stmts.add(ReturnStmt.make(loc, cast))
147 }
148
149 //////////////////////////////////////////////////////////////////////////
150 // Make Field
151 //////////////////////////////////////////////////////////////////////////
152
153 **
154 ** Make enum value field: public static final Foo name = make(ord, name)
155 **
156 FieldDef makeField(EnumDef def)
157 {
158 // ensure there isn't already a slot with same name
159 dup := curType.slot(def.name)
160 if (dup != null)
161 {
162 if (dup.parent === curType)
163 throw err("Enum '$def.name' conflicts with slot", (Location)dup->location)
164 else
165 throw err("Enum '$def.name' conflicts with inherited slot '$dup.qname'", def.location)
166 }
167
168 loc := def.location
169
170 // initializer
171 init := CallExpr.make(loc, null, "make")
172 init.args.add(LiteralExpr.make(loc, ExprId.intLiteral, ns.intType, def.ordinal))
173 init.args.add(LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, def.name))
174 init.args.addAll(def.ctorArgs)
175
176 // static field
177 f := FieldDef.make(loc, curType)
178 f.flags = FConst.Public | FConst.Static | FConst.Const | FConst.Storage | FConst.Enum
179 f.name = def.name
180 f.fieldType = curType
181 f.init = init
182 return f
183 }
184
185 **
186 ** Make values field: List of Enum values
187 **
188 FieldDef makeValuesField()
189 {
190 // ensure there isn't already a slot with same name
191 dup := curType.slot("values")
192 if (dup != null)
193 {
194 if (dup.parent == curType)
195 throw err("Enum 'values' conflicts with slot", (Location)dup->location)
196 else
197 throw err("Enum 'values' conflicts with inherited slot '$dup.qname'", curType.location)
198 }
199
200 loc := curType.location
201
202 // initializer
203 listType := curType.toListOf
204 list := ListLiteralExpr.make(loc, listType)
205 curType.enumDefs.each |EnumDef e|
206 {
207 target := StaticTargetExpr.make(loc, curType)
208 list.vals.add(UnknownVarExpr.make(loc, target, e.name))
209 }
210 init := CallExpr.make(loc, list, "toImmutable")
211
212 // static field
213 f := FieldDef.make(loc, curType)
214 f.flags = FConst.Public | FConst.Static | FConst.Const | FConst.Storage
215 f.name = "values"
216 f.fieldType = listType
217 f.init = init
218 f.doc = ["List of $curType.name values indexed by ordinal"]
219 return f
220 }
221
222 }