Pragmas and feature settings
A pragma is a set of module-scoped switches that tune how the reader and compiler treat your source. Koskari’s optional features – bracket-call checking, name-resolution mode, layout reading, and so on – are all controlled through the pragma. A pragma affects only the code that comes after it in the same module, and never leaks into modules you import.
Setting a pragma
Use the pragma form. Each setting is a bracketed list: a slash-namespaced key
followed by its value(s). A bare [key] uses the setting’s default value.
(pragma [compiler/call-brackets 'error]
[compiler/resolution 'scheme]
[reader/brackets False])
Values are ordinary expressions, so a literal symbol must be quoted –
'error,'scheme,'warn. Booleans (True/False), numbers, and strings are self-carrying and need no quote, and a computed value (a variable or call) works too.The change is forward-only: it applies to everything read and compiled after the form, not retroactively, and not to modules you import.
You may set a pragma more than once; a later setting replaces the earlier one.
Reading a pragma
The same pragma form reads the current state:
(pragma compiler/call-brackets) ; the current value of one setting
(pragma) ; the whole pragma object
And help describes it:
(help pragma) ; current values + every setting's default
(help (pragma compiler/call-brackets)) ; one setting: its signature and doc
(help pragma) lists the current values alongside every registered setting and
its default; (help (pragma KEY)) shows the accepted arguments and
documentation for a single setting.
A second read form exists for code that runs during compilation:
(active-pragma) ; the pragma of the module being compiled
(active-pragma compiler/enable-asserts) ; one of its settings
(pragma KEY) reads the pragma of the module the form is written in. A macro
body, though, is a closure over the module that defined it, while it runs
while some other module compiles – so inside a macro, (pragma KEY) would
answer for the wrong module. (active-pragma KEY) reads the pragma the
compiler has bound for the current compilation turn instead, so a macro’s
expansion is governed by the settings of the module using it. Outside a
compilation turn no pragma is bound, and (active-pragma) answers a
default pragma carrying every setting’s baseline value.
What each setting means
Settings are grouped by what they affect: the reader (reader/*), the compiler
(compiler/*), and the pragma machinery itself (pragma/*).
Reader
reader/index-brackets, reader/dot-attrs, and reader/string-segments
govern the three suffix syntaxes;
Grammar states their rules, and
turning one off makes its character inert in the reader.
reader/brackets, default on: whether[ ]reads into a distinct bracket-pair value that remembers its origin, rather than a plain pair. Off makes[ ]and( )completely indistinguishable. See Brackets and the call convention.reader/index-brackets, default on: whetherdata["bar"]reads as(item data "bar").reader/dot-attrs, default on: whetherobj.valuereads as(attr obj value).reader/string-segments, default on: whether a-on a string literal’s closing quote continues it into the next quoted segment.reader/unary, default on: whether-,+, and~attached to the front of a name, a digit, or a delimited form read as the unary call:-xis(- x),+xis(+ x),~(f y)is(~ (f y)). A signed number keeps its sign as part of the literal, and->or a bare operator stay ordinary symbols.reader/layout, default on: whether indentation-based layout blocks are read. Off leaves only s-expression syntax.reader/implicit-calls, default on: whether several values on one layout line form an implicit call. Off makes more than one value on a line an error.reader/indent, defaulterror: what the layout reader does with a content line whose indent does not match its block: the first line of a body sets the body’s indent, every later line must start at that column, and a top-level line starts at column 0.errorraises aReaderSyntaxError,warnemits aSyntaxWarningand reads the line as a sibling form,ignorereads it as a sibling form silently.reader/fences, default on: whether triple-backtick fences read intocompile/apply-fenceforms. Off, backtick keeps only its quote and quasiquote readings. See compile/apply-fence.reader/decorators, default on: whether leading@layout lines read as decorators, lowering the decorated form tocompile/decorate. Off,@has no layout meaning and reads through the ordinary atom rules. This gates the reader surface only;(decorate ...)remains available as a macro regardless.
Compiler
compiler/resolution, defaultkoskari: how writes to names resolve. The value is required, one of:koskari– strict: a write to an undeclared name is a compile error.scheme– an unsatisfied write promotes to a module global.python– a first write creates an implicit local unless declared global.
compiler/call-brackets, defaultwarn: what happens when a bracket-pair[ ]reaches call position, where( )is conventionally expected. One of:warn– emit aSyntaxWarning.error– raise a compile error.ignore– nothing; the convention is unenforced.
A bare
[compiler/call-brackets]selectswarn. See Brackets and the call convention.compiler/stray-colon, defaultwarn: what happens when a bare:symbol is compiled as a name. Nothing binds:, so this is always a mistake; it most often comes from a layout form half-converted to s-expressions, where the trailing block colon offor-each [x (xs)]: body
survives the rewrite as
(for-each [x (xs)]: body)– which reads as(for-each [x (xs)] : body)and fails at runtime with aNameError. One of:warn– emit aSyntaxWarning.error– raise a compile error.allow– nothing; the:compiles as an ordinary name lookup.
A bare
[compiler/stray-colon]selectswarn. A trailing colon attached to a name is a keyword, not a stray colon:(when foo: (foo))passes the keywordfoo:, whichcompiler/keyword-literalscovers instead.compiler/stray-hash, defaultwarn: what happens when a bare#symbol is compiled as a name. A#prefixes a collection opener (#(,#[,#{) or a syntax quote (#',#`); followed by a space or the end of the form it reads as the symbol#, so# [1 2]is a name lookup of#followed by a list rather than the list literal#[1 2]. One of:warn– emit aSyntaxWarning.error– raise a compile error.allow– nothing; the#compiles as an ordinary name lookup.
A bare
[compiler/stray-hash]selectswarn. A#that begins a longer name, such as#tuple, is not a stray hash, and a quoted'#is data and never flagged.compiler/keyword-literals, defaultwarn: what happens when an unquoted keyword is compiled as a literal value. A keyword evaluates to itself, which is legal but is more often a typo:(if foo: 1 2)tests the keywordfoo:for truthiness – always true – rather than reading the variablefoo. One of:warn– emit aSyntaxWarning.error– raise a compile error.allow– nothing; the keyword compiles to itself.
Quote the keyword to state that the literal is what you meant;
'foo:compiles through the quote special and is always exempt. Two other uses are structural rather than value positions, so they are never flagged: keyword arguments ((print 1 sep: "-")) and keyword markers consumed by a form ((cond [(> x 1) 'big] [else: 'small])).compiler/enable-asserts, default on: whether(assert ...)forms expand to their check. Off, anassertcompiles away entirely: neither the condition nor the message is compiled or evaluated, mirroring Python’s-Obehavior. The gate is consulted as eachassertis compiled, so a change affects only the asserts that appear after it. See compile/assert.compiler/bare-control-words, defaultconvert: what happens when a barereturn,continue,break,raise,yield, orpasssymbol is compiled. These statement words are call forms in Koskari, and their bare forms expand the lone symbol into its call form –returnbecomes(return)– matching the Python-style spelling.passexpands to(begin), a no-op that evaluates toNone. One of:convert– expand silently.warn– emit aSyntaxWarning, then expand.error– refuse with a compile error naming the corrective spelling.
Each module answers for its own setting: the gate reads the pragma of the module being compiled, so a strict module does not harden its neighbors. The cost of conversion is that these six names always expand and can never name a variable. See compile/return, compile/continue, compile/break, compile/raise, compile/yield, and compile/begin (
pass).compiler/rewrite-limit, default 32: how many macro-expansion rewrites a single form may take before the compiler reports it as divergent. A macro that expands into an ever-growing form stops with a compile error instead of hanging. An expansion that repeats an earlier form is reported immediately as a cycle, regardless of this setting.0removes the limit; legitimate expansion chains are only a few rewrites deep, so the default leaves generous headroom.compiler/tco-enable, default on: whether tail calls are optimized.compiler/convert-fast, default on: whether the compiler lowers local variable access to its fast form. Off disables that optimization.compiler/compress, default off: reserved for a future bytecode compression pass; currently inert.compiler/cache-function, default on: whether function and lambda bodies attach a per-site cache to attribute and method-call instructions, so a repeated access with the same receiver type skips the resolution walk. On is the baseline; the reused code that loops flow through is where it pays. See Attribute and method caching.compiler/cache-expression, default off: the same caching for top-level module and REPL expressions. Off by default because top-level code usually runs once as it loads, so a cache would not amortize. A bare[compiler/cache-expression]turns it on. See Attribute and method caching.compiler/protect-code, default off: whether each compiled code object thatcompiler/pack-moduleleaves alone has its bytecode sealed read-only the moment it is built. On, a stray or hostile write to that bytecode faults instead of corrupting the interpreter, at the cost of a full memory page per code object. A bare[compiler/protect-code]turns it on. See Code sealing.compiler/pack-module, default on: whether the function code a module compiles while it loads is sealed read-only when the module finishes loading. On – the baseline – a loaded module’s functions are read-only at no per-object cost. Top-level expressions are not covered.(pragma [compiler/pack-module False])
See Code sealing for the model and its tradeoffs.
Pragma machinery
pragma/strict-settings, default on: when on, naming a setting that is not registered is an error; off downgrades it to a warning.pragma/strict-watchers, default on: when on, an error while applying a pragma change is raised; off downgrades it to a warning.
See also
Brackets and the call convention – the convention
compiler/call-bracketsenforces.Grammar – the reader grammar the
reader/*settings gate.