All Downloads are FREE. Search and download functionalities are using the official Maven repository.

e.thrift-parser_2.10.3.0.0-M15.source-code.ThriftParser.scala Maven / Gradle / Ivy

The newest version!
// Copyright 2013 Foursquare Labs Inc. All Rights Reserved.

package com.foursquare.spindle.codegen.parser

import com.twitter.thrift.descriptors
import java.io.File
import java.nio.charset.Charset
import org.apache.commons.io.FileUtils
import org.parboiled.errors.ErrorUtils
import org.parboiled.matchers.{Matcher, ProxyMatcher}
import org.parboiled.scala.{ANY, EOI, MemoMismatches, Parser, ParseRunner, ReportingParseRunner, Rule, Rule0, Rule1,
    Rule2, RuleOption, SkipNode, SuppressNode, SuppressSubnodes, TracingParseRunner}
import scala.collection.mutable
import scala.util.DynamicVariable

class ThriftParser extends Parser {
  lazy val Letter = rule("Letter")("a" - "z" | "A" - "Z")
  lazy val Sign: Rule0 = rule("Sign") { optional(anyOf("+-")) }
  lazy val Digit: Rule0 = rule("Digit") { "0" - "9" }
  lazy val Digits: Rule0 = rule("Digits") { oneOrMore(Digit) }
  lazy val HexDigit: Rule0 = rule("HexDigit") { Digit | "a" - "f" | "A" - "F" }

  lazy val HexConstant: Rule1[String] = rule("HexConstant") { "0x" ~ oneOrMore(HexDigit) } ~> identity
  lazy val IntConstant: Rule1[String] = rule("IntConstant") { Sign ~ Digits } ~> identity
  lazy val IdConstant: Rule1[Int] = rule("IdConstant") (
    /* HexConstant must come first to avoid ambiguities in the grammar */
    HexConstant ~~> (hexString => Integer.parseInt(hexString.stripPrefix("0x"), 16)) |
    IntConstant ~~> (intString => Integer.parseInt(intString, 10))
  )

  lazy val Frac: Rule0 = rule("Frac") { "." ~ Digits }
  lazy val Exp: Rule0 = rule("Exp") { ignoreCase("e") ~ Sign ~ Digits }
  lazy val DoubleConstant: Rule1[String] = rule("DoubleConstant") (
    Sign ~ zeroOrMore(Digit) ~ Frac ~ optional(Exp) |
    Sign ~ zeroOrMore(Digit) ~ optional(Frac) ~ Exp
  ) ~> identity

  lazy val Identifier: Rule1[String] = rule("Identifier") {
    (Letter | "_") ~ zeroOrMore("." | Letter | "_" | Digit)
  } ~> identity

  lazy val StIdentifier: Rule1[String] = rule("StIdentifier") {
    (Letter | "_") ~ zeroOrMore("." | Letter | "_" | Digit | "-")
  } ~> identity

  lazy val MultiComment: Rule0 = rule("MultiComment") { "/*" ~ zeroOrMore(!"*/" ~ ANY) ~ "*/" }
  lazy val EOLComment: Rule0 = rule("EOLComment") { "//" ~ zeroOrMore(!"\n" ~ ANY) }
  lazy val UnixComment: Rule0 = rule("UnixComment") { "#" ~ zeroOrMore(!"\n" ~ ANY) }
  lazy val Comment: Rule0 = rule("Comment")(MultiComment | EOLComment | UnixComment)

  lazy val Spaces: Rule0 = rule("Spaces") { oneOrMore(anyOf(" \t")) }
  lazy val Newline: Rule0 = rule("Newline") { ("\n" | "\r\n") }
  lazy val EOL: Rule0 = rule("EOL") { zeroOrMore(Spaces | Comment) ~ Newline }
  lazy val EOC: Rule0 = rule("EOC") { zeroOrMore(Spaces | Comment) ~ anyOf(",;") }
  lazy val Deadspace: Rule0 = rule("Deadspace") { zeroOrMore(Spaces | Newline | Comment) }
  lazy val Lines: Rule0 = rule("Lines") { EOL ~ Deadspace }

