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 } |