1 //
2 // Copyright (c) 2007, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 20 Jan 07 Brian Frank Creation
7 //
8
9 **
10 ** CurryResolver handles the process of resolving a CurryExpr.
11 **
12 class CurryResolver : CompilerSupport
13 {
14
15 //////////////////////////////////////////////////////////////////////////
16 // Construction
17 //////////////////////////////////////////////////////////////////////////
18
19 **
20 ** Constructor
21 **
22 new make(Compiler compiler, TypeDef curType, Int curryCount, CurryExpr expr)
23 : super(compiler)
24 {
25 this.loc = expr.location
26 this.curType = curType
27 this.curryCount = curryCount
28 this.expr = expr
29 this.call = (CallExpr)expr.operand
30 this.method = call.method
31 }
32
33 //////////////////////////////////////////////////////////////////////////
34 // Resolve
35 //////////////////////////////////////////////////////////////////////////
36
37 **
38 ** Resolve into a method call or field access
39 **
40 Expr resolve()
41 {
42 try
43 {
44 // generate method signature
45 genSignature
46
47 // optimize static with no partials
48 if (method.isStatic && call.args.isEmpty)
49 return simpleStatic
50
51 // define curry type Curry$xx used to store partial
52 // arguments as fields and implement the curried method
53 defineCurry
54 return mapCurry
55 }
56 catch (CompilerErr err)
57 {
58 expr.ctype = ns.error
59 return expr
60 }
61 }
62
63 //////////////////////////////////////////////////////////////////////////
64 // Signature
65 //////////////////////////////////////////////////////////////////////////
66
67 **
68 ** Define the signature of the curries method which
69 ** is any partial parameters left incomplete.
70 **
71 Void genSignature()
72 {
73 // if more arguments were provided than parameters
74 if (call.args.size > method.params.size)
75 throw err("Too many arguments for curry", loc)
76
77 // get the incomplete CParams
78 incompletes := method.params[call.args.size..-1]
79
80 // map incomplete CParams to CTypes
81 params := (CType[])incompletes.map(CType[,]) |CParam p->CType| { return p.paramType }
82
83 // map incomplete CParams to names
84 names := (Str[])incompletes.map(Str[,]) |CParam p->Str| { return p.name }
85
86 // check if we have an instance method with no target, in that
87 // case then this must be passed as first argument to curried function
88 thisIsParam = !method.isStatic && (call.target == null || call.target.id == ExprId.staticTarget)
89 if (thisIsParam)
90 {
91 params.insert(0, method.parent)
92 names.insert(0, "\$this")
93 }
94
95 // define the signature
96 sig = FuncType.make(params, names, method.returnType)
97 }
98
99 //////////////////////////////////////////////////////////////////////////
100 // Simple Static
101 //////////////////////////////////////////////////////////////////////////
102
103 **
104 ** If the curry is a static method with no arguments, then
105 ** really it isn't a curry per se, because we can optimize
106 ** to just a method lookup.
107 **
108 Expr simpleStatic()
109 {
110 // replace curry with Slot.findMethod
111 result := CallExpr.makeWithMethod(loc, null, ns.slotFindFunc)
112 result.args.add(LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, method.qname))
113 result.ctype = sig
114 return result
115 }
116
117 //////////////////////////////////////////////////////////////////////////
118 // Define Curry
119 //////////////////////////////////////////////////////////////////////////
120
121 **
122 ** Define a synthetic class called Curry$xx.
123 **
124 Void defineCurry()
125 {
126 // define curry class
127 curry = TypeDef.make(ns, loc, curType.unit, "Curry\$${curryCount}")
128 curry.flags = FConst.Internal | FConst.Synthetic
129 curry.base = sig
130 addTypeDef(curry)
131
132 // define ctor
133 ctor = MethodDef.make(loc, curry)
134 ctor.flags = FConst.Ctor | FConst.Internal | FConst.Synthetic
135 ctor.name = "make"
136 ctor.ret = ns.voidType
137 ctor.code = Block.make(loc)
138 curry.addSlot(ctor)
139 }
140
141 //////////////////////////////////////////////////////////////////////////
142 // Map Curry
143 //////////////////////////////////////////////////////////////////////////
144
145 **
146 ** For each argument specified we need to pass to the constructor
147 ** and stash away and in a field for use in redirecting to the
148 ** target method.
149 **
150 Expr mapCurry()
151 {
152 // this is our redirect to the method call to be used in Method.call
153 doCall := CallExpr.makeWithMethod(loc, null, method);
154
155 // these are the arguments to provided to curried function
156 args := call.args.dup
157 allArgsConst := true
158
159 // figure out if there is an implied this parameter to curry, if
160 // so then we need to include this in the implied arguments
161 Expr self := null
162 if (!method.isStatic && call.target != null && call.target.id != ExprId.staticTarget)
163 args.insert(0, self = call.target)
164
165 // for each partial argument:
166 // - add field to curry class
167 // - add parameter to constructor
168 // - store field in constructor
169 // - load field in doCall
170 args.each |Expr expr, Int i|
171 {
172 // name and type of argument
173 name := "p$i"
174 ctype := expr.ctype
175
176 // keep track if all our args are const
177 if (!ctype.isConst)
178 allArgsConst = false
179
180 // add field to curry class
181 field := FieldDef.make(loc, curry)
182 field.name = name
183 field.flags = FConst.Internal | FConst.Storage | FConst.Synthetic
184 field.fieldType = ctype
185 curry.addSlot(field)
186
187 // add parameter to ctor
188 ctor.params.add(ParamDef.make(loc, ctype, name))
189
190 // add field set assignment statement in ctor
191 lhs := FieldExpr.make(loc, ThisExpr.make(loc), field, false)
192 rhs := UnknownVarExpr.make(loc, null, name)
193 assign := BinaryExpr.makeAssign(lhs, rhs)
194 ctor.code.stmts.add(assign.toStmt)
195
196 // add field get to doCall statement
197 loadField := FieldExpr.make(loc, ThisExpr.make(loc), field, false)
198 if (expr === self)
199 doCall.target = loadField
200 else
201 doCall.args.add(loadField)
202 }
203
204 // finish ctor
205 ctor.code.stmts.add(ReturnStmt.make(loc))
206
207 // Method.callX()
208 InitClosures.genMethodCall(compiler, loc, curry, sig, doCall, thisIsParam)
209
210 // generate isImmutable method
211 InitClosures.genIsImmutableMethod(compiler, loc, curry, allArgsConst)
212
213 // replace curry with call to CurryXX.make
214 result := CallExpr.makeWithMethod(loc, null, ctor)
215 result.args = args
216 result.ctype = sig
217 return result
218 }
219
220 //////////////////////////////////////////////////////////////////////////
221 // Fields
222 //////////////////////////////////////////////////////////////////////////
223
224 private Location loc // location of curry expr
225 private TypeDef curType // current enclosing type
226 private Int curryCount // total number of curries in compilation unit
227 private CurryExpr expr // curry expr being resolved
228 private CallExpr call // call operand of curry
229 private CMethod method // target method
230 private FuncType sig // signature of result
231 private Bool thisIsParam // is the "this" target of method incomplete
232 private TypeDef curry // curry implementation class
233 private MethodDef ctor // curry implementation class ctor
234
235 }
2 // Copyright (c) 2007, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 20 Jan 07 Brian Frank Creation
7 //
8
9 **
10 ** CurryResolver handles the process of resolving a CurryExpr.
11 **
12 class CurryResolver : CompilerSupport
13 {
14
15 //////////////////////////////////////////////////////////////////////////
16 // Construction
17 //////////////////////////////////////////////////////////////////////////
18
19 **
20 ** Constructor
21 **
22 new make(Compiler compiler, TypeDef curType, Int curryCount, CurryExpr expr)
23 : super(compiler)
24 {
25 this.loc = expr.location
26 this.curType = curType
27 this.curryCount = curryCount
28 this.expr = expr
29 this.call = (CallExpr)expr.operand
30 this.method = call.method
31 }
32
33 //////////////////////////////////////////////////////////////////////////
34 // Resolve
35 //////////////////////////////////////////////////////////////////////////
36
37 **
38 ** Resolve into a method call or field access
39 **
40 Expr resolve()
41 {
42 try
43 {
44 // generate method signature
45 genSignature
46
47 // optimize static with no partials
48 if (method.isStatic && call.args.isEmpty)
49 return simpleStatic
50
51 // define curry type Curry$xx used to store partial
52 // arguments as fields and implement the curried method
53 defineCurry
54 return mapCurry
55 }
56 catch (CompilerErr err)
57 {
58 expr.ctype = ns.error
59 return expr
60 }
61 }
62
63 //////////////////////////////////////////////////////////////////////////
64 // Signature
65 //////////////////////////////////////////////////////////////////////////
66
67 **
68 ** Define the signature of the curries method which
69 ** is any partial parameters left incomplete.
70 **
71 Void genSignature()
72 {
73 // if more arguments were provided than parameters
74 if (call.args.size > method.params.size)
75 throw err("Too many arguments for curry", loc)
76
77 // get the incomplete CParams
78 incompletes := method.params[call.args.size..-1]
79
80 // map incomplete CParams to CTypes
81 params := (CType[])incompletes.map(CType[,]) |CParam p->CType| { return p.paramType }
82
83 // map incomplete CParams to names
84 names := (Str[])incompletes.map(Str[,]) |CParam p->Str| { return p.name }
85
86 // check if we have an instance method with no target, in that
87 // case then this must be passed as first argument to curried function
88 thisIsParam = !method.isStatic && (call.target == null || call.target.id == ExprId.staticTarget)
89 if (thisIsParam)
90 {
91 params.insert(0, method.parent)
92 names.insert(0, "\$this")
93 }
94
95 // define the signature
96 sig = FuncType.make(params, names, method.returnType)
97 }
98
99 //////////////////////////////////////////////////////////////////////////
100 // Simple Static
101 //////////////////////////////////////////////////////////////////////////
102
103 **
104 ** If the curry is a static method with no arguments, then
105 ** really it isn't a curry per se, because we can optimize
106 ** to just a method lookup.
107 **
108 Expr simpleStatic()
109 {
110 // replace curry with Slot.findMethod
111 result := CallExpr.makeWithMethod(loc, null, ns.slotFindFunc)
112 result.args.add(LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, method.qname))
113 result.ctype = sig
114 return result
115 }
116
117 //////////////////////////////////////////////////////////////////////////
118 // Define Curry
119 //////////////////////////////////////////////////////////////////////////
120
121 **
122 ** Define a synthetic class called Curry$xx.
123 **
124 Void defineCurry()
125 {
126 // define curry class
127 curry = TypeDef.make(ns, loc, curType.unit, "Curry\$${curryCount}")
128 curry.flags = FConst.Internal | FConst.Synthetic
129 curry.base = sig
130 addTypeDef(curry)
131
132 // define ctor
133 ctor = MethodDef.make(loc, curry)
134 ctor.flags = FConst.Ctor | FConst.Internal | FConst.Synthetic
135 ctor.name = "make"
136 ctor.ret = ns.voidType
137 ctor.code = Block.make(loc)
138 curry.addSlot(ctor)
139 }
140
141 //////////////////////////////////////////////////////////////////////////
142 // Map Curry
143 //////////////////////////////////////////////////////////////////////////
144
145 **
146 ** For each argument specified we need to pass to the constructor
147 ** and stash away and in a field for use in redirecting to the
148 ** target method.
149 **
150 Expr mapCurry()
151 {
152 // this is our redirect to the method call to be used in Method.call
153 doCall := CallExpr.makeWithMethod(loc, null, method);
154
155 // these are the arguments to provided to curried function
156 args := call.args.dup
157 allArgsConst := true
158
159 // figure out if there is an implied this parameter to curry, if
160 // so then we need to include this in the implied arguments
161 Expr self := null
162 if (!method.isStatic && call.target != null && call.target.id != ExprId.staticTarget)
163 args.insert(0, self = call.target)
164
165 // for each partial argument:
166 // - add field to curry class
167 // - add parameter to constructor
168 // - store field in constructor
169 // - load field in doCall
170 args.each |Expr expr, Int i|
171 {
172 // name and type of argument
173 name := "p$i"
174 ctype := expr.ctype
175
176 // keep track if all our args are const
177 if (!ctype.isConst)
178 allArgsConst = false
179
180 // add field to curry class
181 field := FieldDef.make(loc, curry)
182 field.name = name
183 field.flags = FConst.Internal | FConst.Storage | FConst.Synthetic
184 field.fieldType = ctype
185 curry.addSlot(field)
186
187 // add parameter to ctor
188 ctor.params.add(ParamDef.make(loc, ctype, name))
189
190 // add field set assignment statement in ctor
191 lhs := FieldExpr.make(loc, ThisExpr.make(loc), field, false)
192 rhs := UnknownVarExpr.make(loc, null, name)
193 assign := BinaryExpr.makeAssign(lhs, rhs)
194 ctor.code.stmts.add(assign.toStmt)
195
196 // add field get to doCall statement
197 loadField := FieldExpr.make(loc, ThisExpr.make(loc), field, false)
198 if (expr === self)
199 doCall.target = loadField
200 else
201 doCall.args.add(loadField)
202 }
203
204 // finish ctor
205 ctor.code.stmts.add(ReturnStmt.make(loc))
206
207 // Method.callX()
208 InitClosures.genMethodCall(compiler, loc, curry, sig, doCall, thisIsParam)
209
210 // generate isImmutable method
211 InitClosures.genIsImmutableMethod(compiler, loc, curry, allArgsConst)
212
213 // replace curry with call to CurryXX.make
214 result := CallExpr.makeWithMethod(loc, null, ctor)
215 result.args = args
216 result.ctype = sig
217 return result
218 }
219
220 //////////////////////////////////////////////////////////////////////////
221 // Fields
222 //////////////////////////////////////////////////////////////////////////
223
224 private Location loc // location of curry expr
225 private TypeDef curType // current enclosing type
226 private Int curryCount // total number of curries in compilation unit
227 private CurryExpr expr // curry expr being resolved
228 private CallExpr call // call operand of curry
229 private CMethod method // target method
230 private FuncType sig // signature of result
231 private Bool thisIsParam // is the "this" target of method incomplete
232 private TypeDef curry // curry implementation class
233 private MethodDef ctor // curry implementation class ctor
234
235 }