  lazy val LiteralChoice: Rule2[String, String] = rule("LiteralChoice") {
    ("'" ~ zeroOrMore(!"'" ~ ANY) ~> identity ~ "'") |
    ("\"" ~ zeroOrMore(!"\"" ~ ANY) ~> identity ~ "\"")
  } ~> identity
  lazy val Literal: Rule1[String] = rule("Literal") { LiteralChoice ~~> ((text, source) => text) }
  lazy val LiteralConstant: Rule1[String] = rule("LiteralConstant") { LiteralChoice ~~> ((text, source) => source) }

  lazy val BlockSeparator: Rule0 = rule("BlockSeparator") { zeroOrMore(Spaces | Comment) ~ ((anyOf(",;") ~ Newline) | Newline | anyOf(",;")) ~ Deadspace }
  lazy val ArgSeparator: Rule0 = rule("ArgSeparator") { EOC ~ Deadspace }
  def block[T](rule: Rule1[T]): Rule1[List[T]] = {
    Deadspace ~ "{" ~ Deadspace ~ zeroOrMore(rule, BlockSeparator) ~ optional(BlockSeparator) ~ Deadspace ~ "}"
  }
  def argList[T](rule: Rule1[T], allowNewlineSeparator: Boolean = false): Rule1[List[T]] = {
    val separator = if (allowNewlineSeparator) BlockSeparator else ArgSeparator
    Deadspace ~ "(" ~ Deadspace ~ zeroOrMore(rule, separator) ~ optional(separator) ~ Deadspace ~ ")"
  }

  lazy val Program: Rule1[descriptors.Program] = rule("Program")(
    (Deadspace ~ zeroOrMore(Header ~ optional(EOC), Lines) ~ Deadspace ~ zeroOrMore(Definition ~ optional(EOC), Lines) ~ Deadspace ~ EOI) ~~> ((headers, definitions) => {
      val result = descriptors.Program.createRawRecord
      headers.foreach(header => result.merge(header))
      definitions.foreach(definition => result.merge(definition))
      val aliasToTypeId = Map() ++ result.typedefs.map(td => td.typeAlias -> td.typeId)
      val typeRegistry = descriptors.TypeRegistry(idToTypeBuilder.result, aliasToTypeId)
      result.typeRegistry_=(typeRegistry)
      if (!result.namespacesIsSet) result.namespaces_=(Nil)
      if (!result.includesIsSet) result.includes_=(Nil)
      if (!result.constantsIsSet) result.constants_=(Nil)
      if (!result.enumsIsSet) result.enums_=(Nil)
      if (!result.typedefsIsSet) result.typedefs_=(Nil)
      if (!result.structsIsSet) result.structs_=(Nil)
      if (!result.unionsIsSet) result.unions_=(Nil)
      if (!result.exceptionsIsSet) result.exceptions_=(Nil)
      if (!result.servicesIsSet) result.services_=(Nil)
      result
    })
  )

  lazy val Header: Rule1[descriptors.Program] = rule("Header")(
    Include ~~> (include => descriptors.Program.newBuilder.includes(List(include)).typeRegistry(null).result()) |
    Namespace ~~> (namespace => descriptors.Program.newBuilder.namespaces(List(namespace)).typeRegistry(null).result()))

  lazy val Include: Rule1[descriptors.Include] = rule("Include")(
    ("include" ~ Spaces ~ Literal) ~~> (include => descriptors.Include(include)) |
    ("cpp_include" ~ Spaces ~ Literal) ~~> (include => descriptors.Include(include)))

  lazy val Namespace: Rule1[descriptors.Namespace] = rule("Namespace")(
    ("namespace" ~ Spaces ~ Identifier ~ Spaces ~ Identifier) ~~> ((lang, path) => descriptors.Namespace(lang, path)) |
    ("namespace" ~ Spaces ~ "*" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("*", path)) |
    ("cpp_namespace" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("cpp", path)) |
    ("php_namespace" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("php", path)) |
    ("py_module" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("py", path)) |
    ("perl_package" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("perl", path)) |
    ("ruby_namespace" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("ruby", path)) |
    ("smalltalk_category" ~ Spaces ~ StIdentifier) ~~> (path => descriptors.Namespace("smalltalk_category", path)) |
    ("smalltalk_prefix" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("smalltalk_prefix", path)) |
    ("java_package" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("java", path)) |
    ("scala_package" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("scala", path)) | 
    ("cocoa_package" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("cocoa", path)) |
    ("xsd_namespace" ~ Spaces ~ Literal) ~~> (path => descriptors.Namespace("xsd", path)) |
    ("csharp_namespace" ~ Spaces ~ Identifier) ~~> (path => descriptors.Namespace("csharp", path))
  )

