Skip to content

Instantly share code, notes, and snippets.

@landonf
Created June 3, 2014 18:53
Show Gist options
  • Save landonf/de27286aaca1ae8833a1 to your computer and use it in GitHub Desktop.
Save landonf/de27286aaca1ae8833a1 to your computer and use it in GitHub Desktop.
AST example
/*
* Copyright (c) 2014 Plausible Labs Cooperative, Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package borate.protobuf.ast
import coop.plausible.util.Result
import Result.syntax._
import borate.protobuf.FilePosition
import borate.protobuf.types.{LiteralValue, FieldRuleType, OptionType, FieldType}
import scala.math.BigInt
import java.io.File
/**
* An AST representing the .proto IDL language.
*/
private[protobuf] sealed trait AST extends SelfTyped {
type Self <: AST
/** The file position of this AST node */
def position: FilePosition
}
/**
* A comment-documented AST node.
*/
private[protobuf] sealed trait DocumentedNode extends SelfTyped { self:AST =>
/**
* The documentation comment associated with this AST node, if any.
* @return This node's documentation comment.
*/
def docs: Option[Documentation]
/**
* Returning a new instance of this node with an updated [[docs]] value.
*
* @param newValue The [[docs]] to include in the new [[DocumentedNode]] instance.
*/
def updateDocs (newValue: Option[Documentation]): SelfID
}
/**
* An entity that contains child AST nodes.
*/
private[protobuf] sealed trait NodeContainer extends SelfTyped { self =>
/** The child AST node's self type. This creates an explicit binding between the child's self-type (and thus
* the values returned on modification) to its parent's declared `Child` and `CB` types.
*/
type Child = SelfTyped {
type Self <: self.ChildSelf
}
/** The actual concrete type of this parent's child nodes */
type ChildSelf
/** A list of all children within this parent */
def children: List[Child]
/**
* Returning a new instance of this container object with the given list of children.
*
* @param newValue The list of [[children]] to include in the new [[NodeContainer]] instance.
*/
def updateChildren (newValue: List[Child]): SelfID
}
/**
* A protobuf file.
*
* @param filePath The file's declared path.
* @param children All parsed child nodes.
*/
private[protobuf]
sealed case class ProtoFile (filePath: File, children: List[ProtoFile#Child]) extends NodeContainer with SelfTyped {
import ProtoFile._
override type Self = ProtoFile
override type ChildSelf = ProtoFile.ChildNode
/** Accumulates well-typed AST subtypes */
private case class Accumulator (imports: List[ImportStatement] = List(),
packages: List[PackageStatement] = List(),
messages: List[Message] = List(),
opts: List[ProtoFile.OptionNode] = List(),
enums: List[Enum] = List(),
services: List[Service] = List(),
extenders: List[Extend] = List())
/**
* Accumulated lists of well-typed nodes.
* Breaks the node list into distinct typed lists, using match to ensure the compiler warns on any non-exhaustive matches.
*/
private val accum = children.foldLeft(Accumulator()) { (accum, next) =>
next match {
case i:ImportStatement => accum.copy(imports = accum.imports :+ i)
case p:PackageStatement => accum.copy(packages = accum.packages :+ p)
case m:Message => accum.copy(messages = accum.messages :+ m)
case o:OptionNode => accum.copy(opts = accum.opts :+ o)
case e:Extend => accum.copy(extenders = accum.extenders :+ e)
case s:Service => accum.copy(services = accum.services :+ s)
case e:Enum => accum.copy(enums = accum.enums :+ e)
case c:Comment => accum /* We don't accumulate comments */
}
}
/** All imports. */
val imports: List[ImportStatement] = accum.imports
/** All package declarations (there should be only one; this must be validated in a compiler phase). */
val packages: List[PackageStatement] = accum.packages
/** All top-level message declarations. */
val messages: List[Message] = accum.messages
/** All top-level option declarations. */
val options: List[ProtoFile.OptionNode] = accum.opts
/** All top-level enum declarations. */
val enums: List[Enum] = accum.enums
/** All top-level service declarations. */
val services: List[Service] = accum.services
/** All top-level extend declarations. */
val extenders: List[Extend] = accum.extenders
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = this.copy(children = newValue)
}
/**
* ProtoFile's companion object.
*/
private[protobuf] object ProtoFile {
/**
* A trait from which all direct children of [[ProtoFile]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
/**
* An file option value.
*
* This class wraps the file-specific option value with a type conforming to [[ProtoFile.ChildNode]], providing reliable typing
* of direct children of [[Message]] nodes.
*
* @param value The actual option value.
* @param position The position of this option value.
*/
sealed case class OptionNode (value: OptionValue.FileOptionValueScope.ScopedOptionValue, position: FilePosition) extends AST with ProtoFile.ChildNode {
override type Self = OptionNode
}
}
/**
* Import statement.
*
* @param path The imported path.
* @param public If true, imported definitions should be re-exported from the importing proto file.
* @param position The position of this statement.
*/
private[protobuf] sealed case class ImportStatement (path: ImportPath, public: Boolean, position: FilePosition) extends AST with ProtoFile.ChildNode {
override type Self = ImportStatement
}
/**
* An import path.
*
* @param path The import path.
* @param position The position of this path string.
*/
private[protobuf] sealed case class ImportPath (path: String, position: FilePosition) extends AST {
override type Self = ImportPath
}
/**
* A package declaration.
*
* @param name The declared package name.
* @param position The file position of this package statement.
*/
private[protobuf] sealed case class PackageStatement (name: PackageName, position: FilePosition) extends AST with ProtoFile.ChildNode {
override type Self = PackageStatement
}
/**
* A package name.
*
* @param path The qualified package path, eg, List("parent", "package").
* @param position The file position of this package name.
*/
private[protobuf] sealed case class PackageName (path: List[Ident], position: FilePosition) extends AST {
override type Self = PackageName
}
/**
* A protobuf file, message, or field option value.
*/
private[protobuf] sealed abstract class OptionValue extends AST {
override type Self <: OptionValue
}
/**
* All option value types.
*/
private[protobuf] object OptionValue {
/**
* Common option types that require unique path-dependent types for all levels (file, message, or field).
*/
sealed trait OptionValueScope {
/** The option type scope. */
val optionTypeScope: OptionType.OptionTypeScope
/**
* A scope-specific option value.
*/
sealed abstract class ScopedOptionValue extends OptionValue
/**
* A built-in protobuf option value.
*
* @param optionType The option's type.
* @param position The position of this AST node.
* @param value The option's value, or None if no value was included with the option.
*
* @tparam T1 The option's type.
* @tparam V The option's value type.
*/
sealed case class BuiltinOptionValue[+T1 <: optionTypeScope.OT, +V <: Constant] (optionType: optionTypeScope.OT, value: Option[V], position: FilePosition) extends ScopedOptionValue
/**
* An custom option with an unresolved type and field reference.
*
* @param optionRef The option's unresolved type and field reference.
* @param value The option's value.
* @param position The position of the option statement.
*
* @tparam V The option's value type.
*/
sealed case class UnresolvedOptionValue[+V <: Constant] (optionRef: Ref.CustomOptionRef, value: V, position: FilePosition) extends ScopedOptionValue
}
/** File-level option values. */
object FileOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.FileOptionType
}
/** Message-level option values. */
object MessageOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.MessageOptionType
}
/** Field-level option values. */
object FieldOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.FieldOptionType
}
/** Enum-level option values. */
object EnumOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.EnumOptionType
}
/** Enum field-level option values. */
object EnumFieldOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.EnumFieldOptionType
}
/** Service-level option values. */
object ServiceOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.ServiceOptionType
}
/** Service method-level option values. */
object ServiceMethodOptionValueScope extends OptionValueScope {
/** The option type scope. */
override val optionTypeScope = OptionType.ServiceMethodOptionType
}
}
/**
* An extension field number reservation, eg, "extensions 100 to 199".
*
* @param ranges The range(s) reserved for extension use.
*/
private[protobuf] sealed case class ExtensionStatement (ranges: Seq[ExtensionRange], position: FilePosition) extends AST with Message.ChildNode {
override type Self = ExtensionStatement
}
/**
* An extension field number range.
*
* @param start The first tag value in the extension range.
* @param end The last tag value in the extension range. If this range covers only a single value, this may be equal to `start`.
*/
private[protobuf] sealed case class ExtensionRange (start: Tag, end: Option[Tag], position: FilePosition) extends AST {
override type Self = ExtensionRange
/** The range of field numbers covered by this extension range. */
val fieldNumbers: Range = new Range(start.value, end.getOrElse(start).value, 1)
}
/**
* A protobuf field tag. As per the protobuf language guide, the smallest tag number you can specify is
* [[Tag.TagMin]], and the largest is [[Tag.TagMax]] Additionally, the [[Tag.TagReserved]] range is
* reserved for the Protocol Buffers implementation use.
*
* Tag ranges can be validated using [[Tag.parse()]].
*
* @param value The tag's value.
* @param position The file position of this tag.
*/
private[protobuf] sealed case class Tag (value: Int, position: FilePosition) extends AST {
override type Self = Tag
}
/**
* Field tag constants.
*/
private[protobuf] object Tag {
/**
* Construct a new tag, validating that the tag number falls within the protobuf-defined valid range.
*
* @param value The tag's field value.
* @param position The input position to be associated with the validated tag.
* @return A valid Tag instance on success, or an error message if the tag is out-of-range.
*/
def parse (value: BigInt, position: FilePosition): Result[String, Tag] = {
if (value < TagMin.value) {
"Field numbers must be positive (non-zero) integers.".failure
} else if (value > TagMax.value) {
s"Field numbers cannot be greater than ${TagMax.value}".failure
} else if (TagReserved.contains(value)) {
s"Field numbers ${TagReserved.start} through ${TagReserved.end} are reserved for the protocol buffer library implementation".failure
} else {
/* If we got this far, the value *must* be representable as an integer */
assert(value <= Int.MaxValue)
Tag(value.toInt, position).success
}
}
/** The minimum value of a [[Tag]], `1`. */
val TagMin: Int = 1
/** The maximum value of a [[Tag]], `2<sup>29</sup>-1`. */
val TagMax: Int = 536870911
/** The range of tag values reserved for implementation use. */
val TagReserved = 19000 to 19999
}
/**
* An enum declaration.
*
* @param name The enum's name.
* @param children All parsed child nodes.
* @param docs The documentation comment, if any.
* @param position The file position of this enum declaration.
*/
private[protobuf]
sealed case class Enum (name: Ident, children: List[Enum#Child], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
Message.ChildNode with
ProtoFile.ChildNode with
NodeContainer
{
override type ChildSelf = Enum.ChildNode
override type Self = Enum
/** Accumulates well-typed AST subtypes */
private case class Accumulator (fields: List[EnumField] = List(), opts: List[Enum.OptionNode] = List())
/**
* Accumulated lists of well-typed nodes.
* Breaks the node list into distinct typed lists, using match to ensure the compiler warns on any non-exhaustive matches.
*/
private val accum = children.foldLeft(Accumulator()) { (accum, next) =>
next match {
case f:EnumField => accum.copy(fields = accum.fields :+ f)
case o:Enum.OptionNode => accum.copy(opts = accum.opts :+ o)
case c:Comment => accum /* We don't accumulate comments */
}
}
/** All declared enum fields. */
val fields: List[EnumField] = accum.fields
/** All declared enum options. */
val options: List[Enum.OptionNode] = accum.opts
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = this.copy(children = newValue)
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* [[Enum]] companion object.
*/
private[protobuf] object Enum {
/**
* A trait from which all direct children of [[Enum]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
/**
* An enum option value.
*
* This class wraps the enum-specific option value with a type conforming to [[Enum.ChildNode]], providing reliable typing
* of direct children of [[Enum]] nodes.
*
* @param value The actual option value.
* @param position The position of this option value.
*/
sealed case class OptionNode (value: OptionValue.EnumOptionValueScope.ScopedOptionValue, position: FilePosition) extends AST with Enum.ChildNode {
override type Self = OptionNode
}
}
/**
* A protobuf enum value. This must be within [[borate.protobuf.idl.EnumField.EnumValueMin]] and [[borate.protobuf.idl.EnumField.EnumValueMax]].
*
* @param value The enum's integer value.
* @param position The file position of the this enum value.
*/
private[protobuf] sealed case class EnumFieldValue (value: Int, position: FilePosition) extends AST {
override type Self = EnumFieldValue
}
/**
* An enum field declaration.
*
* @param name The enum field's name.
* @param value The enum field's constant value.
* @param options All options set on the field.
* @param docs The documentation comment, if any.
* @param position The file position of this field declaration.
*/
private[protobuf] sealed case class EnumField (name: Ident, value: EnumFieldValue, options: List[OptionValue.EnumFieldOptionValueScope.ScopedOptionValue], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
Enum.ChildNode
{
override type Self = EnumField
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* An extension block. Extension blocks directly declare new fields within a given message.
*
* @param messageType Reference to the extended message's type.
* @param children All parsed child nodes.
* @param docs The documentation comment, if any.
* @param position The file position of this extension declaration.
*/
private[protobuf] sealed case class Extend (messageType: Type, children: List[Extend#Child], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
NodeContainer with
Message.ChildNode with
ProtoFile.ChildNode
{
override type ChildSelf = Extend.ChildNode
override type Self = Extend
/** All declared extension fields. */
val fields: List[Field] = children.foldLeft(List[Field]()) { (accum, next) =>
next match {
case f:Field => accum :+ f
case c:Comment => accum /* Drop comments */
}
}
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = copy(children = newValue)
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* Extend constants and types.
*/
private[protobuf] object Extend {
/**
* A trait from which all direct children of [[Extend]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
}
/**
* An message declaration.
*
* @param name The message's name.
* @param children All parsed child nodes.
* @param docs The documentation comment, if any.
* @param position The file position of this message declaration.
*/
private[protobuf]
sealed case class Message (name: Ident, children: List[Message#Child], docs: Option[Documentation], position: FilePosition) extends
AST with
NodeContainer with
DocumentedNode with
Message.ChildNode with
ProtoFile.ChildNode
{
import Message._
override type ChildSelf = Message.ChildNode
override type Self = Message
/** Accumulates well-typed AST node subtypes */
private case class Accumulator (messages: List[Message] = List(),
fields: List[Field] = List(),
opts: List[OptionNode] = List(),
extenders: List[Extend] = List(),
enums: List[Enum] = List(),
extensions: List[ExtensionStatement] = List())
/**
* Accumulated lists of well-typed nodes.
* Breaks the node list into distinct typed lists, using match to ensure the compiler warns on any non-exhaustive matches.
*/
private val accum = children.foldLeft(Accumulator()) { (accum, next) =>
next match {
case m:Message => accum.copy(messages = accum.messages :+ m)
case f:Field => accum.copy(fields = accum.fields :+ f)
case o:OptionNode => accum.copy(opts = accum.opts :+ o)
case e:Extend => accum.copy(extenders = accum.extenders :+ e)
case e:Enum => accum.copy(enums = accum.enums :+ e)
case e:ExtensionStatement => accum.copy(extensions = accum.extensions :+ e)
case c:Comment => accum /* We don't accumulate comments */
}
}
/** All declared messages. */
val messages: List[Message] = accum.messages
/** All declared message fields. */
val fields: List[Field] = accum.fields
/** All declared message options. */
val options: List[Message.OptionNode] = accum.opts
/** All declared extension ranges. */
val extensions: List[ExtensionStatement] = accum.extensions
/** All declared enums. */
val enums: List[Enum] = accum.enums
/** All declared message extends. */
val extenders: List[Extend] = accum.extenders
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = copy(children = newValue)
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* Message companion object.
*/
private[protobuf] object Message {
/**
* A trait from which all direct children of [[Message]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
/**
* A message option value.
*
* This class wraps the message-specific option value with a type conforming to [[Message.ChildNode]], providing reliable typing
* of direct children of [[Message]] nodes.
*
* @param value The actual option value.
* @param position The position of this option value.
*/
sealed case class OptionNode (value: OptionValue.MessageOptionValueScope.ScopedOptionValue, position: FilePosition) extends AST with Message.ChildNode {
override type Self = OptionNode
}
}
/**
* A field declaration.
*
* @param name The field's name.
* @param fieldType The field rule (e.g., required, optional, repeated).
* @param rule The field's label.
* @param tag The field's tag.
* @param options All options set on the field.
* @param position The file position of this field declaration.
* @param docs The documentation comment, if any.
*/
private[protobuf]
sealed case class Field (name: Ident, fieldType: Type, rule: FieldRule, tag: Tag, options: List[OptionValue.FieldOptionValueScope.ScopedOptionValue], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
Message.ChildNode with
Extend.ChildNode
{
override type Self = Field
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* A field rule (ie, 'required', 'optional', 'repeated').
*
* @param ruleType The field rule type.
* @param position The file position of this field rule.
*/
private[protobuf] sealed case class FieldRule (ruleType: FieldRuleType, position: FilePosition) extends AST {
override type Self = FieldRule
}
/**
* An RPC service declaration.
*
* @param name The service name.
* @param children All parsed child nodes.
* @param docs The documentation comment, if any.
* @param position The file position of this service declaration.
*/
private[protobuf]
sealed case class Service (name: Ident, children: List[Service#Child], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
NodeContainer with
ProtoFile.ChildNode
{
import Service._
override type ChildSelf = Service.ChildNode
override type Self = Service
/** Accumulates well-typed AST subtypes */
private case class Accumulator (methods: List[ServiceMethod] = List(), opts: List[OptionNode] = List())
/**
* Accumulated lists of well-typed nodes.
* Breaks the node list into distinct typed lists, using match to ensure the compiler warns on any non-exhaustive matches.
*/
private val accum = children.foldLeft(Accumulator()) { (accum, next) =>
next match {
case m:ServiceMethod => accum.copy(methods = accum.methods :+ m)
case o:OptionNode => accum.copy(opts = accum.opts :+ o)
case c:Comment => accum /* We don't accumulate comments */
}
}
/** All methods defined within this service declaration. */
val methods: List[ServiceMethod] = accum.methods
/** All options set on this service. */
val options: List[Service.OptionNode] = accum.opts
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = copy(children = newValue)
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* Service constants and types.
*/
private[protobuf] object Service {
/**
* A trait from which all direct children of [[Message]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
/**
* A Service option value.
*
* This class wraps the method-specific option value with a type conforming to [[Service.ChildNode]], providing reliable typing
* of direct children of [[Service]] nodes.
*
* @param value The actual option value.
* @param position The position of this option value.
*/
sealed case class OptionNode (value: OptionValue.ServiceOptionValueScope.ScopedOptionValue, position: FilePosition) extends AST with ChildNode {
override type Self = OptionNode
}
}
/**
* An RPC service method.
*
* @param name The method name.
* @param argumentType The method argument's type.
* @param returnType The method's return type.
* @param children All parsed child nodes.
* @param docs The documentation comment, if any.
* @param position The file position of this method declaration.
*/
private[protobuf]
sealed case class ServiceMethod (name: Ident, argumentType: Type, returnType: Type, children: List[ServiceMethod#Child], docs: Option[Documentation], position: FilePosition) extends
AST with
DocumentedNode with
NodeContainer with
Service.ChildNode
{
import ServiceMethod._
override type ChildSelf = ServiceMethod.ChildNode
override type Self = ServiceMethod
/** All options set on this method. */
val options: List[OptionNode] = children.foldLeft(List[OptionNode]()) { (accum, next) =>
next match {
case o:OptionNode => accum :+ o
case c:Comment => accum /* Drop comments */
}
}
/** Replace the current list of children, returning a new instance of this parent object */
override def updateChildren (newValue: List[Child]) = copy(children = newValue)
/** Returning a new instance of this node with an updated [[docs]] value. */
override def updateDocs (newValue: Option[Documentation]) = copy(docs = newValue)
}
/**
* ServiceMethod constants and types.
*/
private[protobuf] object ServiceMethod {
/**
* A trait from which all direct children of [[ServiceMethod]] inherent.
*/
sealed trait ChildNode extends SelfTyped { self:AST => }
/**
* A ServiceMethod option value.
*
* This class wraps the method-specific option value with a type conforming to [[ServiceMethod.ChildNode]], providing reliable typing
* of direct children of [[ServiceMethod]] nodes.
*
* @param value The actual option value.
* @param position The position of this option value.
*/
sealed case class OptionNode (value: OptionValue.ServiceMethodOptionValueScope.ScopedOptionValue, position: FilePosition) extends AST with ChildNode {
override type Self = OptionNode
}
}
/**
* A comment.
*/
private[protobuf]
sealed trait Comment extends AST
with ProtoFile.ChildNode
with Enum.ChildNode
with Message.ChildNode
with Service.ChildNode
with ServiceMethod.ChildNode
with Extend.ChildNode
{
override type Self <: Comment
/** The comment's lines. */
def lines: List[CommentLine]
}
/**
* A documentation comment.
*
* @param lines The comment block's lines.
* @param position The position of this comment doc.
*/
private[protobuf] sealed case class Documentation (lines: List[CommentLine], position: FilePosition) extends Comment {
override type Self = Documentation
}
/**
* A (non-documentation) comment block
*/
private[protobuf] sealed case class CommentBlock (lines: List[CommentLine], position: FilePosition) extends Comment {
override type Self = CommentBlock
}
/**
* A comment line.
*
* @param value The comment line's string representation.
* @param position The position of this comment line.
*/
private[protobuf] sealed case class CommentLine (value: String, position: FilePosition) extends AST
/**
* A (potentially) unresolved type.
*/
private[protobuf] sealed abstract class Type extends AST {
override type Self = Type
}
/**
* All type identifiers.
*/
private[protobuf] object Type {
/**
* A fully resolved reference to a proto-defined type.
*
* @param fieldType The protobuf field type for this reference.
* @param position The position of this AST node.
*/
sealed case class BuiltinType (fieldType: FieldType, position: FilePosition) extends Type
/**
* Reference to an unresolved type.
*
* @param typeRef The unresolved type identifier.
* @param position The position of this AST node.
*/
sealed case class UnresolvedType (typeRef: Ref.TypeRef, position: FilePosition) extends Type
}
/**
* A constant value.
*/
private[protobuf] sealed trait Constant extends AST {
override type Self <: Constant
}
/**
* A protobuf identifier, e.g. "my_field", "MyMessage", "myField", etc.
*
* @param name The identifier name.
* @param position The position of this identifier.
*/
private[protobuf] sealed case class Ident (name: String, position: FilePosition) extends AST with Constant {
override type Self = Ident
}
/**
* A generic qualfied reference to a type or field.
*/
private[protobuf] sealed trait Ref extends AST {
override type Self <: Ref
/** The reference's qualified path, e.g., List("parent", "child", "grandchild") */
def path: List[Ident]
/** String representation of this reference's path */
def pathString: String = path.map(_.name).mkString(".")
}
/**
* All reference types.
*/
private[protobuf] object Ref {
/**
* A qualified type reference.
*
* @param path The qualified type path, eg, List("parent", "child", "type").
* @param isAbsolute If true, this identifier is absolute. If false, a relative lookup should be attempted first,
* falling back to absolute lookup.
* @param position The position of this type reference.
*/
sealed case class TypeRef (path: List[Ident], isAbsolute: Boolean, position: FilePosition) extends Ref {
override type Self = TypeRef
}
/**
* A qualified field reference.
*
* @param path The qualified field path, e.g., List("Message", "fieldName")
* @param position The position of this field reference.
*/
sealed case class FieldRef (path: List[Ident], position: FilePosition) extends Ref {
override type Self = FieldRef
}
/**
* An extension-defined custom option type reference.
*
* @param typeRef The option's type reference.
* @param fieldRef The option's field reference. This will either refer to a field within a Message-typed option, or None.
* @param position The position of the option's name.
*/
sealed case class CustomOptionRef (typeRef: TypeRef, fieldRef: Option[FieldRef], position: FilePosition) extends AST {
override type Self = CustomOptionRef
}
}
/**
* A literal (i.e., constant) value.
*
* @param value This associated literal value.
* @param position The position of this literal.
* @tparam T The [[LiteralValue]] type.
*/
private[protobuf] sealed case class Literal[T <: LiteralValue] (value: T, position: FilePosition) extends AST with Constant {
override type Self = Literal[T]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment