Reader
koskari.reader
Koskari s-expression reader and layout block parsing.
- exception koskari.reader.FormatStringSyntaxError(message, location=None, filename=None, text=None)
Bases:
ReaderSyntaxErrorErrors in format string (f-string) syntax
- class koskari.reader.IndentAwareStream(stream, filename=None, auto_skip_exec=True, line_no=1, col_no=0, max_indent=None)
Bases:
SourceStreamSource stream with indent-bounded skip_whitespace
Extends SourceStream with a max_indent threshold. When max_indent is set, skip_whitespace inspects the indent of the next content line without consuming it. If that indent is at or below max_indent, skip_whitespace sets self.dedented = True and returns without consuming any characters, leaving the stream positioned at the newline so the outer reader can handle the boundary.
When max_indent is None, skip_whitespace delegates to the native C implementation (BaseSourceStream.skip_whitespace) for maximum speed.
The dedented attribute is reset to False at the start of each skip_whitespace call. Callers read it immediately after skip_whitespace returns; it is never guarded by getattr.
- classmethod from_source(source_stream, max_indent=None)
The layout stream over a plain SourceStream, continuing from its current position. Made once where a stream is created for layout reading; the plain stream itself is not read afterwards
- Parameters:
source_stream – a SourceStream, its
#!line already handledmax_indent – as for the constructor
- indent_limit(max_indent)
Bound skip_whitespace by
max_indentfor the durationWhile active, skip_whitespace sets
dedentedand stops without consuming when the next content line is at or belowmax_indent. The previous limit is restored on exit.- Parameters:
max_indent – column that ends a block body on dedent
- skip_whitespace()
Skip whitespace, stopping at dedent when max_indent is set
When max_indent is None, delegates to the native C skip_whitespace.
Otherwise, speculatively scans the upcoming whitespace and blank lines to find the next content character. Blank lines are skipped, and a line holding only whitespace and a comment is a blank line: a comment is never content and its column is never an indent. The first non-blank content line is measured:
If the content is on the current line (no newline was crossed), the leading horizontal whitespace is consumed and the stream is left at the content.
If a newline was crossed and the content line’s indent is <= max_indent, sets self.dedented = True and consumes nothing, leaving the stream at the boundary.
If a newline was crossed and the indent is > max_indent, consumes the newline(s) and indent, leaving the stream at the content.
The scan is speculative (mark/rewind) so its lookahead is unbounded regardless of how many blank lines separate the content.
- suspend_indent()
Temporarily disable indent-bounded whitespace skipping
Indentation has no meaning inside a balanced delimited form, so a pair read suspends the indent guard for the duration of its body. While suspended, max_indent is None and skip_whitespace consumes vertical whitespace freely (native behavior), letting a form span dedented lines up to its closing delimiter. The previous max_indent is restored on exit.
- class koskari.reader.LayoutReader(namespace, nodefaults=False, layout_enabled=True, layout_implicit_calls=True, bracket_pairs=True)
Bases:
ReaderReader that supports layout-based block syntax
Subclasses Reader and adds indent-aware block reading. Resolves layout heads via namespace (layout/<head>). Requires a namespace for layout resolution, like the compiler.
Three default surfaces: (1)
self.inneris a plainReaderwith full s-expr defaults—balanced forms and prefix reads there use only inner state. (2) Bridge macros on this coordinator forward( ) [ ] { }";frb#intoinnerso delimiter and string/comment/prefix parsing matches s-expr mode; an index bracket attached to a value is the exception, read by this coordinator’s own suffix macro in header mode. (3) Layout-only registrations: structural:,_layout_macros(e.g.@), and stacked'/ backtick (predicated raw block then normal quote).Top-level layout reads are line-oriented: header-mode values on a logical line are collected until
LINE_ENDor structuralCOLON. Empty lines are skipped. Multiple values on one line fold to an implicitconschain or raise perlayout_implicit_calls. A structural colon ends the block header (headplusargs); the body is read until dedent and passed to the layout handler (non-symbol heads use thelayout/blockfallback when no named layout matches). Structural:rules still allow keyword atoms such asas:inside header arguments at delimiter depth 0.Stream interface contract: LayoutReader expects its stream arguments to be SourceStream instances (for pushback support). Body reads always use an IndentAwareStream so that dedent detection is available through the stream.dedented attribute rather than getattr guards.
Subclasses may override _add_default_layout_macros to register or omit layout-mode-only prefix handlers (_layout_macros). Raw block quote and quasiquote use predicated reader macros on
'and backtick when defaults are enabled.Layout header tails use LINE_END and COLON events with horizontal-only whitespace between tokens on a logical line.
- add_event_macro(char, macro_fn, terminating=False, predicate=None)
Add a character event macro to the reader
Each call prepends
(macro_fn, terminating, predicate)to that character’s macro stack (newest first).- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, char) -> (event, value). The macro may return (DEFER, None) or (DEFER, repl) to decline handling and try the next handler from
_event_macro_chain_for(); the chain always ends with_read_default. The opening character was already consumed; passcthrough to_read_default. On DEFER, do not advance the stream beyond peek-only inspection.terminating – if True, this character terminates atoms
predicate – optional
(stream, char, atom_prefix) -> bool. If false beforemacro_fnruns at read dispatch, that entry is skipped (early DEFER). For terminating registrations, also participates in atom boundary decisions (see_atom_boundary_split()).
- add_inner_event_macro(char, macro_fn, terminating=False, predicate=None)
- add_layout_event_macro(char, macro_fn, terminating=False, predicate=None)
- find_layout(name)
- fold_elements(elements, position=None)
The form the elements of one line denote, by the line rule
- Parameters:
elements – proper cons list of the line’s elements, never empty
position – the line’s position, stamped on a folded call
- Returns:
the form
- Raises:
LayoutError – several elements with implicit calls off
- fold_line(entry)
The form one body entry denotes
A line is
(:#line: ELEMENT ...)over the elements the reader saw on it. One element is that element; several fold to the call of the first, or raise whenlayout_implicit_callsis off. A block entry lowers through its layout to its form.- Parameters:
entry – a :#line: cons, never without elements, or a :#block: cons
- Returns:
the entry’s form
- Raises:
LayoutError – several elements with implicit calls off
- fold_lines(entries)
Fold every entry of a body with
fold_line()- Parameters:
entries – proper cons list of :#line: and :#block: conses
- Returns:
proper cons list of forms
- classmethod from_pragma(namespace, pragma=None)
Build a reader from a module pragma (a default pragma when omitted), apply its reader settings, and subscribe for later pragma changes.
- property inner
The dedicated s-expression reader used inside bridge macros.
Attach s-expr-only extensions with
reader.inner.add_event_macro.
- property namespace
The namespace layout handlers resolve against
- process_atom(atom)
Process a raw atom string into a typed value
Tries each atom pattern in order. If none match, returns a symbol.
- Parameters:
atom – the raw atom string
- Returns:
the processed value
- read(reader_stream)
Read a single layout form from the stream
The stream must be an IndentAwareStream, made once for the whole source with
IndentAwareStream.from_source(), so successive reads continue one position track and stream.dedented is always available to the layout read. A plain SourceStream is refused: a wrapper made per read would restart its line count at the stream’s stale position on every call.A top-level form must start at column 0;
layout_indentdecides whether anything else is an error, a warning, or read as-is.At EOF returns READ_EOF (same as Reader.read), not None, so callers can distinguish end-of-stream from a parsed Python None literal.
A
LINEfolds throughfold_line();BLOCKvalues are expanded viaapply_layout()andBLOCKSspines viaapply_layouts()(returning the expanded car), each with this reader.- Raises:
TypeError – when reader_stream is not an IndentAwareStream
- read_and_position(reader_stream)
Read a single layout form and return it with the position it started at, as Reader.read_and_position does: a form that is an atom carries no position of its own, and this is where the compiler learns its line
- Returns:
a tuple of (value, position) where position is (line, col); value is READ_EOF at EOF
- Raises:
TypeError – when reader_stream is not an IndentAwareStream
- read_block(stream, outer_indent, head=None, args=None)
Read a block as a :#block: cons without invoking layout handlers
Used when
_block_modeis set (quote/quasiquote inner blocks). Sets _block_mode for the duration of the body read so nested colons also produce raw :#block: conses.- Parameters:
stream – the source stream, positioned after the header colon and its trailing whitespace
outer_indent – column of the block head line
head – block head symbol if already read by the caller
args – header argument forms if already read by the caller
- Returns:
(:#block: (head . args) entry…) cons
- read_block_body(stream, outer_indent)
Read body elements until dedent or EOF
stream must be an IndentAwareStream (guaranteed by LayoutReader.read wrapping the top-level stream, and by all internal callers using the same stream throughout). Temporarily installs outer_indent as the max_indent so that skip_whitespace stops and sets stream.dedented when the next content line is at or below that column. Restores the previous max_indent on exit.
- Parameters:
stream – an IndentAwareStream
outer_indent – column that ends the block body on dedent
- Returns:
list of body entries: a line, the proper cons list of the elements the reader saw on it, or a nested :#block: cons
- read_block_header(stream)
Read the block head and arguments up to a structural header ‘:’.
Uses header-mode atom parsing so peer heads like else: are read as symbols, not keyword atoms.
- Parameters:
stream – the source stream positioned at the first header token
- Returns:
(head, args, opener) where head is the first form, args are the remaining forms before the colon, opener is symbol(‘:’)
- Raises:
ReaderSyntaxError – on EOF or missing colon
- read_form(stream)
Read one layout form using a line-oriented header-mode loop
When layout is disabled, delegates to
Reader._read()oninner.When layout is enabled:
cin_layout_macrosruns the layout-only prefix handler (e.g.@), provided its gate is open (see_layout_macro_enabled()). Otherwise collects header-mode values on the current logical line untilLINE_END,EOF, or structuralCOLON(horizontal whitespace only between tokens). Empty lines are skipped. AtLINE_ENDreturnsLINEwith the elements as a proper list, unfolded. AtCOLON, returnsBLOCKfor a single :#block: cons, orBLOCKSfor a primary :#block: plus stream-collected peer :#block: conses on a spine. Expansion is deferred toread().- Parameters:
stream – source stream positioned at the start of the next form
- Returns:
(event, position, value)
- reload_pragma(pragma)
Apply reader settings from a module pragma
Extends
Reader.reload_pragma()with the layout switches (reader/layout, reader/implicit-calls, reader/decorators, and reader/indent) and mirrors every setting onto the inner s-expression reader, so both surfaces agree.- Parameters:
pragma – a module pragma, or None for a no-op
- set_atom_pattern(namesym, match_fn, conversion_fn)
Add or update an atom pattern
- Parameters:
namesym – symbol name for this pattern
match_fn – function(string) -> bool
conversion_fn – function(string) -> value
- set_layout_modes(*, layout_enabled=None, layout_implicit_calls=None, layout_decorators=None, layout_indent=None)
Update layout-mode feature switches on this reader.
reload_pragma calls this with the pragma’s layout switches on every pragma change, so a direct call holds only until the next pragma change that sets the same switch. Any None parameter leaves the current setting unchanged.
- temporary_event_macro(char, macro_fn, terminating=False, predicate=None)
Context manager to temporarily apply a character event macro
Applies the triplet to both the layout coordinator (via the base implementation) and the inner s-expr reader for the duration, so delimited reads inside the block see it too. Each side removes only its own triplet on exit.
- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, char) -> (event, value); may return (DEFER, None) like other event macros (see add_event_macro).
terminating – if True, this character terminates atoms
predicate – optional predicate (see
add_event_macro())
- Yields:
self
- class koskari.reader.Reader(nodefaults=False, bracket_pairs=True)
Bases:
objectS-expression reader with configurable reader macros and atom patterns
The reader parses textual input into Koskari data structures. It supports reader macros for special character handling and atom patterns for converting atomic tokens into typed values.
Each character maps to a newest-first stack of
(macro_fn, terminating, predicate_fn)triplets (seeadd_event_macro());predicate_fnmay be None. Optional predicates gate macro dispatch (early DEFER) and soft atom boundaries for terminating registrations. Event macros may return DEFER so the next handler in_event_macro_chain_forruns; the leading-character chain ends with_read_default.Suffix macros are a second, parallel registry keyed the same way (see
add_suffix_macro()). Where an event macro dispatches on the character that opens a token, a suffix macro dispatches on the character following a completed value, and wraps that value. Each consumes exactly one suffix and the driver re-enters, so chains across differing suffix kinds compose without any one macro knowing about the others.- static abandon_forms(forms, prefixes)
Release what the open forms and waiting prefixes hold, innermost first; after a completed read there is nothing left to release
- add_event_macro(char, macro_fn, terminating=False, predicate=None)
Add a character event macro to the reader
Each call prepends
(macro_fn, terminating, predicate)to that character’s macro stack (newest first).- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, char) -> (event, value). The macro may return (DEFER, None) or (DEFER, repl) to decline handling and try the next handler from
_event_macro_chain_for(); the chain always ends with_read_default. The opening character was already consumed; passcthrough to_read_default. On DEFER, do not advance the stream beyond peek-only inspection.terminating – if True, this character terminates atoms
predicate – optional
(stream, char, atom_prefix) -> bool. If false beforemacro_fnruns at read dispatch, that entry is skipped (early DEFER). For terminating registrations, also participates in atom boundary decisions (see_atom_boundary_split()).
- add_macro_character(char, macro_fn, terminating=False, predicate=None)
Add a reader macro to the parser
Unlike add_event_macro, this wraps the macro function to always return a VALUE event; wrapped macros cannot DEFER.
- Parameters:
char – the character to trigger on
macro_fn – function(stream, char) -> value
terminating – if True, this character terminates atoms
predicate – optional predicate (see
add_event_macro())
- add_suffix_macro(char, macro_fn, predicate=None)
Add a suffix macro to the reader
Suffix macros are the reader’s post macros: after any token is read as a
VALUE, the driver peeks the next character and dispatches to that character’s macro stack. This is how attached-bracket indexing turnsfoo[x]into(item foo x).Each call prepends
(macro_fn, predicate)to that character’s macro stack (newest first), mirroringadd_event_macro().A macro consumes exactly one suffix and returns the value to carry forward; the driver re-enters with that value, so chains such as
foo[a][b]fall out of dispatch rather than out of any one macro.- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, value, position) -> value. The trigger character has not been consumed. May return
DEFERto decline, leaving the stream untouched beyond peek-only inspection, in which case the next handler from that character’s stack runs. A macro that has consumed input owes a reader error rather than a lateDEFER, and one that claims a suffix without consuming anything is an error.predicate – optional
(stream, char, value) -> bool. If false, that entry is skipped without callingmacro_fn(early DEFER). Feature gating belongs here, where the value the suffix would attach to is available.
- apply_prefixes(stream, pending, position, value)
Wrap a completed value in the prefixes waiting for it, innermost first, each wrapped form taking its prefix’s position and its own suffix macros
- Returns:
(position, value) of the outermost wrapped form
- clear_atom_pattern(namesym)
Remove an atom pattern by name
- Parameters:
namesym – symbol name to remove
- clear_event_macro(char)
Remove all stacked event macros for each character in
char- Parameters:
char – the character(s) to clear
- clear_suffix_macro(char)
Remove all stacked suffix macros for each character in
char- Parameters:
char – the character(s) to clear
- close_form(stream, form, position, closechar)
Close an open list at its closing delimiter and hand back the finished value with the position of its opener
- drive_forms(stream, forms, prefixes, raw, vertical_ws)
The form driver’s loop: one event at a time until the outermost form completes or an event the caller must judge arrives
- Parameters:
forms – the stack of open forms, innermost last
prefixes – the prefixes waiting at the outermost level
- Returns:
tuple of (event, position, value)
- eof_error(stream, forms, pending)
The error for input ending with a prefix or a form still open: the innermost one reports
- form_append(stream, form, value, position)
A completed value inside an open list
- form_dot(stream, form, position)
A dot inside an open list: the next value is the tail
- form_eof(stream, form)
The error for input ending inside an open list: EOF at the opener whatever the form’s state, since a list that has read its dot, or its dot and its tail, is completed by more input as any open list is. A misplaced dot is an error where it sits, raised by form_dot and the value step, never here.
- get_atom_pattern(namesym)
Get an atom pattern by name
- Parameters:
namesym – symbol name to look up
- Returns:
the pattern list [name, match, conv] or None
- get_event_macro(char)
Get the newest character event macro from the reader
- Parameters:
char – the character to look up
- Returns:
tuple of (macro_fn, terminating, predicate_fn) or None
- get_event_macros(char)
Return stacked event macros for one character
- Parameters:
char – single character to look up
- Returns:
list of
(macro_fn, terminating, predicate_fn), newest first; empty if none
- get_suffix_macro(char)
Get the newest suffix macro from the reader
- Parameters:
char – the character to look up
- Returns:
tuple of (macro_fn, predicate) or None
- get_suffix_macros(char)
Return stacked suffix macros for one character
- Parameters:
char – single character to look up
- Returns:
list of
(macro_fn, predicate), newest first; empty if none
- nesting_guard(stream)
Report recursion that runs out as a positioned reader error, at the point the read had reached
- open_form(stream, open_, position)
Enter an open list: its own cell kind, the base terminators, indentation suspended, and the macro its opener holds
- process_atom(atom)
Process a raw atom string into a typed value
Tries each atom pattern in order. If none match, returns a symbol.
- Parameters:
atom – the raw atom string
- Returns:
the processed value
- read(reader_stream)
Read a single s-expression from the stream
Returns a cons cell, symbol, or numeric value. Returns READ_EOF if no data remains in the stream.
- Parameters:
reader_stream – the source stream to read from
- Returns:
the parsed value, or READ_EOF at EOF
- Raises:
ReaderSyntaxError – on syntax errors
- read_and_position(reader_stream)
Read a single s-expression and return its position
- Parameters:
reader_stream – the source stream to read from
- Returns:
a tuple of (value, position) where position is (line, col); value is READ_EOF at EOF
- Raises:
ReaderSyntaxError – on syntax errors
- read_atom(reader_stream)
Read a single s-expression, returning atoms as unprocessed strings
Returns a cons cell, or an unprocessed atomic value
- Parameters:
reader_stream – the source stream to read from
- Returns:
the parsed value, or READ_EOF at EOF
- Raises:
ReaderSyntaxError – on syntax errors
- read_event(stream, raw=False, vertical_ws=True)
Read one event: a complete atom or value, or what a delimiter or prefix macro reports for the driver to carry on with
- Parameters:
stream – the source stream
raw – if True, return atoms without processing
vertical_ws – if True (default), skip all whitespace including newlines before each token; if False, skip only horizontal whitespace (layout header tails and logical-line reads)
- Returns:
tuple of (event, position, value)
- read_pair(stream, openchar=None, closechar=None)
Read a pair (list) from the stream
- Parameters:
stream – the source stream
openchar – the opening character (read if None)
closechar – the closing character (inferred if None)
- Returns:
the parsed pair
- static ref_called(child, pos)
- reload_pragma(pragma)
Apply reader settings from a module pragma to this reader
Covers every setting a plain reader carries: reader/brackets, reader/index-brackets, reader/dot-attrs, reader/string-segments, reader/unary, and reader/fences.
Raw reader defaults are deliberately bare, so this is how a directly-constructed reader is brought up to the pragma’s baseline. Tooling that builds a reader and skips this step sees none of the pragma-gated syntax, which is rarely what it wants.
- Parameters:
pragma – a module pragma, or None for a no-op
- set_atom_pattern(namesym, match_fn, conversion_fn)
Add or update an atom pattern
- Parameters:
namesym – symbol name for this pattern
match_fn – function(string) -> bool
conversion_fn – function(string) -> value
- set_atom_regex(namesym, regexstr, conversion_fn)
Add an atom pattern using a regex string
- Parameters:
namesym – symbol name for this pattern
regexstr – regex pattern string
conversion_fn – function(string) -> value
- temporary_event_macro(char, macro_fn, terminating=False, predicate=None)
Context manager to temporarily apply a character event macro
Prepends one
(macro_fn, terminating, predicate)triplet shared acrosschar. On exit, removes only that triplet from the head of each affected list when it still matches by identity.- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, char) -> (event, value); may return (DEFER, None) like other event macros (see add_event_macro).
terminating – if True, this character terminates atoms
predicate – optional predicate (see
add_event_macro())
- Yields:
self
- temporary_macro_character(char, macro_fn, terminating=False, predicate=None)
Context manager to temporarily apply a reader macro
- Parameters:
char – the character to trigger on
macro_fn – function(stream, char) -> value
terminating – if True, this character terminates atoms
predicate – optional predicate (see
add_event_macro())
- Returns:
context manager
- temporary_suffix_macro(char, macro_fn, predicate=None)
Context manager to temporarily apply a suffix macro
Prepends one
(macro_fn, predicate)entry shared acrosschar. On exit, removes only that entry from the head of each affected list when it still matches by identity.- Parameters:
char – the character(s) to trigger on
macro_fn – function(stream, value, position) -> value; may return
DEFERlike other suffix macros (seeadd_suffix_macro()).predicate – optional predicate (see
add_suffix_macro())
- Yields:
self
- terminators_extended(chars)
Read with extra atom terminators in force at this level: the atom collector takes the terminator string at each call, so an atom read under this context stops at
charstoo, while a delimited form read inside it (_read_pair) reads its contents with the base terminators. Nests, restoring the previous set on exit- Parameters:
chars – the characters to add
- exception koskari.reader.ReaderEOF(message, location=None, filename=None, text=None)
Bases:
ReaderErrorStream ended before a delimited or quoted form finished reading.
Distinct from top-level READ_EOF (no form started). REPL callers may treat this as incomplete input; file loaders should surface it as an error.
- exception koskari.reader.ReaderSyntaxError(message, location=None, filename=None, text=None)
Bases:
ReaderErrorLow-level parse errors during s-expression or layout reading
- class koskari.reader.SourceStream(stream, filename=None, auto_skip_exec=True, line_no=1, col_no=0)
Bases:
BaseSourceStreamSource stream with error reporting helpers
Inherits from the native BaseSourceStream for efficient character-level I/O with position tracking. Adds error reporting and the helper methods used by the reader (skip_exec, suspend_indent).
- dedented = False
- error(message, position=None)
Create a ReaderSyntaxError for the given or current position
- Parameters:
message – the error message
position – optional (line, col) tuple; uses current if None
- Returns:
a ReaderSyntaxError instance
- peek_line()
Peek the rest of the current physical line without consuming it
Returns the text from the current position up to (not including) the next newline or carriage return, or to end of stream. Unbounded: the result is the whole line regardless of length.
- Returns:
the remaining text on the current physical line
- reader_eof(position=None, *, context=None, opened_at=None)
Build a ReaderEOF for stream end inside an unfinished form
Does not read the source file for a source line snippet; callers raise this immediately and REPL parse paths map it to incomplete input without displaying the exception.
When the unfinished form has an opener, a quote, a bracket, a field brace, the error is located at that opener and the message names it, so a reader of the error is pointed at what was never closed rather than at the end of the input.
- Parameters:
position – optional (line, col) tuple; uses current if None
context – optional phrase naming the unfinished form
opened_at – (line, col) of the form’s opener; when given it becomes the error location and is named in the message
- Returns:
a ReaderEOF instance (caller should raise it)
- skip_exec()
Skip a #! line at the start of the stream
- speculate()
Mark the stream on entry and rewind to that mark on exit
For pure lookahead: read freely inside the block to inspect upcoming input; the stream (position and line/col) is restored to its pre-block state on exit, whether the block returns or raises. Unlike a fixed-size peek, a speculative scan sees the whole remaining input.
- suspend_indent()
No-op indent suspension for streams without indent tracking
Streams that do not bound whitespace skipping by indentation have nothing to suspend, so this simply yields.
- koskari.reader.source_open(filename, auto_skip_exec=True)
Context manager to open a file as a SourceStream
The file is read whole and closed before the stream is handed out, so the module body runs with no file open: text appended to the file while it runs is not executed, a fork does not split the read position, and the native stream’s tell and seek are index operations on the text rather than a text-wrapper’s decoder replay. Newlines are normalized as for a string source.
- Parameters:
filename – path to the file to open
auto_skip_exec – if True, skip any #! line at the start
- Yields:
SourceStream for the file
- koskari.reader.source_str(src, filename, auto_skip_exec=True, line_no=1, col_no=0)
Create a SourceStream from a string
Newlines are normalized to
\nso string sources behave like files opened in text mode; the reader then never sees a carriage return.- Parameters:
src – the source string
filename – the filename to report in errors
auto_skip_exec – if True, skip any #! line at the start
line_no – starting line number
col_no – starting column number
- Returns:
a SourceStream
- koskari.reader.source_stream(underlying, filename, auto_skip_exec=True)
Create a SourceStream wrapping an existing stream
- Parameters:
underlying – the stream to wrap
filename – the filename to report in errors
auto_skip_exec – if True, skip any #! line at the start
- Returns:
a SourceStream