  lazy val Definition: Rule1[descriptors.Program] = rule("Definition")(
    TypeDefinition |
    Const ~~> (const => descriptors.Program.newBuilder.constants(List(const)).typeRegistry(null).result()) |
    Service ~~> (service => descriptors.Program.newBuilder.services(List(service)).typeRegistry(null).result())
  )

  lazy val TypeDefinition: Rule1[descriptors.Program] = rule("TypeDefinition")(
    Struct ~~> (struct => descriptors.Program.newBuilder.structs(List(struct)).typeRegistry(null).result()) |
    Typedef ~~> (typedef => descriptors.Program.newBuilder.typedefs(List(typedef)).typeRegistry(null).result()) |
    Enum ~~> (enum => descriptors.Program.newBuilder.enums(List(enum)).typeRegistry(null).result()) |
    // Senums are parsed but not represented in the AST. Return an empty Program.
    Senum ~~> (senum => descriptors.Program.newBuilder.typeRegistry(null).result()) |
    Union ~~> (union => descriptors.Program.newBuilder.unions(List(union)).typeRegistry(null).result()) |
    Xception ~~> (exception => descriptors.Program.newBuilder.exceptions(List(exception)).typeRegistry(null).result())
  )

  lazy val Typedef: Rule1[descriptors.Typedef] = rule("Typedef")(
    ("typedef" ~ Spaces ~ FieldType ~ optional(Spaces) ~ Identifier ~ optional(TypeAnnotations)) ~~> ((tpe, name, annots) => {
      (descriptors.Typedef.newBuilder
        .typeId(tpe)
        .typeAlias(name)
        .__annotations(annots)
        .result())
    })
  )

