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

name.lakhin.eliah.projects.papacarlo.Syntax.scala Maven / Gradle / Ivy

/*
   Copyright 2013 Ilya Lakhin (Илья Александрович Лахин)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package name.lakhin.eliah.projects
package papacarlo

import name.lakhin.eliah.projects.papacarlo.lexis.Token
import name.lakhin.eliah.projects.papacarlo.syntax.{State, Rule, Node, Cache}
import name.lakhin.eliah.projects.papacarlo.utils.{Signal, Registry}
import name.lakhin.eliah.projects.papacarlo.syntax.rules.{NamedRule,
  ReferentialRule}

final class Syntax(val lexer: Lexer) {
  final class RuleDefinition(val name: String) {
    private[papacarlo] var productKind: String = name
    private[papacarlo] var cachingFlag = false
    private[papacarlo] var transformer: Option[Node => Node] = None

    private var constructor = Option.empty[() => Rule]
    private[papacarlo] lazy val body = constructor match {
      case Some(bodyConstructor) => bodyConstructor()
      case _ => throw new RuntimeException("Rule " + name + " undefined")
    }

    def produce(kind: String) = {
      productKind = kind

      this
    }

    def main = {
      if (Syntax.this.mainRule.isEmpty) Syntax.this.mainRule = Some(name)

      this
    }

    def cachable = {
      cachingFlag = true
      transformer = None

      this
    }

    def transform(transformer: Node => Node) = {
      cachingFlag = false
      this.transformer = Some(transformer)

      this
    }

    def reference = NamedRule(name, ReferentialRule(name))

    def apply(body: => Rule) = {
      constructor match {
        case None =>
          constructor = Some(() => body)

          if (mainRule.exists(_ == name)) {
            val rootFragment = lexer.fragments.rootFragment
            val rootNode = new Node(name, rootFragment.begin,
              rootFragment.end)

            nodes.add(id => {rootNode.id = id; rootNode})

            new Cache(Syntax.this, rootFragment, rootNode)

            Syntax.this.rootNode = Some(rootNode)
          }

        case _ =>
      }

      reference
    }
  }

  private[papacarlo] var rules = Map.empty[String, RuleDefinition]
  private var rootNode: Option[Node] = None
  private var mainRule: Option[String] = None
  private[papacarlo] var nodes = new Registry[Node]
  private[papacarlo] var cache = Map.empty[Int, Cache]
  private[papacarlo] var deepRecoveryMode = false

  val onCacheCreate = new Signal[Cache]
  val onCacheRemove = new Signal[Cache]
  val onCacheInvalidate = new Signal[Cache]
  val onNodeCreate = nodes.onAdd
  val onNodeMerge = new Signal[Node]
  val onNodeRemove = nodes.onRemove
  val onParseStep = new Signal[Seq[Token]]
  val onRuleEnter = new Signal[(Rule, State)]
  val onRuleLeave = new Signal[(Rule, State, Int)]

  lexer.fragments.onInvalidate.bind {
    case (fragment, range) =>
      var candidate = Option(fragment)

      while (candidate.exists(fragment => !cache.contains(fragment.id)))
        candidate = candidate.flatMap(_.parent)

      for (cache <-
           cache.get(candidate.getOrElse(lexer.fragments.rootFragment).id)) {
        onCacheInvalidate.trigger(cache)
        cache.invalidate(range)
      }
  }

  def getErrors = cache.values.map(cache => cache.errors).flatten.toList

  def rule(name: String) =
    rules.get(name) match {
      case Some(definition) => definition

      case None =>
        val definition = new RuleDefinition(name)

        rules += name -> definition

        definition
    }

  def setDeepRecovery(mode: Boolean) {
    deepRecoveryMode = mode
  }

  def getRootNode = rootNode

  def getNode(id: Int) = nodes.get(id)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy