Created
June 3, 2014 18:53
-
-
Save landonf/de27286aaca1ae8833a1 to your computer and use it in GitHub Desktop.
AST example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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