Background: I'm writing a compiler for a toy language in Swift, and I want an elegant way to create ASTs for my language. The AST type for a statement in my toy language looks like this:
indirect enum Statement {
case assignment(variable: String, expression: Expression)
case conditional(`if`: Expression, then: Statement, `else`: Statement)
case loop(`while`: Expression, `do`: Statement)
case sequence(Statement, Statement)
case noop
case halt
}
Right now, if I want to write the AST for a series of statements, I would have to write:
// let's say I want to represent a series of 4 no-ops:
.sequence(.noop,
.sequence(.noop,
.sequence(.noop,
.noop)))
That looks very verbose. I thought it would be nice if I could use the @resultBuilder feature, so that I can write:
Statement {
.noop
.noop
.noop
.noop
}
This is my attempt:
@resultBuilder
struct StatementBuilder {
static func buildBlock(_ components: Statement...) -> Statement {
if components.isEmpty {
return .noop
} else {
return components.dropFirst().reduce(components.first!) { x, y in .sequence(x, y) }
}
}
}
extension Statement {
init(@StatementBuilder block: () -> Statement) {
self = block()
}
}
However, this gives me the error:
Cannot infer contextual base in reference to member 'noop'
in the Statement { ... } block.
What is unclear about the contextual base? What type can it be, other than Statement? I could fix this by prefixing everything with Statement., but that's too verbose. What else can I do?
Note that I also plan on overloading operators so that I can easily create assignments and expressions, conforming the syntax tree types to ExpressibleXXXLiteral, and adding If and While functions taking StatementBuilders, which create .conditional and .loop statements. So the result builder will be far more useful than just for creating no-ops and halts.