Swift AST written in Swift. Part 5 of ∞

Alexey Demedeckiy
3 min readMar 22, 2017

--

In previous part I have covered function declaration. I started simplify grammar for AST needs. Making resulting types as easy to use as I can.

In this part I will cover enumeration declaration. As usually, let’s look on grammar part:

First of all, surprisingly, raw value enum has special syntax. It is actually two different declarations. And enum declaration itself is simply one of 2.

So let’s model them one by one first.

Union enum declaration

Union enum consists from ab optional indirection modifier, name, optional generic parameters, optional type inheritance, optional generic constraints and list of members. Struct, I choose you!

struct UnionEnumDeclaration {
let indirect: Indirect?
let name: Identifier
let genericParameters: GenericParameterClause?
let typeInheritance: TypeInheritanceClause?
let genericConstraints: GenericWhereClause?
let members: [UnionEnumMember]
}

There is two options how to model indirect state. Temporary, I choose to model it not as bool, but as optional enum with single case.

enum Indirect { case indirect }

I hope this will make building AST from code more intuitive than passing bools around.

Enum body consists from array of member definitions. There is 3 kind of members in possible inside enum:

  1. Regular declaration as nested classes or functions.
  2. Actual enum cases
  3. Compiler statements for conditional compilation etc.

I model all cases as enums, lets model enum body as enum:

enum UnionEnumMember {
case `case`(UnionEnumCaseClause)
case declaration(Declaration)
case compilerControl(CompilerControlStatement)
}

Compiler control statements is out of our scope for this post, declaration is already defined in second post of this cycle, so we need to focus on case clause:

struct UnionEnumCaseClause {
let attributes: [Attribute]
let indirect: Indirect?
let cases: [UnionEnumCase]
}
struct UnionEnumCase {
let name: Identifier
let type: TupleType
}

As you can notice, we can declare several cases with single case construct, thats why model it as array of actual case definitions.

Raw value enum declaration

Raw enums consists from name, optional generic parameters, required type inheritance, optional generic constraints and array of members. Pretty similar to union value.

Also I have never seen Raw enumeration with generics. Do you know any example?

Let’s code this type:

struct RawEnumDeclaration {
let name: Identifier
let genericParameters: GenericParameterClause?
let typeInheritance: TypeInheritanceClause
let genericConstraints: GenericWhereClause?
let members: [RawEnumMember]
}

Raw member declaration is effectively the same:

enum RawEnumMember {
case `case`(RawEnumCaseClause)
case declaration(Declaration)
case compilerControl(CompilerControlStatement)
}

Case clause also practically the same, only difference here is that it cannot be marked as indirect.

struct RawEnumCaseClause {
let attributes: [Attribute]
let cases: [RawEnumCase]
}

Single case is where all difference lives. It consist from name and optional raw value. There is limitation what kind of values can be assigned to cases. Currently it is numbers, strings and boolean.

struct RawEnumCase {
let name: Identifier
let rawValue: RawValueLiteral?
}
enum RawValueLiteral {
case numeric(NumericLiteral)
case string(StaticStringLiteral)
case boolean(BooleanLiteral)
}

Finally, when we declare our possible enum declarations, we can wrap in a single enum:

enum EnumDeclaration {
case union(UnionEnumDeclaration)
case raw(RawEnumDeclaration)
}

This is it for today. In this post we discovered how two enums are different and in next post I will cover struct and class declarations.

--

--