Compiler
koskari.compile
The koskari compiler. Converts source forms into bytecode fragments.
- class koskari.compile.ByteCodeFragment(compiler=None, name=None, filename=None, resolution=None)
Bases:
objectHolds a fragment of bytecode during compilation: instructions plus closure requests and name declarations for the namespace
- as_expression(parent)
- as_function(parent, formals_spec=((), ()), defaults=None)
- as_function_spec(formals_spec=((), ()))
- as_namespace_spec()
Build a NamespaceSpec from this fragment’s name and declarations.
- child_namespace(ns_name, names, values)
Context manager that compiles a child scope with bound names. On entry, compiles all value expressions and yields a fragment for the body. On exit, emits value expressions (if any), then PUSH_NS with nspec and names in the EXTRA row, body instructions, POP_NS.
- Parameters:
ns_name – name for the child namespace (e.g. source form)
names – sequence of bound names, symbols
values – sequence of expressions (one per name)
- collector_namespace(ns_name)
Context manager for a collector namespace (CollectorNamespace). Like child_namespace but uses PUSH_COLLECT/POP_COLLECT. POP_COLLECT pushes the collected namespace onto the stack.
- Parameters:
ns_name – name for the child namespace
names – sequence of bound names (strings or symbols)
values – sequence of expressions (one per name)
- declare_var(var_name, position=None, type_expr=None)
Record a name declared in this fragment’s namespace.
- Parameters:
var_name – name to declare
position – optional (line, col) for error reporting
type_expr – optional source form for a type annotation (compile metadata only; not evaluated here)
- declare_vars(var_names)
Record multiple names declared in this fragment’s namespace
- Parameters:
var_names – iterable of names to declare
- instruction(op, opa)
Append one bytecode instruction
- Parameters:
op – OpCode for the instruction
opa – argument for the instruction; a variable operand, the opcode’s own or a variable field of a fused row, is a symbol
- Raises:
TypeError – when a variable operand is not a symbol
- marked = False
- merge(other)
Merge another fragment into this one: adopt its declarations, requests, requests_for_write, request_global, and annotation_bindings, then append its instructions.
Used when combining forms in the same scope (e.g. begin body); request_global must propagate so that (define-global x) followed by (setq x 1) in the same block works.
- note_mark(sym)
Record that this fragment references a marked name, so its completion resolves its names
- Parameters:
sym – a gensym
- position(linecol, extra=None)
Emit a position instruction
- Parameters:
linecol – (line, column) or position tuple
extra – optional extra position data (e.g. source text)
- position_of(source)
Emit a position instruction from the source’s position if set
- Parameters:
source – a pair or object with get_position
- request_for_write(var_name, position=None)
Record that var_name must be writable (setq, delq, inplace).
- Parameters:
var_name – name that must be writable
position – optional (line, col) for error reporting
- request_global(var_name, position=None)
Record explicit need from module (define-global).
- Parameters:
var_name – name to pull from module
position – optional (line, col) for error reporting
- request_var(var_name, position=None)
Record a closure request for var_name unless it is already declared
- Parameters:
var_name – name to request from enclosing scope
position – optional (line, col) for error reporting
- rewrite_vars(choose)
Replace each variable operand in the instruction stream with choose(name, scopes), scopes holding the spelling sets of the inline scopes open around it, innermost first. An inline scope’s closure names are chosen from outside that scope
- Parameters:
choose – callable taking an atom and the open scopes, returning the atom to emit
- class koskari.compile.Compiler(name, module, filename=None, tco_enabled=True, resolution_mode=ResolutionMode.KOSKARI, bracket_calls=BracketCalls.IGNORE, stray_colon=StrayColon.ALLOW, stray_hash=StrayHash.ALLOW, keyword_literals=KeywordLiterals.ALLOW)
Bases:
object- property bracket_calls
- check_keyword_literal(kwd, pos=None)
Diagnose an unquoted keyword compiled as a literal value per the keyword_literals policy. It evaluates to itself, which is legal but is far more often a typo –
(if foo: 1 2)tests the keywordfoo:for truthiness (always true) instead of readingfoo.Quoting is exempt: the quote special emits its own constant and never reaches here. Keyword arguments are exempt too, being consumed structurally by gather_parameters rather than as values.
Keywords are interned and so carry no position of their own;
posis the enclosing form’s(line, col), used to attribute a WARN. It defaults to the innermost form compiled so far.- Parameters:
kwd – the keyword about to be compiled as a constant
pos – position of the enclosing form, or None
- check_rewrite_cycle(seen, form)
Guard the full_compile rewrite loop: a rewrite that repeats an earlier form in this position is a macro-expansion cycle, and a chain past the settling limit is divergent growth; either way the compile stops here instead of spinning.
Identity is exempt from the cycle check: a special may mutate its source in place and return the same object to request a recompile (the return/yield keyword-values fixup), which is progress, not a cycle. A genuinely stuck identical form falls to the settling limit instead.
The settling limit comes from the compiler/rewrite-limit pragma setting; 0 disables the limit while the cycle check stays in force.
- Parameters:
seen – prior rewrite forms in this position, oldest first
form – the newly produced rewrite
- Raises:
CompilerError – on a cycle or an unsettled chain
- check_stray_symbol(sym, pos=None)
Diagnose a bare
:or#symbol compiled as a name per the stray_colon or stray_hash policy. Nothing binds either, so reaching here is always a mistake. A stray:is usually a layout form half-converted to s-expressions, where the trailing block marker of(for-each [x (xs)]: body)reads as a symbol element rather than as a block colon. A stray#is usually a typo for a collection opener or a syntax quote.Symbols are interned and so carry no position of their own;
posis the enclosing form’s(line, col), used to attribute a WARN. It defaults to the innermost form compiled so far.- Parameters:
sym – the symbol about to be compiled as a name
pos – position of the enclosing form, or None
- compile(source_obj, *, position=None)
- compile_const(fragment, value, position=None)
- compile_expr(fragment, source_obj, position=None)
positionis the innermost enclosing source position known to the caller, threaded to diagnostics about positionless atoms; a source object carrying its own position wins over it
- compile_expression(source_obj, position=None)
Compile one top-level source expression: the entry point for the places a compilation turn starts – the module loaders, the REPL, the serializer, and any harness whose pragma should govern the turn. Binds this compiler’s pragma into the active-compiler-pragma parameter for the turn’s extent, so macro bodies running during the compile read the compiling module’s pragma through (active-pragma), regardless of which module defined them. Inner recursive compilation chains through compile and full_compile directly and never rebinds.
- Parameters:
source_obj – top-level source form
position – where the reader found the form, for a form that carries no position of its own (an atom)
- Returns:
compiled ByteCodeFragment
- compile_pair(fragment, source_obj, position=None)
- compile_raw_symbol(fragment, sym, position=None)
- compile_special(fragment, comp, source_obj)
- compile_symbol(fragment, sym, position=None)
- complete(fragment, mode=None, _validate=True, is_root=False, ancestors=None)
Complete a fragment into a Code object.
The Deferred constants the fragment holds complete first, against this fragment’s now final declarations, and their values take their place
- Parameters:
fragment – the fragment to complete
mode – the mode to complete in
_validate – whether to validate the code
is_root – whether this is the root fragment
ancestors – the fragments lexically enclosing this one, outermost first; the open lexical stack when omitted
- Returns:
the completed Code object
- complete_function(fragment, ancestors)
Complete a function body fragment under the given ancestry
- Parameters:
fragment – the function body fragment
ancestors – the fragments enclosing it, outermost first
- Returns:
the completed Code object
- find_bare_compiled(name)
Resolve as
find_compiled(), keeping only a Compiled that declares a bare form. Anything else resolves as not found, so a symbol naming an ordinary special still compiles as a variable; prefixed spellings never resolve as uses, per find_compiled- Parameters:
name – symbol or string for the compile-time object
- Returns:
Compiled instance with a bare form, or None
- find_compiled(name)
Resolve compile/<name> in the module namespace
- Parameters:
name – symbol or string for the compile-time object (e.g. if, begin)
- Returns:
Compiled instance, or None
- fragment(name=None)
- classmethod from_pragma(name, module, filename=None, pragma=None)
Create a compiler from a pragma, or a default pragma and its settings.
- full_compile(source_obj, *, position=None)
- property keyword_literals
- limit_error(fragment, ex)
A size the processor or the finalizer refused, reported as a compiler error at the fragment’s first recorded position
- lower_head(form, position=None)
Attempt to lower a form found in operator position. Caller: compile_pair.
The attempt nests through arbitrarily deep pair heads: each level resolves its own head, and only a form whose innermost head names a syntax is attempted at all. A syntax whose expansion yields a source form succeeds, reconstructing each enclosing level around the result; one that compiles to bytecode answers with that fragment, which the caller remembers against the head so the call path’s compile of the same head is that fragment and the head is never compiled twice. A non-syntax head fails the whole attempt, committing nothing.
- Parameters:
form – the pair found in operator position
position – enclosing source position threaded to diagnostics
- Returns:
the lowered replacement form, a fragment the head compiled to, or None when the attempt fails
- macro_overlay(name, syntax)
- macro_overlays(**dfns)
Context manager that overlays the macro namespace with the given definitions.
- open_allocator()
Open a CodeAllocator for a module load, unless one is already open. Function code completed from here until the next seal is placed into its arena chunks while compiler/pack-module is on
- Returns:
the open CodeAllocator
- position_atom(fragment, position)
An atom carries no position; when it is the whole of what the fragment holds so far, the position threaded from the reader names its line
- reload_pragma(pragma)
Reload the compiler from a pragma.
- property resolution_mode
- resolution_scope(fragment=None, function=False)
Bracket the compilation of a nested scope’s body
- Parameters:
fragment – the fragment the body compiles into; while the body is open it sits on the lexical stack as an enclosing scope of anything completed inside it
function – the fragment is a function body, the kind of scope a Code is built from
- scope_overlay(name, syntax)
Overlay the macro namespace with one definition until the innermost open resolution scope, or the enclosing macro_overlay extent, closes
- Parameters:
name – the binding spelling, compile/NAME
syntax – the Compiled object to resolve it to
- seal()
Seal the arena chunks this module load placed function code into, read-only, and close the load’s allocator. Code completed afterwards is a heap block until a later load opens another.
Idempotent: with no allocator open, there is nothing to seal. Nothing moves, so sealing is safe at any point between two completions.
- Returns:
tuple of the sealed Arena chunks; empty when no allocator was open or nothing was placed
- property stray_colon
- property stray_hash
- symbol_is_variable(sym)
Whether a lone symbol compiles to a variable read, so a special may name it in a fused VAR operand: it is not a compiler literal and no bare arm expands it. Anything else must go through full_compile, which expands the bare arm or emits the constant
- Parameters:
sym – the symbol as written, marks included
- exception koskari.compile.CompilerError(message, source=None, position=None, filename=None)
Bases:
KoskariExceptionBase class for compile-phase errors
- attach_position(position=None, filename=None)
Fill in a position and a filename the raise site did not have: the innermost enclosing form’s position and the compiler’s file. What the error already carries is kept
- explain()
User-facing error text for REPL and similar UIs
- exception koskari.compile.ParametersError(message, source=None, position=None, filename=None)
Bases:
CompilerErrorCall-site parameter errors from
koskari.compile.helpers.gather_parameters()validation
- exception koskari.compile.SpecialError(message, source=None, position=None, filename=None)
Bases:
CompilerErrorSpecial-form validation errors
- exception koskari.compile.SyntaxMatchError(message, source=None, position=None, filename=None, *, syntax=None, patterns=None)
Bases:
SpecialErrorSyntax dispatch errors when no matcher arm matches the source form
- explain()
User-facing error text for REPL and similar UIs
- exception koskari.compile.UncaughtCompilerError(message, source=None, position=None, filename=None)
Bases:
CompilerErrorWraps unexpected exceptions raised during compilation