@@ -32,6 +32,21 @@ def __getitem__(self, i):
3232 return self .data [i ]
3333
3434
35+ @dataclass
36+ class SourceSpan :
37+ """Identifies a span of material from the data to parse.
38+
39+ Attributes:
40+ source (str | None): the source of the data, e.g. a file path.
41+ start ([int, int]): the start row and column of the span.
42+ end ([int, int]): the end row and column of the span.
43+ """
44+
45+ source : str | None
46+ start : [int , int ]
47+ end : [int , int ]
48+
49+
3550def line_info_at (stream : Stream , index ):
3651 if index > len (stream ):
3752 raise ValueError ("invalid index" )
@@ -368,6 +383,9 @@ def mark(self) -> Parser:
368383 ((start_row, start_column),
369384 original_value,
370385 (end_row, end_column))
386+
387+ ``.span()'' is a more powerful version of this combinator, returning a
388+ SourceSpan.
371389 """
372390
373391 @generate
@@ -379,6 +397,24 @@ def marked():
379397
380398 return marked
381399
400+ def span (self ) -> Parser :
401+ """
402+ Returns a parser that augments the initial parser's result with a
403+ SourceSpan capturing where that parser started and stopped.
404+ The new value is a tuple:
405+
406+ (source_span, original_value)
407+ """
408+
409+ @generate
410+ def marked ():
411+ source , * start = yield line_info
412+ body = yield self
413+ _ , * end = yield line_info
414+ return (SourceSpan (source , tuple (start ), tuple (end )), body )
415+
416+ return marked
417+
382418 def tag (self , name : str ) -> Parser :
383419 """
384420 Returns a parser that wraps the produced value of the initial parser in a
0 commit comments