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

dotty.tools.repl.ParseResult.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools
package repl

import dotc.reporting.diagnostic.MessageContainer
import dotc.core.Contexts.Context
import dotc.parsing.Parsers.Parser
import dotc.parsing.Tokens
import dotc.util.SourceFile
import dotc.ast.untpd
import dotty.tools.dotc.core.StdNames.str

import scala.annotation.internal.sharable

/** A parsing result from string input */
sealed trait ParseResult

/** An error free parsing resulting in a list of untyped trees */
case class Parsed(source: SourceFile, trees: List[untpd.Tree]) extends ParseResult

/** A parsing result containing syntax `errors` */
case class SyntaxErrors(sourceCode: String,
                        errors: List[MessageContainer],
                        trees: List[untpd.Tree]) extends ParseResult

/** Parsed result is simply a newline */
case object Newline extends ParseResult

/** `ctrl-c` obtained from input string */
case object SigKill extends ParseResult

/** A command is on the format:
 *
 *  ```none
 *  :commandName 
 *  ```
 *  The `Command` trait denotes these commands
 */
sealed trait Command extends ParseResult

/** An unknown command that will not be handled by the REPL */
case class UnknownCommand(cmd: String) extends Command

/** `:load ` interprets a scala file as if entered line-by-line into
 *  the REPL
 */
case class Load(path: String) extends Command
object Load {
  val command: String = ":load"
}

/** To find out the type of an expression you may simply do:
 *
 * ```
 * scala> :type (1 * 54).toString
 * String
 * ```
 */
case class TypeOf(expr: String) extends Command
object TypeOf {
  val command: String = ":type"
}

/**
 * A command that is used to display the documentation associated with
 * the given expression.
 */
case class DocOf(expr: String) extends Command
object DocOf {
  val command: String = ":doc"
}

/** `:imports` lists the imports that have been explicitly imported during the
 *  session
 */
case object Imports extends Command {
  val command: String = ":imports"
}

/** Reset the session to the initial state from when the repl program was
 *  started
 */
case object Reset extends Command {
  val command: String = ":reset"
}

/** `:quit` exits the repl */
case object Quit extends Command {
  val command: String = ":quit"
}

/** `:help` shows the different commands implemented by the Dotty repl */
case object Help extends Command {
  val command: String = ":help"
  val text: String =
    """The REPL has several commands available:
      |
      |:help                    print this summary
      |:load              interpret lines in a file
      |:quit                    exit the interpreter
      |:type        evaluate the type of the given expression
      |:doc         print the documentation for the given expresssion
      |:imports                 show import history
      |:reset                   reset the repl to its initial state, forgetting all session entries
    """.stripMargin
}

object ParseResult {

  @sharable private[this] val CommandExtract = """(:[\S]+)\s*(.*)""".r

  private def parseStats(implicit ctx: Context): List[untpd.Tree] = {
    val parser = new Parser(ctx.source)
    val stats = parser.blockStatSeq()
    parser.accept(Tokens.EOF)
    stats
  }

  def apply(source: SourceFile)(implicit state: State): ParseResult = {
    val sourceCode = source.content().mkString
    sourceCode match {
      case "" => Newline
      case CommandExtract(cmd, arg) => cmd match {
        case Quit.command => Quit
        case Help.command => Help
        case Reset.command => Reset
        case Imports.command => Imports
        case Load.command => Load(arg)
        case TypeOf.command => TypeOf(arg)
        case DocOf.command => DocOf(arg)
        case _ => UnknownCommand(cmd)
      }
      case _ =>
        implicit val ctx: Context = state.context

        val reporter = newStoreReporter
        val stats = parseStats(state.context.fresh.setReporter(reporter).withSource(source))

        if (reporter.hasErrors)
          SyntaxErrors(
            sourceCode,
            reporter.removeBufferedMessages,
            stats)
        else
          Parsed(source, stats)
    }
  }

  def apply(sourceCode: String)(implicit state: State): ParseResult =
    apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode))

  /** Check if the input is incomplete
   *
   *  This can be used in order to check if a newline can be inserted without
   *  having to evaluate the expression
   */
  def isIncomplete(sourceCode: String)(implicit ctx: Context): Boolean =
    sourceCode match {
      case CommandExtract(_) | "" => false
      case _ => {
        val reporter = newStoreReporter
        val source = SourceFile.virtual("", sourceCode)
        val localCtx = ctx.fresh.setSource(source).setReporter(reporter)
        var needsMore = false
        reporter.withIncompleteHandler((_, _) => needsMore = true) {
          parseStats(localCtx)
        }
        !reporter.hasErrors && needsMore
      }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy