wenzelm@34166: /* Title: Pure/Isar/outer_syntax.scala wenzelm@34166: Author: Makarius wenzelm@34166: wenzelm@34166: Isabelle/Isar outer syntax. wenzelm@34166: */ wenzelm@34166: wenzelm@34166: package isabelle wenzelm@34166: wenzelm@34166: wenzelm@43411: import scala.collection.mutable wenzelm@58706: import scala.annotation.tailrec wenzelm@34166: wenzelm@34166: wenzelm@43774: object Outer_Syntax wenzelm@43774: { wenzelm@58706: /* syntax */ wenzelm@58706: wenzelm@58706: val empty: Outer_Syntax = new Outer_Syntax() wenzelm@58706: wenzelm@58706: def init(): Outer_Syntax = new Outer_Syntax(completion = Completion.init()) wenzelm@58706: wenzelm@58706: wenzelm@58706: /* string literals */ wenzelm@58706: wenzelm@43774: def quote_string(str: String): String = wenzelm@43774: { wenzelm@43774: val result = new StringBuilder(str.length + 10) wenzelm@43774: result += '"' wenzelm@43774: for (s <- Symbol.iterator(str)) { wenzelm@43774: if (s.length == 1) { wenzelm@43774: val c = s(0) wenzelm@43774: if (c < 32 && c != YXML.X && c != YXML.Y || c == '\\' || c == '"') { wenzelm@43774: result += '\\' wenzelm@43774: if (c < 10) result += '0' wenzelm@43774: if (c < 100) result += '0' wenzelm@43774: result ++= (c.asInstanceOf[Int].toString) wenzelm@43774: } wenzelm@43774: else result += c wenzelm@43774: } wenzelm@43774: else result ++= s wenzelm@43774: } wenzelm@43774: result += '"' wenzelm@43774: result.toString wenzelm@43774: } wenzelm@46626: wenzelm@58696: wenzelm@58697: /* line-oriented structure */ wenzelm@58696: wenzelm@58697: object Line_Structure wenzelm@58696: { wenzelm@58700: val init = Line_Structure() wenzelm@58696: } wenzelm@58696: wenzelm@58700: sealed case class Line_Structure( wenzelm@58700: improper: Boolean = true, wenzelm@58700: command: Boolean = false, wenzelm@58700: depth: Int = 0, wenzelm@58700: span_depth: Int = 0, wenzelm@58700: after_span_depth: Int = 0) wenzelm@58706: wenzelm@58706: wenzelm@58706: /* overall document structure */ wenzelm@58706: wenzelm@58706: sealed abstract class Document { def length: Int } wenzelm@58747: case class Document_Block(name: String, text: String, body: List[Document]) extends Document wenzelm@58706: { wenzelm@58706: val length: Int = (0 /: body)(_ + _.length) wenzelm@58706: } wenzelm@58747: case class Document_Atom(command: Command) extends Document wenzelm@58706: { wenzelm@58706: def length: Int = command.length wenzelm@58706: } wenzelm@43774: } wenzelm@43774: wenzelm@46712: final class Outer_Syntax private( wenzelm@58900: val keywords: Keyword.Keywords = Keyword.Keywords.empty, wenzelm@53280: val completion: Completion = Completion.empty, wenzelm@55749: val language_context: Completion.Language_Context = Completion.Language_Context.outer, wenzelm@56393: val has_tokens: Boolean = true) extends Prover.Syntax wenzelm@34166: { wenzelm@58706: /** syntax content **/ wenzelm@58706: wenzelm@58900: override def toString: String = keywords.toString wenzelm@56393: wenzelm@58695: wenzelm@58695: /* add keywords */ wenzelm@58695: wenzelm@58907: def + (name: String): Outer_Syntax = this + (name, None, None) wenzelm@58907: def + (name: String, kind: String): Outer_Syntax = this + (name, Some((kind, Nil)), None) wenzelm@58901: def + (name: String, opt_kind: Option[(String, List[String])], replace: Option[String]) wenzelm@58901: : Outer_Syntax = wenzelm@53280: { wenzelm@58901: val keywords1 = wenzelm@58901: opt_kind match { wenzelm@58901: case None => keywords + name wenzelm@58901: case Some(kind) => keywords + (name, kind) wenzelm@58901: } wenzelm@53280: val completion1 = wenzelm@58853: if (replace == Some("")) completion wenzelm@53280: else completion + (name, replace getOrElse name) wenzelm@58900: new Outer_Syntax(keywords1, completion1, language_context, true) wenzelm@53280: } wenzelm@48706: wenzelm@48873: def add_keywords(keywords: Thy_Header.Keywords): Outer_Syntax = wenzelm@48873: (this /: keywords) { wenzelm@58901: case (syntax, (name, opt_spec, replace)) => wenzelm@58901: val opt_kind = opt_spec.map(_._1) wenzelm@50128: syntax + wenzelm@58901: (Symbol.decode(name), opt_kind, replace) + wenzelm@58901: (Symbol.encode(name), opt_kind, replace) wenzelm@46940: } wenzelm@34166: wenzelm@58695: wenzelm@59073: /* merge */ wenzelm@59073: wenzelm@59077: def ++ (other: Prover.Syntax): Prover.Syntax = wenzelm@59073: if (this eq other) this wenzelm@59073: else { wenzelm@59077: val keywords1 = keywords ++ other.asInstanceOf[Outer_Syntax].keywords wenzelm@59077: val completion1 = completion ++ other.asInstanceOf[Outer_Syntax].completion wenzelm@59077: if ((keywords eq keywords1) && (completion eq completion1)) this wenzelm@59077: else new Outer_Syntax(keywords1, completion1, language_context, has_tokens) wenzelm@59073: } wenzelm@59073: wenzelm@59073: wenzelm@59735: /* load commands */ wenzelm@58900: wenzelm@58900: def load_command(name: String): Option[List[String]] = keywords.load_command(name) wenzelm@58900: def load_commands_in(text: String): Boolean = keywords.load_commands_in(text) wenzelm@58900: wenzelm@58900: wenzelm@58706: /* language context */ wenzelm@34166: wenzelm@58706: def set_language_context(context: Completion.Language_Context): Outer_Syntax = wenzelm@58900: new Outer_Syntax(keywords, completion, context, has_tokens) wenzelm@58706: wenzelm@58706: def no_tokens: Outer_Syntax = wenzelm@46969: { wenzelm@58900: require(keywords.is_empty) wenzelm@58706: new Outer_Syntax( wenzelm@58706: completion = completion, wenzelm@58706: language_context = language_context, wenzelm@58706: has_tokens = false) wenzelm@46969: } wenzelm@40454: wenzelm@58706: wenzelm@40454: wenzelm@58706: /** parsing **/ wenzelm@34166: wenzelm@58697: /* line-oriented structure */ wenzelm@58696: wenzelm@58700: def line_structure(tokens: List[Token], struct: Outer_Syntax.Line_Structure) wenzelm@58700: : Outer_Syntax.Line_Structure = wenzelm@58696: { wenzelm@58700: val improper1 = tokens.forall(_.is_improper) wenzelm@58700: val command1 = tokens.exists(_.is_command) wenzelm@58700: wenzelm@58696: val depth1 = wenzelm@59122: if (tokens.exists(tok => tok.is_command_kind(keywords, Keyword.theory))) 0 wenzelm@58700: else if (command1) struct.after_span_depth wenzelm@58700: else struct.span_depth wenzelm@58700: wenzelm@58700: val (span_depth1, after_span_depth1) = wenzelm@58700: ((struct.span_depth, struct.after_span_depth) /: tokens) { wenzelm@58703: case ((x, y), tok) => wenzelm@58703: if (tok.is_command) { wenzelm@59122: if (tok.is_command_kind(keywords, Keyword.theory_goal)) wenzelm@58900: (2, 1) wenzelm@59122: else if (tok.is_command_kind(keywords, Keyword.theory)) wenzelm@58900: (1, 0) wenzelm@59122: else if (tok.is_command_kind(keywords, Keyword.proof_goal) || tok.is_begin_block) wenzelm@58900: (y + 2, y + 1) wenzelm@59122: else if (tok.is_command_kind(keywords, Keyword.qed) || tok.is_end_block) wenzelm@58900: (y + 1, y - 1) wenzelm@59122: else if (tok.is_command_kind(keywords, Keyword.qed_global)) wenzelm@58900: (1, 0) wenzelm@58703: else (x, y) wenzelm@58703: } wenzelm@58703: else (x, y) wenzelm@58696: } wenzelm@58700: wenzelm@58700: Outer_Syntax.Line_Structure(improper1, command1, depth1, span_depth1, after_span_depth1) wenzelm@58696: } wenzelm@58696: wenzelm@58696: wenzelm@58706: /* command spans */ wenzelm@57905: wenzelm@57905: def parse_spans(toks: List[Token]): List[Command_Span.Span] = wenzelm@57905: { wenzelm@57905: val result = new mutable.ListBuffer[Command_Span.Span] wenzelm@57905: val content = new mutable.ListBuffer[Token] wenzelm@57905: val improper = new mutable.ListBuffer[Token] wenzelm@57905: wenzelm@57905: def ship(span: List[Token]) wenzelm@57905: { wenzelm@57905: val kind = wenzelm@59319: if (span.nonEmpty && span.head.is_command && !span.exists(_.is_error)) { wenzelm@57910: val name = span.head.source wenzelm@57911: val pos = Position.Range(Text.Range(0, Symbol.iterator(name).length) + 1) wenzelm@57910: Command_Span.Command_Span(name, pos) wenzelm@57910: } wenzelm@57905: else if (span.forall(_.is_improper)) Command_Span.Ignored_Span wenzelm@57905: else Command_Span.Malformed_Span wenzelm@57905: result += Command_Span.Span(kind, span) wenzelm@57905: } wenzelm@57905: wenzelm@57905: def flush() wenzelm@57905: { wenzelm@59319: if (content.nonEmpty) { ship(content.toList); content.clear } wenzelm@59319: if (improper.nonEmpty) { ship(improper.toList); improper.clear } wenzelm@57905: } wenzelm@57905: wenzelm@57905: for (tok <- toks) { wenzelm@57905: if (tok.is_command) { flush(); content += tok } wenzelm@57905: else if (tok.is_improper) improper += tok wenzelm@57905: else { content ++= improper; improper.clear; content += tok } wenzelm@57905: } wenzelm@57905: flush() wenzelm@57905: wenzelm@57905: result.toList wenzelm@57905: } wenzelm@57905: wenzelm@57906: def parse_spans(input: CharSequence): List[Command_Span.Span] = wenzelm@59083: parse_spans(Token.explode(keywords, input)) wenzelm@57906: wenzelm@57905: wenzelm@58706: /* overall document structure */ wenzelm@55616: wenzelm@58706: def heading_level(command: Command): Option[Int] = wenzelm@58706: { wenzelm@59735: val name = command.span.name wenzelm@59735: name match { wenzelm@59735: case Thy_Header.CHAPTER => Some(0) wenzelm@59735: case Thy_Header.SECTION | Thy_Header.HEADER => Some(1) wenzelm@59735: case Thy_Header.SUBSECTION => Some(2) wenzelm@59735: case Thy_Header.SUBSUBSECTION => Some(3) wenzelm@58868: case _ => wenzelm@59735: keywords.command_kind(name) match { wenzelm@59700: case Some(kind) if Keyword.theory(kind) && !Keyword.theory_end(kind) => Some(4) wenzelm@58868: case _ => None wenzelm@58868: } wenzelm@58706: } wenzelm@58706: } wenzelm@58706: wenzelm@58743: def parse_document(node_name: Document.Node.Name, text: CharSequence): wenzelm@58743: List[Outer_Syntax.Document] = wenzelm@58706: { wenzelm@58706: /* stack operations */ wenzelm@58706: wenzelm@58706: def buffer(): mutable.ListBuffer[Outer_Syntax.Document] = wenzelm@58706: new mutable.ListBuffer[Outer_Syntax.Document] wenzelm@58706: wenzelm@58747: var stack: List[(Int, Command, mutable.ListBuffer[Outer_Syntax.Document])] = wenzelm@58747: List((0, Command.empty, buffer())) wenzelm@55616: wenzelm@58706: @tailrec def close(level: Int => Boolean) wenzelm@58706: { wenzelm@58706: stack match { wenzelm@58747: case (lev, command, body) :: (_, _, body2) :: rest if level(lev) => wenzelm@59735: body2 += Outer_Syntax.Document_Block(command.span.name, command.source, body.toList) wenzelm@58706: stack = stack.tail wenzelm@58706: close(level) wenzelm@58706: case _ => wenzelm@58706: } wenzelm@58706: } wenzelm@58706: wenzelm@58743: def result(): List[Outer_Syntax.Document] = wenzelm@58706: { wenzelm@58706: close(_ => true) wenzelm@58743: stack.head._3.toList wenzelm@58706: } wenzelm@58706: wenzelm@58706: def add(command: Command) wenzelm@58706: { wenzelm@58706: heading_level(command) match { wenzelm@58706: case Some(i) => wenzelm@58706: close(_ > i) wenzelm@58747: stack = (i + 1, command, buffer()) :: stack wenzelm@58706: case None => wenzelm@58706: } wenzelm@58706: stack.head._3 += Outer_Syntax.Document_Atom(command) wenzelm@58706: } wenzelm@58706: wenzelm@58706: wenzelm@58706: /* result structure */ wenzelm@58706: wenzelm@58706: val spans = parse_spans(text) wenzelm@59702: spans.foreach(span => add(Command(Document_ID.none, node_name, Command.no_blobs, span))) wenzelm@58706: result() wenzelm@55616: } wenzelm@34166: }