@@ -152,9 +152,17 @@ public final class Compiler {
152152 return ( arguments, next)
153153 }
154154
155- /// Compiles the given body of a function (or expression, if this compiler is not used to
156- /// compile a function).
157- private func compileBody( _ expr: Expr , localDefine: Bool = false ) throws {
155+ /// Compiles the given body of a function `expr` (or expression, if this compiler is not
156+ /// used to compile a function).
157+ /// When an `optionals` formal list of parameters is provided, the formals are matched
158+ /// against a list on the stack. The matching happens in the context of `optenv`. If
159+ /// `optenv` is not provided, the matching happens in the context of the environment
160+ /// constructed on top of `self.env`.
161+ /// When `localDefine` is set to true, local definitions are allowed.
162+ private func compileBody( _ expr: Expr ,
163+ optionals: Expr ? = nil ,
164+ optenv: Env ? = nil ,
165+ localDefine: Bool = false ) throws {
158166 if expr. isNull {
159167 self . emit ( . pushVoid)
160168 self . emit ( . return)
@@ -170,12 +178,31 @@ public final class Compiler {
170178 }
171179 }
172180 // Compile body
173- if !( try compileSeq ( expr, in: self . env, inTailPos: true , localDefine: localDefine) ) {
174- self . emit ( . return)
181+ if let optionals = optionals {
182+ let initialLocals = self . numLocals
183+ let group = try self . compileOptionalBindings ( optionals,
184+ in: self . env,
185+ optenv: optenv)
186+ let res = try self . compileSeq ( expr,
187+ in: Env ( group) ,
188+ inTailPos: true ,
189+ localDefine: localDefine)
190+ if !self . finalizeBindings ( group, exit: res, initialLocals: initialLocals) {
191+ self . emit ( . return)
192+ }
193+ } else {
194+ if !( try compileSeq ( expr, in: self . env, inTailPos: true , localDefine: localDefine) ) {
195+ self . emit ( . return)
196+ }
175197 }
176198 // Insert instruction to reserve local variables
177199 if self . maxLocals > self . arguments? . count ?? 0 {
178- self . patch ( . alloc( self . maxLocals - ( self . arguments? . count ?? 0 ) ) , at: reserveLocalIp)
200+ let n = self . maxLocals - ( self . arguments? . count ?? 0 )
201+ if optionals != nil {
202+ self . patch ( . allocBelow( n) , at: reserveLocalIp)
203+ } else {
204+ self . patch ( . alloc( n) , at: reserveLocalIp)
205+ }
179206 }
180207 }
181208 // Checkpoint argument mutability
@@ -1021,6 +1048,70 @@ public final class Compiler {
10211048 return group
10221049 }
10231050
1051+ func compileOptionalBindings( _ bindingList: Expr ,
1052+ in lenv: Env ,
1053+ optenv: Env ? ) throws -> BindingGroup {
1054+ let group = BindingGroup ( owner: self , parent: lenv)
1055+ let env = optenv ?? . local( group)
1056+ var bindings = bindingList
1057+ var prevIndex = - 1
1058+ while case . pair( . symbol( let sym) , let rest) = bindings {
1059+ self . emit ( . decons)
1060+ let binding = group. allocBindingFor ( sym)
1061+ guard binding. index > prevIndex else {
1062+ throw RuntimeError . eval ( . duplicateBinding, . symbol( sym) , bindingList)
1063+ }
1064+ if binding. isValue {
1065+ self . emit ( . setLocal( binding. index) )
1066+ } else {
1067+ self . emit ( . makeLocalVariable( binding. index) )
1068+ }
1069+ prevIndex = binding. index
1070+ bindings = rest
1071+ }
1072+ while case . pair( let binding, let rest) = bindings {
1073+ guard case . pair( . symbol( let sym) , . pair( let expr, . null) ) = binding else {
1074+ throw RuntimeError . eval ( . malformedBinding, binding, bindingList)
1075+ }
1076+ self . emit ( . dup)
1077+ self . emit ( . isNull)
1078+ let branchIfIp = self . emitPlaceholder ( )
1079+ self . emit ( . decons)
1080+ let branchIp = self . emitPlaceholder ( )
1081+ self . patch ( . branchIf( self . offsetToNext ( branchIfIp) ) , at: branchIfIp)
1082+ try self . compile ( expr, in: env, inTailPos: false )
1083+ self . patch ( . branch( self . offsetToNext ( branchIp) ) , at: branchIp)
1084+ let binding = group. allocBindingFor ( sym)
1085+ guard binding. index > prevIndex else {
1086+ throw RuntimeError . eval ( . duplicateBinding, . symbol( sym) , bindingList)
1087+ }
1088+ if binding. isValue {
1089+ self . emit ( . setLocal( binding. index) )
1090+ } else {
1091+ self . emit ( . makeLocalVariable( binding. index) )
1092+ }
1093+ prevIndex = binding. index
1094+ bindings = rest
1095+ }
1096+ switch bindings {
1097+ case . null:
1098+ self . emit ( . failIfNotNull)
1099+ case . symbol( let sym) :
1100+ let binding = group. allocBindingFor ( sym)
1101+ guard binding. index > prevIndex else {
1102+ throw RuntimeError . eval ( . duplicateBinding, . symbol( sym) , bindingList)
1103+ }
1104+ if binding. isValue {
1105+ self . emit ( . setLocal( binding. index) )
1106+ } else {
1107+ self . emit ( . makeLocalVariable( binding. index) )
1108+ }
1109+ default :
1110+ throw RuntimeError . eval ( . malformedBindings, bindingList)
1111+ }
1112+ return group
1113+ }
1114+
10241115 /// This function should be used for finalizing the compilation of blocks with local
10251116 /// bindings. It finalizes the binding group and resets the local bindings so that the
10261117 /// garbage collector can deallocate the objects that are not used anymore.
@@ -1071,10 +1162,17 @@ public final class Compiler {
10711162 /// Compiles a closure consisting of a list of formal arguments `arglist`, a list of
10721163 /// expressions `body`, and a local environment `env`. It puts the closure on top of the
10731164 /// stack.
1165+ /// When `optionals` is set to true, optional parameters are supported.
1166+ /// When `atomic` is set to true, defaults of optional parameters are evaluated in `env`.
1167+ /// When `tagged` is set to true, it is assumed a tag is on the stack and this tag is stored
1168+ /// in the resulting closure.
1169+ /// When `continuation` is set to true, the resulting closure is represented as a continuation.
10741170 public func compileLambda( _ nameIdx: Int ? ,
10751171 _ arglist: Expr ,
10761172 _ body: Expr ,
10771173 _ env: Env ,
1174+ optionals: Bool = false ,
1175+ atomic: Bool = true ,
10781176 tagged: Bool = false ,
10791177 continuation: Bool = false ) throws {
10801178 // Create closure compiler as child of the current compiler
@@ -1085,20 +1183,39 @@ public final class Compiler {
10851183 let ( arguments, next) = closureCompiler. collectArguments ( arglist)
10861184 switch next {
10871185 case . null:
1186+ // Handle arguments
10881187 closureCompiler. emit ( . assertArgCount( arguments. count) )
1188+ closureCompiler. arguments = arguments
1189+ closureCompiler. env = . local( arguments)
1190+ // Compile body
1191+ try closureCompiler. compileBody ( body, localDefine: true )
10891192 case . symbol( let sym) :
1193+ // Handle arguments
10901194 if arguments. count > 0 {
10911195 closureCompiler. emit ( . assertMinArgCount( arguments. count) )
10921196 }
10931197 closureCompiler. emit ( . collectRest( arguments. count) )
10941198 arguments. allocBindingFor ( sym)
1199+ closureCompiler. arguments = arguments
1200+ closureCompiler. env = . local( arguments)
1201+ // Compile body
1202+ try closureCompiler. compileBody ( body, localDefine: true )
1203+ case . pair( . pair( . symbol( _) , _) , _) :
1204+ // Handle arguments
1205+ if arguments. count > 0 {
1206+ closureCompiler. emit ( . assertMinArgCount( arguments. count) )
1207+ }
1208+ closureCompiler. emit ( . collectRest( arguments. count) )
1209+ closureCompiler. arguments = arguments
1210+ closureCompiler. env = . local( arguments)
1211+ // Compile body
1212+ try closureCompiler. compileBody ( body,
1213+ optionals: next,
1214+ optenv: atomic ? env : nil ,
1215+ localDefine: true )
10951216 default :
10961217 throw RuntimeError . eval ( . malformedArgumentList, arglist)
10971218 }
1098- closureCompiler. arguments = arguments
1099- closureCompiler. env = . local( arguments)
1100- // Compile body
1101- try closureCompiler. compileBody ( body, localDefine: true )
11021219 // Link compiled closure in the current compiler
11031220 let codeIndex = self . fragments. count
11041221 let code = closureCompiler. bundle ( )
0 commit comments