  lazy val EnumDefExplicitId: Rule1[descriptors.EnumElement] = rule("EnumDefExplicitId")(
    (Identifier ~ optional(Spaces) ~ "=" ~ optional(Spaces) ~ IdConstant ~ optional(TypeAnnotations)) ~~> {
      (name, id, annots) => {
        (descriptors.EnumElement
          .newBuilder
          .name(name)
          .value(id)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val EnumDefImplicitId: Rule1[descriptors.EnumElement] = rule("EnumDefImplicitId")(
    (Identifier ~ optional(TypeAnnotations)) ~~> {
      (name, annots) => {
        (descriptors.EnumElement
          .newBuilder
          .name(name)
          .value(-1)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val EnumDef: Rule1[descriptors.EnumElement] = rule("EnumDef")(EnumDefExplicitId | EnumDefImplicitId)

  lazy val Enum: Rule1[descriptors.Enum] = rule("Enum") {
    ("enum" ~ Spaces ~ Identifier ~ block(EnumDef) ~ optional(TypeAnnotations)) ~~> {
      (name, elems, annots) => {
        var _nextId = 0
        val numberedElems =
          for (elem <- elems) yield {
            val id = if (elem.value == -1) _nextId else elem.value
            _nextId = id + 1
            elem.copy(value = id)
          }

        val nums = numberedElems.map(_.value)
        assert(nums.size == nums.distinct.size)

        (descriptors.Enum
          .newBuilder
          .name(name)
          .elements(numberedElems)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  }

  lazy val Senum: Rule1[Unit] = rule("Senum") { 
    ("senum" ~ Spaces ~ Identifier ~ block(SenumDef) ~ optional(TypeAnnotations)) ~~> {
      (name, elems, annots) => {
        // Senums are parsed but not represented in the AST. Return Unit.
        ()
      }
    }
  }

  lazy val SenumDef: Rule1[String] = rule("SenumDef") { Literal }

  lazy val Const: Rule1[descriptors.Const] = rule("Const")(
    ("const" ~ Spaces ~ FieldType ~ optional(Spaces) ~ Identifier ~ optional(Spaces) ~ "=" ~ optional(Spaces) ~ ConstValue) ~~> { (tpe, name, value) =>
      descriptors.Const(tpe, name, value)
    }
  )

  lazy val ConstValue: Rule1[String] = rule("ConstValue")(
    DoubleConstant |
    HexConstant |
    IntConstant |
    LiteralConstant |
    Identifier |
    ConstList |
    ConstMap)

  lazy val ConstList: Rule1[String] = rule("ConstList") {
    "[" ~ optional(Deadspace) ~ zeroOrMore(ConstValue, optional(EOC ~ Deadspace)) ~ optional(Deadspace) ~ "]"
  } ~> identity ~~> ((_, s) => s)

  lazy val ConstMap: Rule1[String] = rule("ConstMap") {
    "{" ~ optional(Deadspace) ~ zeroOrMore(ConstValuePair, optional(EOC ~ Deadspace)) ~ optional(Deadspace) ~ "}"
  } ~> identity ~~> ((_, s) => s)

  lazy val ConstValuePair: Rule1[String] = rule("ConstValuePair") {
    optional(Spaces) ~ ConstValue ~ optional(Spaces) ~ ":" ~ optional(Spaces) ~ ConstValue
  } ~> identity ~~> ((_, _, s) => s)

  lazy val Struct: Rule1[descriptors.Struct] = rule("Struct")(
    "struct" ~ Spaces ~ Identifier ~ optional(Spaces) ~ optional("xsdAll") ~ block(Field) ~ optional(TypeAnnotations) ~~> {
      (name, fields, annots) => {
        (descriptors.Struct
          .newBuilder
          .name(name)
          .__fields(fields)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val Union: Rule1[descriptors.Union] = rule("Union")(
    "union" ~ Spaces ~ Identifier ~ optional(Spaces) ~ optional("xsdAll") ~ block(Field) ~ optional(TypeAnnotations) ~~> {
      (name, fields, annots) => {
        (descriptors.Union
          .newBuilder
          .name(name)
          .__fields(fields)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val Xception: Rule1[descriptors.Exception] = rule("Xception")(
    "exception" ~ Spaces ~ Identifier ~ block(Field) ~ optional(TypeAnnotations) ~~> {
      (name, fields, annots) => {
        (descriptors.Exception
          .newBuilder
          .name(name)
          .__fields(fields)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val Service: Rule1[descriptors.Service] = rule("Service")(
    "service" ~ Spaces ~ Identifier ~ optional(Extends) ~ block(Function) ~ optional(TypeAnnotations) ~~> {
      (name, extendz, functions, annots) => {
        (descriptors.Service
          .newBuilder
          .name(name)
          .extendz(extendz)
          .functions(functions)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val Extends: Rule1[String] = rule("Extends") { Deadspace ~ "extends" ~ Spaces ~ Identifier }

  lazy val Oneway: Rule1[Boolean] = rule("Oneway") { optional("oneway" ~ Spaces) ~> (_.startsWith("oneway")) }

  lazy val Function: Rule1[descriptors.Function] = rule("Function")(
    (Oneway ~ FunctionType ~ Spaces ~ Identifier ~ argList(Field) ~ optional(Throws) ~ optional(TypeAnnotations)) ~~> {
      (oneway, tpe, name, argz, throwz, annots) => {
        (descriptors.Function
          .newBuilder
          .name(name)
          .returnTypeId(tpe)
          .oneWay(oneway)
          .argz(argz)
          .throwz(throwz.getOrElse(Nil))
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val Throws: Rule1[List[descriptors.Field]] = rule("Throws") { Deadspace ~ "throws" ~ argList(Field) }

  lazy val Field: Rule1[descriptors.Field] = rule("Field")(
    FieldIdentifier ~ optional(Spaces) ~ optional(FieldRequiredness ~ Spaces) ~ FieldType ~ optional(Spaces) ~
    Identifier ~ optional(Spaces) ~ optional(FieldValue) ~ optional(Spaces) ~ optional("xsdOptional") ~
    optional(Spaces) ~ optional("xsdNillable") ~ optional(Spaces) ~ optional(XsdAttributes) ~
    optional(TypeAnnotations) ~~> {
      (id, reqd, tpe, name, value, _, annots) => {
        (descriptors.Field
          .newBuilder
          .identifier(id.toShort)
          .name(name)
          .typeId(tpe)
          .requiredness(reqd)
          .defaultValue(value)
          .__annotations(annots.getOrElse(Nil))
          .result())
      }
    }
  )

  lazy val XsdAttributes: Rule1[List[descriptors.Field]] = rule("XsdAttributes") { "xsd_attributes" ~ block(Field) }

  lazy val FieldIdentifier: Rule1[Int] = rule("FieldIdentifier") { IdConstant ~ optional(Spaces) ~ ":" }

  lazy val FieldRequiredness: Rule1[descriptors.Requiredness] = rule("FieldRequiredness")(
    "required" ~> (_ => descriptors.Requiredness.REQUIRED) |
    "optional" ~> (_ => descriptors.Requiredness.OPTIONAL)
  )

  lazy val FieldValue: Rule1[String] = rule("FieldValue") { "=" ~ optional(Spaces) ~ ConstValue }

  lazy val FunctionType: Rule1[Option[String]] = rule("FunctionType")(
    "void" ~> (_ => None) |
    FieldType ~~> (tpe => Some(tpe))
  )

  var _nextTypeId = 0

  val idToTypeBuilder = Map.newBuilder[String, descriptors.Type]
  val aliasToTypeIdBuilder = Map.newBuilder[String, String]

  def nextTypeId = {
    val id = _nextTypeId
    _nextTypeId += 1
    "T" + id
  }

  def addType(simpleType: descriptors.SimpleType): String = {
    val id = nextTypeId
    val tpe = descriptors.Type(id, simpleType)
    idToTypeBuilder += ((id, tpe))
    id
  }

  lazy val FieldType: Rule1[String] = rule("FieldType") {
    BaseType ~~> (baseType => {
      val simpleType = descriptors.SimpleType.newBuilder.baseType(baseType).result()
      addType(simpleType)
    }) |
    ContainerType ~~> (containerType => {
      val simpleType = descriptors.SimpleType.newBuilder.containerType(containerType).result()
      addType(simpleType)
    }) |
    Identifier ~~> (identifier => {
      val typeref = descriptors.Typeref(identifier)
      val simpleType = descriptors.SimpleType.newBuilder.typeref(typeref).result()
      addType(simpleType)
    })
  }

  lazy val BaseType: Rule1[descriptors.BaseType] = rule("BaseType") {
    SimpleBaseType ~ optional(TypeAnnotations)
  } ~~> ((sbt, annots) => descriptors.BaseType(sbt, annots.getOrElse(Nil)))

  lazy val SimpleBaseType: Rule1[descriptors.SimpleBaseType] = rule("SimpleBaseType")(
    "string" ~ push(descriptors.SimpleBaseType.STRING) |
    "binary" ~ push(descriptors.SimpleBaseType.BINARY) |
    "slist" ~> (_ => throw new RuntimeException("slist not a supported type")) |
    "bool" ~ push(descriptors.SimpleBaseType.BOOL) |
    "byte" ~ push(descriptors.SimpleBaseType.BYTE) |
    "i16" ~ push(descriptors.SimpleBaseType.I16) |
    "i32" ~ push(descriptors.SimpleBaseType.I32) |
    "i64" ~ push(descriptors.SimpleBaseType.I64) |
    "double" ~ push(descriptors.SimpleBaseType.DOUBLE)
  )

  lazy val ContainerType: Rule1[descriptors.ContainerType] = rule("ContainerType") {
    SimpleContainerType ~ optional(TypeAnnotations)
  } ~~> ((simpleContainerType, annots) => descriptors.ContainerType(simpleContainerType, annots.getOrElse(Nil)))

  lazy val SimpleContainerType: Rule1[descriptors.SimpleContainerType] = rule("SimpleContainerType") {
    MapType ~~> (mapType => descriptors.SimpleContainerType.newBuilder.mapType(mapType).result()) |
    SetType ~~> (setType => descriptors.SimpleContainerType.newBuilder.setType(setType).result()) |
    ListType ~~> (listType => descriptors.SimpleContainerType.newBuilder.listType(listType).result())
  }

  lazy val MapType: Rule1[descriptors.MapType] = rule("MapType") {
    ("map" ~ optional(CppType) ~ optional(Spaces) ~ "<" ~ optional(Spaces) ~ FieldType ~ optional(Spaces) ~
      "," ~ optional(Spaces) ~ FieldType ~ optional(Spaces) ~ ">")
  } ~~> ((_, keyTypeId, valueTypeId) => descriptors.MapType(keyTypeId, valueTypeId))

  lazy val SetType: Rule1[descriptors.SetType] = rule("SetType") {
    ("set" ~ optional(CppType) ~ optional(Spaces) ~ "<" ~ optional(Spaces) ~ FieldType ~ optional(Spaces) ~
      ">")
  } ~~> ((_, typeId) => descriptors.SetType(typeId))

  lazy val ListType: Rule1[descriptors.ListType] = rule("ListType") {
    ("list" ~ "<" ~ optional(Spaces) ~ FieldType ~ optional(Spaces) ~ ">" ~ optional(CppType))
  } ~~> ((typeId, _) => descriptors.ListType(typeId))

  lazy val CppType: Rule1[String] = rule("CppType") { Spaces ~ "cpp_type" ~ Spaces ~ Literal }

  lazy val TypeAnnotations: Rule1[List[descriptors.Annotation]] = rule("TypeAnnotations") {
    argList(TypeAnnotation, allowNewlineSeparator = true)
  }

  lazy val TypeAnnotation: Rule1[descriptors.Annotation] = rule("TypeAnnotation") {
    Identifier ~ optional(Spaces) ~ "=" ~ optional(Spaces) ~ Literal
  } ~~> ((key, value) => descriptors.Annotation(key, value))

  /**
   * The code below is copied from Parboiled's Parser.scala and re-written to avoid getting stack traces.
   * This gives us a ~20x speedup from the standard Parboiled parser setup.
   */
  override def rule[T <: Rule](label: String, options: RuleOption*)(block: => T)(implicit creator: Matcher => T): T = {
    myRule(label, label, options, block, creator)
  }

  private val cache = mutable.Map.empty[String, Rule]
  private val lock = new AnyRef()

  private def myRule[T <: Rule](label: String, key: String, options: Seq[RuleOption], block: => T, creator: Matcher => T): T =
    lock.synchronized {
      cache.get(key) match {
        case Some(rule) => rule.asInstanceOf[T]
        case None => {
          val proxy = new ProxyMatcher
          // protect block from infinite recursion by immediately caching a new Rule of type T wrapping the proxy creator
          cache += key -> creator(proxy)
          var rule = ThriftParser.withCurrentRuleLabel(label) { block.label(label) } // evaluate rule definition block
          if (!buildParseTree || options.contains(SuppressNode)) rule = rule.suppressNode
          if (options.contains(SuppressSubnodes)) rule = rule.suppressSubnodes
          if (options.contains(SkipNode)) rule = rule.skipNode
          if (options.contains(MemoMismatches)) rule = rule.memoMismatches
          proxy.arm(rule.matcher) // arm the proxy in case it is in use
          cache += key -> rule // replace the cache value with the actual rule (overwriting the proxy rule)
          rule
        }
      }
    }
}

class ParserException(val message: String, val file: File) extends RuntimeException(file.getPath + ": " + message)

object ThriftParser {
  private val currentRuleLabel = new DynamicVariable[String](null)
  private val currentActionIndex = new DynamicVariable[Int](0)

  private def withCurrentRuleLabel[A](s: String)(f: => A): A =
    currentRuleLabel.withValue(s) {
      currentActionIndex.withValue(0) {
        f
      }
    }

  def parseProgram(name: String): descriptors.Program = {
    parseProgram(new File(name))
  }

  def parsePrograms(files: Seq[File]): Seq[descriptors.Program] = {
    files.map(file => parseProgram(file))
  }

  def parseProgram(file: File, trace: Boolean = false): descriptors.Program = {
    val parser = new ThriftParser
    val runner: ParseRunner[descriptors.Program] =
      if (trace) TracingParseRunner(parser.Program) else ReportingParseRunner(parser.Program)
    val thrift = FileUtils.readFileToString(file, "UTF-8")
    val runResult = runner.run(thrift)
    val result = runResult.result.getOrElse(throw new ParserException(ErrorUtils.printParseErrors(runResult), file))
    val validator = new ThriftValidator(file)
    validator.validateProgram(result)
    result
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy