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

laika.parse.markup.DefaultRecursiveParsers.scala Maven / Gradle / Ivy

/*
 * Copyright 2013-2017 the original author or authors.
 *
 * 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 laika.parse.markup

import laika.ast._
import laika.parse._
import laika.parse.text.TextParsers._

/** Default implementation for parsing inline markup and blocks recursively.
  *
  * @author Jens Halm
  */
trait DefaultRecursiveParsers extends RecursiveParsers with DefaultRecursiveSpanParsers {


  /** The maximum level of block nesting. Some block types like lists
    * and blockquotes contain nested blocks. To protect against malicious
    * input or accidentally broken markup, the level of nesting is restricted.
    */
  val maxNestLevel: Int = 12

  /** Parses any kind of top-level block supported by a concrete markup language.
    */
  protected def rootBlock: Parser[Block]

  /** Parses any kind of nested block supported by a concrete markup language.
    */
  protected def nestedBlock: Parser[Block]

  /**  Parses blocks, excluding blocks that allow nesting.
    *  Only used in rare cases when the maximum nest level allowed had been reached
    */
  protected def fallbackBlock: Parser[Block]

  /** Builds a parser for a list of blocks based on the parser for a single block.
    */
  protected def blockList (p: => Parser[Block]): Parser[Seq[Block]]


  private class RecursiveBlockParser {

    lazy val recursive    = consumeAll(opt(blankLines) ~> blockList(nestedBlock))
    lazy val nonRecursive = consumeAll(opt(blankLines) ~> blockList(fallbackBlock))

    def parse (source: String, nestLevel: Int): Parsed[Seq[Block]] = {
      val newNestLevel = nestLevel + 1
      val p = if (newNestLevel <= maxNestLevel) recursive else nonRecursive
      p.parse(ParserContext(source, newNestLevel))
    }

  }

  private val recursiveBlockParser: RecursiveBlockParser = new RecursiveBlockParser


  def recursiveBlocks (p: Parser[String]): Parser[Seq[Block]] = Parser { ctx =>
    p.parse(ctx) match {
      case Success(str, next) =>
        recursiveBlockParser.parse(str, ctx.nestLevel) match {
          case Success(blocks, _) => Success(blocks, next)
          case f: Failure => f
        }
      case f: Failure => f
    }
  }

  def withRecursiveBlockParser [T] (p: Parser[T]): Parser[(String => Seq[Block], T)] = Parser { ctx =>
    p.parse(ctx) match {
      case Success(res, next) =>
        val recParser: String => Seq[Block] = { source: String =>
          recursiveBlockParser.parse(source, next.nestLevel) match {
            case Success(blocks, _) => blocks
            case Failure(msg, next) =>
              val message = SystemMessage(MessageLevel.Error, msg.message(next))
              val fallback = Paragraph(Seq(Text(source)))
              List(InvalidBlock(message, fallback))
          }
        }
        Success((recParser, res), next)
      case f: Failure => f
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy