Obviously, this is a circular reference as the first variable needs the last one for the init method.
How can I avoid this? Internally I used a (non-optional) let next: ExpressionFactory to store the references to the next level. Do I have to make this optional and assign one level at a later time? When?
(I don't care for the strong reference cycle internally, these are all singletons)
You can't create a reference cycle with let properties. So here you need to initialize those variables later. But if you want to keep things working almost as they are, you could transform let _next to a computed property and instead store an enum to identify which factory singleton to use as the next one:
enum ExpressionFactoryID {
case primary
...
}
init(_ level: Int, _ nextID: ExpressionFactoryID) {
_level = level
_nextID = nextID
}
let _nextID: ExpressionFactoryID
var _next: ExpressionFactory {
switch _nextID {
case .primary: return primary
...
}
}
static let primary = PrimaryExpressionFactory(.assign)
A bit more code, but you don't have to do a two-step initialization sequence this way.
Thank you very much. At least this will work.
But I still don't like the solution as everything that was in one place is now in three places (enum, switch, declaration). If I can't use _next as a direct reference, I may try to use _level (which I need anyway for different reasons) as an index into an array of ExpressionFactories.
enum Expression: Int {
case primary
case postfix
case unary
case multiplicative
case additive
case shift
case relational
case logical
case conditional
case assign
var next: Expression {
self == .primary ? .assign : Expression(rawValue: rawValue - 1)!
}
var isRightAssociative: Bool {
self == .assign
}
}
You could look up the next factory lazily like this:
class ExpressionFactory {
init(_ level: Int, _ next: @escaping () -> ExpressionFactory) {
_level = level
_next = next
}
let _level: Int
let _next: () -> ExpressionFactory
static let primary = PrimaryExpressionFactory(_assign())
static let postfix = BinaryExpressionFactory(primary)
static let unary = UnaryExpressionFactory(postfix)
static let multiplicative = BinaryExpressionFactory(unary)
static let additive = BinaryExpressionFactory(multiplicative)
static let shift = BinaryExpressionFactory(additive)
static let relational = BinaryExpressionFactory(shift)
static let logical = BinaryExpressionFactory(relational)
static let conditional = ConditionalExpressionFactory(logical)
static let assign = BinaryExpressionFactory(conditional, true)
static func _assign() -> ExpressionFactory { assign }
}
class PrimaryExpressionFactory: ExpressionFactory { init(_ next: @escaping @autoclosure () -> ExpressionFactory) {super.init(0, next)}}
class BinaryExpressionFactory: ExpressionFactory { init(_ next: @escaping @autoclosure () -> ExpressionFactory, _ rightAssoc: Bool = false) {_rightAssoc = rightAssoc; super.init(next()._level + 1, next)}; let _rightAssoc: Bool}
class UnaryExpressionFactory: ExpressionFactory { init(_ next: @escaping @autoclosure () -> ExpressionFactory) {super.init(next()._level + 1, next)}}
class ConditionalExpressionFactory: ExpressionFactory { init(_ next: @escaping @autoclosure () -> ExpressionFactory) { super.init(next()._level + 1, next)}}
Note that I still had to refer to one of the factories indirectly to avoid the circular reference error, even though there is no true circular reference. That's why I used the _assign function instead of naming assign directly. Since each factory subclass init takes an @autoclosure, it won't actually call the _assign function until something calls primary._next().