src/Pure/Tools/bibtex.scala
changeset 58534 573ce5ad13bc
parent 58533 dfbfc92118eb
child 58535 4815429974fe
equal deleted inserted replaced
58533:dfbfc92118eb 58534:573ce5ad13bc
   213         case ss => Chunk("", List(Token(Token.Kind.IGNORED, ss.mkString))) }
   213         case ss => Chunk("", List(Token(Token.Kind.IGNORED, ss.mkString))) }
   214 
   214 
   215 
   215 
   216     /* delimited string: outermost "..." or {...} and body with balanced {...} */
   216     /* delimited string: outermost "..." or {...} and body with balanced {...} */
   217 
   217 
       
   218     // see also bibtex.web: scan_a_field_token_and_eat_white, scan_balanced_braces
   218     private def delimited_depth(delim: Delimited): Parser[(String, Delimited)] =
   219     private def delimited_depth(delim: Delimited): Parser[(String, Delimited)] =
   219       new Parser[(String, Delimited)]
   220       new Parser[(String, Delimited)]
   220       {
   221       {
   221         require(if (delim.quoted) delim.depth > 0 else delim.depth >= 0)
   222         require(if (delim.quoted) delim.depth > 0 else delim.depth >= 0)
   222 
   223 
   229           var q = delim.quoted
   230           var q = delim.quoted
   230           var d = delim.depth
   231           var d = delim.depth
   231           var finished = false
   232           var finished = false
   232           while (!finished && i < end) {
   233           while (!finished && i < end) {
   233             val c = in.source.charAt(i)
   234             val c = in.source.charAt(i)
       
   235 
   234             if (c == '"' && d == 0) { i += 1; d = 1; q = true }
   236             if (c == '"' && d == 0) { i += 1; d = 1; q = true }
   235             else if (c == '"' && d == 1 && q) {
   237             else if (c == '"' && d == 1 && q) {
   236               i += 1; d = 0; q = false; finished = true
   238               i += 1; d = 0; q = false; finished = true
   237             }
   239             }
   238             else if (c == '{') { i += 1; d += 1 }
   240             else if (c == '{') { i += 1; d += 1 }
   239             else if (c == '}' && (d > 1 || d == 1 && !q)) {
   241             else if (c == '}') {
   240               i += 1; d -= 1; if (d == 0) finished = true
   242               if (d == 1 && !q || d > 1) { i += 1; d -= 1; if (d == 0) finished = true }
       
   243               else {i = start; finished = true }
   241             }
   244             }
   242             else if (d > 0) i += 1
   245             else if (d > 0) i += 1
   243             else finished = true
   246             else finished = true
   244           }
   247           }
   245           if (i == start) Failure("bad input", in)
   248           if (i == start) Failure("bad input", in)
   252 
   255 
   253     private def delimited: Parser[Token] =
   256     private def delimited: Parser[Token] =
   254       delimited_depth(Closed) ^?
   257       delimited_depth(Closed) ^?
   255         { case (s, delim) if delim == Closed => Token(Token.Kind.STRING, s) }
   258         { case (s, delim) if delim == Closed => Token(Token.Kind.STRING, s) }
   256 
   259 
   257     private def delimited_line(ctxt: Line_Context): Parser[(Chunk, Line_Context)] =
   260     private def recover_delimited: Parser[Token] =
   258     {
   261       """(?m)["{][^@]*""".r ^^ token(Token.Kind.ERROR)
   259       ctxt match {
   262 
   260         case Item_Context(kind, delim, right) =>
   263     def delimited_line(item_ctxt: Item_Context): Parser[(Chunk, Line_Context)] =
       
   264       item_ctxt match {
       
   265         case Item_Context(kind, delim, _) =>
   261           delimited_depth(delim) ^^ { case (s, delim1) =>
   266           delimited_depth(delim) ^^ { case (s, delim1) =>
   262             (Chunk(kind, List(Token(Token.Kind.STRING, s))), Item_Context(kind, delim1, right)) }
   267             (Chunk(kind, List(Token(Token.Kind.STRING, s))), item_ctxt.copy(delim = delim1)) } |
   263         case _ => failure("")
   268           recover_delimited ^^ { case a => (Chunk(kind, List(a)), Ignored_Context) }
   264       }
   269         }
   265     }
       
   266 
       
   267     private def recover_delimited: Parser[Token] =
       
   268       """(?m)["{][^@]+""".r ^^ token(Token.Kind.ERROR)
       
   269 
   270 
   270 
   271 
   271     /* other tokens */
   272     /* other tokens */
   272 
   273 
   273     private val at = "@" ^^ keyword
   274     private val at = "@" ^^ keyword
   330             { case (kind, a) ~ b ~ c =>
   331             { case (kind, a) ~ b ~ c =>
   331                 val right = if (b.source == "{") "}" else ")"
   332                 val right = if (b.source == "{") "}" else ")"
   332                 val chunk = Chunk(kind, a ::: List(b) ::: (c getOrElse Nil))
   333                 val chunk = Chunk(kind, a ::: List(b) ::: (c getOrElse Nil))
   333                 (chunk, Item_Context(kind, Closed, right)) } |
   334                 (chunk, Item_Context(kind, Closed, right)) } |
   334           recover_item ^^ { case a => (a, Ignored_Context) }
   335           recover_item ^^ { case a => (a, Ignored_Context) }
   335         case Item_Context(kind, delim, right) =>
   336         case item_ctxt @ Item_Context(kind, delim, right) =>
   336           if (delim.depth > 0)
   337           if (delim.depth > 0)
   337             delimited_line(ctxt)
   338             delimited_line(item_ctxt)
   338           else {
   339           else {
   339             delimited_line(ctxt) |
   340             delimited_line(item_ctxt) |
   340             other_token ^^ { case a => (Chunk(kind, List(a)), ctxt) } |
   341             other_token ^^ { case a => (Chunk(kind, List(a)), ctxt) } |
   341             right ^^ { case a => (Chunk(kind, List(keyword(a))), Ignored_Context) }
   342             right ^^ { case a => (Chunk(kind, List(keyword(a))), Ignored_Context) }
   342           }
   343           }
   343         case _ => failure("")
   344         case _ => failure("")
   344       }
   345       }