Compile-Time Code Generation¶
IVLS-KSP provides compile-time code generation via literate macros, callback injection, and voice logic generation. These directives are processed by the ivls-sksp compiler before conversion to native KSP.
Literate Macros¶
literate_macro(pattern) on LIST¶
Iterates over a comma-separated list and expands a pattern for each item.
| Placeholder | Description |
|---|---|
#l# |
Current list item (literal value) |
#n# |
Current index (0-based) |
define IVLS_ALL_NODES := Ivls.NodeUI, Ivls.VoiceOps, Ivls.Flows
family ivls
const node
literate_macro(#l#) on IVLS_ALL_NODES
end const
end familydefine DEBUGS := Audio, MIDI, UI
literate_macro(debug.category_names[debug.category.#l#] := "#l#") on DEBUGSWhen the pattern lacks #l#, it is called as a macro with the list item as argument:
literate_macro(Voice.WriteInitList) on Voice.INIT_LIST
// Expands to: Voice.WriteInitList(Voice.BASE_INIT), Voice.WriteInitList(Voice.ADD_INIT)literate_post_macro(pattern) on LIST¶
Processed after regular macro expansion -- use when the list is itself defined by macros:
macro ivls.MakeArgs(#node#)
const args
literate_post_macro(#l#) on #node#.Args
end const
end macroCallback Injection¶
__RUN_CB__(CallbackName)¶
Injects code from all nodes that define the named callback:
on init
__RUN_CB__(Init)
__RUN_CB__(ICB)
__RUN_CB__(PostInit)
__RUN_CB__(Post_ICB)
end on__VIRTUAL__(Name)¶
Defines an extension point in a base node that child nodes can override:
node Stl.PlayEvent.Template:
cb NoteOn:
declare event.note := Voice[self].note
__VIRTUAL__(EventArgs)
Voice[self].event := play_note(event.note, event.vel, 0, 0)
__VIRTUAL__(EventGroups)
node Stl.PlayEvent from Stl.PlayEvent.Template:
cb EventArgs:
event.note := event.note + 12
cb EventGroups:
event.groups[0] := TRUE
end nodeThe compiler replaces each __VIRTUAL__(Name) with the corresponding cb Name: block from the child node.
Voice Logic Generation¶
__DECLARE_VOICE_LOGIC__ / __SELECT_VOICE_LOGIC__¶
Generates voice logic taskfuncs and dispatch case statements for all nodes with NoteOn/NoteOff callbacks:
// Generated pattern:
taskfunc ivls.NodeName.VoiceLogic(var self, var self_invalid, var user_continue, var passed_vo, nenv)
if NodeEnv[nenv].input_type = ivls.input_type.VOICE_ON
// NoteOn callback code
else if NodeEnv[nenv].input_type = ivls.input_type.VOICE_OFF
// NoteOff callback code
end if
end taskfunc
select NodeEnv[nenv].node_id
__SELECT_VOICE_LOGIC__
end select__DECLARE_VOICE_PASS__ / __SELECT_VOICE_PASS__¶
Same pattern for nodes with NotePass callbacks.
__CACHE_PASSES__ / __CACHE_OFFS__¶
Generate lookup tables marking which nodes support pass-through or note-off behavior:
declare node_passes[ivls.node.SIZE] := (FALSE)
__CACHE_PASSES__
// Sets ivls.node_passes[ivls.node.NodeWithPass] := TRUE for each applicable nodeProcessing Order¶
- Define substitution --
defineconstants resolved - Literate macro expansion --
literate_macrodirectives expanded - Regular macro expansion -- standard macros expanded
- Post macro expansion --
literate_post_macrodirectives expanded - Node assembly -- node callbacks parsed, inheritance resolved
- Callback injection --
__RUN_CB__directives inject node code - Voice logic generation --
__DECLARE_*/__SELECT_*generate dispatch code - Caching --
__CACHE_*directives generate lookup tables