CompilerContext provides a high-level API for constructing executable specifications through a fluent interface that leverages Python operator overloading and builder patterns. It orchestrates the creation of CompiledSubroutine objects by managing basic blocks, local variables, and instruction sequences. The module supports primitive operations, container manipulations (arrays, sets, maps, transforms), field access, exception handling, and both static and virtual method calls. Readable objects encapsulate instruction sequences and enable arithmetic and comparison operations through dunder methods, while VariableHandle and JoinedHandle manage variable lifecycle and access patterns.
CompilerContext serves as the primary interface for programmatic specification assembly in the Micro-SVM project. It abstracts the low-level instruction generation process, allowing developers to write specifications using "natural" Python constructs. The context maintains compilation state including the current basic block, local variables, loop identifiers, and incomplete control flow graph builders. It provides methods for all supported operations including primitive arithmetic, container operations, object field access, exception handling, and method invocation. The design follows a single-entry, single-exit subroutine model for simplicity, with special handling for error paths via error().
The compiler module is central to the Project's specification system, enabling the creation of executable program specifications that can be explored and validated. It works in conjunction with GlobalContext for function and structure metadata, micro_svm.cfg for control flow graph construction, and micro_svm.instructions for low-level instruction definitions. Specifications created via CompilerContext are used by PathEnumerator for path exploration, SymbolicStateMachine for symbolic execution. The module's output is serialized via micro_svm.serialization for storage and loading.
CompilerContextexpectsFunctionInfoobjects fromGlobalContextto provide parameter metadata and result type information.- All
VariableHandleobjects must be created viaCompilerContextmethods (make_local_variable(),get_global_variable(),get_parameter(),get_function_result()). JoinedHandleobjects combine aReadable(read handle) with aVariableHandle(write handle) and must be used consistently for variable operations.call()method requires proper handling of return types; whendiscard_result=Trueis set, the return value is explicitly popped from the stack.- Loop constructs (
begin_loop(),loop_break(),loop_continue()) require proper nesting and must be used within the context of a loop ID stack managed byCompilerContext. - Exception handling via
begin_try()requires at least one catch handler or a finally handler to be registered. error()marks the current path as failed and sets_ignore_followup_instructionstoTrue, preventing further instruction generation on that path.
CompilerContext manages the compilation state and provides methods for generating instructions. It maintains the current basic block, local variables, loop identifiers, and incomplete control flow graph builders. The context is initialized with an optional FunctionInfo object representing the function being compiled.
-
Fields:
current_function: FunctionInfo | None- The function currently being compiled, orNonefor top-level specifications
-
Methods:
abs(value: Readable) -> Readable- Computes absolute valuearray_copy(item_type: PrimitiveTypeInfo | None, src: Readable, src_index: Readable, dst: Readable, dst_index: Readable, count: Readable) -> None- Copies elements between arraysarray_equals_range(item_type: PrimitiveTypeInfo, a: Readable, a_index: Readable, b: Readable, b_index: Readable, count: Readable) -> Readable- Checks if two array ranges are equalarray_get(item_type: PrimitiveTypeInfo, array_ref: Readable, index: Readable) -> Readable- Gets an array elementarray_new(item_type: PrimitiveTypeInfo, size: Readable) -> Readable- Creates a new arrayarray_set_size(item_type: PrimitiveTypeInfo, array_ref: Readable, size: Readable) -> None- Sets the size of an arrayarray_set(item_type: PrimitiveTypeInfo, array_ref: Readable, index: Readable, value: Readable) -> None- Sets an array elementassume(expression: Readable) -> None- Adds an assumption/constraint to the current pathbegin_if(condition: BranchCallbackWithResult) -> IfBuilder- Creates an if-then-else blockbegin_loop(condition: BranchCallbackWithResult) -> LoopBuilder- Creates a while loopbegin_switch(value_type: PrimitiveTypeInfo, value: BranchCallbackWithResult) -> SwitchBuilder- Creates a switch statementbegin_try(body: BranchCallback) -> TryBuilder- Creates a try-catch-finally blockbuild() -> CompiledSubroutine- Compiles the current subroutine, adding entry/exit blocks and initializing parameters and return valuecall(function_or_method: str | tuple[str, str], args: list[Readable], return_type: PrimitiveTypeInfo | None, *, discard_result: bool = False, virtual: bool = True) -> Readable | None- Calls a static or virtual function/methodclear_fault_status() -> None- Clears the fault status flagconcat_strings(str_a: Readable, str_b: Readable) -> Readable- Concatenates two stringsconst(value: object, type: PrimitiveTypeInfo = integer) -> Readable- Creates aReadablecontaining a constant valuecontainer_size(container_ref: Readable) -> Readable- Returns the size of a containerdistinct_values(values: list[Readable]) -> Readable- Checks if the provided values are distinct or notend_of_program() -> None- Marks the end of the programerror(metadata: str | None = None) -> None- Marks the current path as failedfield_read(instance_ref: Readable, structure_name: str, field_name: str) -> Readable- Reads a field value from an objectfield_write(instance_ref: Readable, structure_name: str, field_name: str, value: Readable) -> None- Writes a new value to a field of an objectfloor(value: Readable) -> Readable- Computes floor valuefree_instance(ref: Readable, destructor: str | tuple[str, str] | None = None) -> None- Frees an instance (with optional destructor call)get_fault_status() -> Readable- Reads the fault status flagget_function_result() -> JoinedHandle- Retrieves the function result variable as aJoinedHandleget_global_variable(name: str) -> JoinedHandle- Retrieves a global variable as aJoinedHandleget_parameter(name: str) -> JoinedHandle- Retrieves a function parameter as aJoinedHandleget_string_length(s: Readable) -> Readable- Gets the length of a stringinstance_of(ref: Readable, structure_name: str, *, exact_match: bool = False) -> Readable- Checks if an object is an instance of a specific structure/class typeinteger_to_string(value: Readable) -> Readable- Converts an integer to a stringis_array(ref: Readable, item_type: PrimitiveTypeInfo) -> Readable- Checks if a reference points to an arrayis_map(ref: Readable, key_type: PrimitiveTypeInfo, value_type: PrimitiveTypeInfo) -> Readable- Checks if a reference points to a mapis_set(ref: Readable, item_type: PrimitiveTypeInfo) -> Readable- Checks if a reference points to a setis_transform(ref: Readable, key_type: PrimitiveTypeInfo, value_type: PrimitiveTypeInfo) -> Readable- Checks if a reference points to a transformloop_break() -> None- Breaks out of the current looploop_continue() -> None- Continues to the next iteration of the current loopmake_local_variable(type: PrimitiveTypeInfo, tags: set[str] | None = None) -> JoinedHandle- Creates a new local variable and returns aJoinedHandlefor read/write accessmap_any_key(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable) -> Readable- Gets an arbitrary key from a mapmap_any_value(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable) -> Readable- Gets an arbitrary value from a mapmap_equals(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], a: Readable, b: Readable) -> Readable- Checks if two maps are equalmap_get(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, key: Readable) -> Readable- Gets a map valuemap_has_key(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, key: Readable) -> Readable- Checks if a map has a keymap_has_pair(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, key: Readable, value: Readable) -> Readable- Checks if a map has a key-value pairmap_has_value(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, value: Readable) -> Readable- Checks if a map has a valuemap_intersection(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], in_a: Readable, in_b: Readable, out: Readable) -> None- Computes map intersectionmap_new(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo]) -> Readable- Creates a new mapmap_remove(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, key: Readable) -> None- Removes a map entrymap_set(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], map_ref: Readable, key: Readable, value: Readable) -> None- Sets a map valuemap_union(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], in_a: Readable, in_b: Readable, out: Readable) -> None- Computes map unionmax(a: Readable, b: Readable) -> Readable- Computes maximum valuemin(a: Readable, b: Readable) -> Readable- Computes minimum valuenew_instance(structure_name: str, constructor: str | None = None, args: list[Readable] = None) -> Readable- Creates a new instance of a structurenoop(comment: str | None = None) -> None- Adds a no-op instruction (for documentation/comments)set_add(item_type: PrimitiveTypeInfo, set_ref: Readable, item: Readable) -> None- Adds an item to a setset_contains(item_type: PrimitiveTypeInfo, set_ref: Readable, item: Readable) -> Readable- Checks if a set contains an itemset_equals(item_type: PrimitiveTypeInfo, a: Readable, b: Readable) -> Readable- Checks if two sets are equalset_get_any(item_type: PrimitiveTypeInfo, set_ref: Readable) -> Readable- Gets an arbitrary item from a setset_intersection(item_type: PrimitiveTypeInfo, in_a: Readable, in_b: Readable, out: Readable) -> None- Computes set intersectionset_new(item_type: PrimitiveTypeInfo) -> Readable- Creates a new setset_remove(item_type: PrimitiveTypeInfo, set_ref: Readable, item: Readable) -> None- Removes an item from a setset_union(item_type: PrimitiveTypeInfo, in_a: Readable, in_b: Readable, out: Readable) -> None- Computes set unionsimple_diff(result_type: PrimitiveTypeInfo, condition_same: Readable) -> Readable- Creates a simple difference operationstring_contains(s: Readable, substr: Readable) -> Readable- Checks if a string contains a substringstring_copy(s: Readable, offset: Readable, count: Readable) -> Readable- Copies a substringstring_ends_with(s: Readable, suffix: Readable) -> Readable- Checks if a string ends with a suffixstring_index_of(s: Readable, sub: Readable, offset: Readable) -> Readable- Finds the index of a substringstring_last_index_of(s: Readable, sub: Readable) -> Readable- Finds the last index of a substringstring_ord(s: Readable) -> Readable- Gets the ordinal value of a characterstring_replace(s: Readable, old: Readable, new: Readable) -> Readable- Replaces a substring oncestring_starts_with(s: Readable, prefix: Readable) -> Readable- Checks if a string starts with a prefixstring_to_integer(value: Readable) -> Readable- Converts a string to an integersymbolic(type: PrimitiveTypeInfo) -> Readable- Creates aReadablecontaining a symbolic (unconstrained) valuethrow(value: Readable) -> None- Throws an exceptiontransform_get(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], transform_ref: Readable, key: Readable) -> Readable- Gets a transform valuetransform_new(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo]) -> Readable- Creates a new transformtransform_set(kv_type: tuple[PrimitiveTypeInfo, PrimitiveTypeInfo], transform_ref: Readable, key: Readable, value: Readable) -> None- Sets a transform valuewrite(var: VariableHandle, value: Readable) -> None- Writes aReadablevalue to a variable
CompilerContext uses a builder pattern where Readable objects collect consecutive instructions. When an operation is performed (e.g., array_get()), the method returns a new Readable that chains the previous instructions with the new operation. This allows for fluent composition like ctx.array_get(type, array, index) where the result can be used directly in subsequent operations.
VariableHandles are used for writes, while Readable objects are used for reads, both combined into JoinedHandles. When writing to a variable, the write() method adds the value's instructions followed by a VariableWrite instruction.
Control flow constructs like begin_if(), begin_loop(), begin_switch(), and begin_try() use a deferred execution model. They create IfBuilder, LoopBuilder, SwitchBuilder, or TryBuilder objects that capture the current state and are later "explored" to generate the actual control flow graph. This allows for natural Python syntax while still building the appropriate CFG nodes.
Error handling is managed via the _ignore_followup_instructions flag. When error() is called, this flag is set to True, preventing further instruction generation on that path. This is important for handling unreachable code after error conditions.
Loop management uses a stack-based approach with _loop_id_stack. Loop identifiers are generated via _next_loop_id() and pushed when entering a loop (via begin_loop() for while loops) and popped when exiting. This allows loop_break() and loop_continue() to target the correct loop.
Readable represents a sequence of instructions that can be executed. It is used throughout the compiler API to represent values that can be composed using Python operators. The class is marked with @final to prevent inheritance.
-
Fields:
instructions: tuple[Instruction]- The sequence of instructions represented by thisReadableobject
-
Methods:
__add__(other: Readable) -> Readable- Composition via addition__sub__(other: Readable) -> Readable- Composition via subtraction__mul__(other: Readable) -> Readable- Composition via multiplication__div__(other: Readable) -> Readable- Composition via division__truediv__(other: Readable) -> Readable- Alias for__div__()__mod__(other: Readable) -> Readable- Composition via modulo__and__(other: Readable) -> Readable- Composition via bitwise AND__or__(other: Readable) -> Readable- Composition via bitwise OR__xor__(other: Readable) -> Readable- Composition via bitwise XOR__rshift__(other: Readable) -> Readable- Composition via right bit-shift__lshift__(other: Readable) -> Readable- Composition via left bit-shift__eq__(other: Readable) -> Readable- Composition via equality__ne__(other: Readable) -> Readable- Composition via inequality__lt__(other: Readable) -> Readable- Composition via less-than__le__(other: Readable) -> Readable- Composition via less-than-or-equal__gt__(other: Readable) -> Readable- Composition via greater-than__ge__(other: Readable) -> Readable- Composition via greater-than-or-equal__pos__(self) -> Readable- Returns theselfunchanged (identity operation)__neg__(self) -> Readable- Composition via negation__invert__(self) -> Readable- Composition via bitwise NOT
Readable objects are immutable in the sense that they cannot be modified after creation. Operations like __add__() create new Readable objects by concatenating the instructions from both operands and adding a new PrimitiveOp instruction. This design allows for natural, readable code while maintaining the ability to track instruction sequences.
VariableHandle represents a variable with a name, type, and local flag. It is used for writing to variables and is paired with a Readable for reading.
- Fields:
name: str- The name of the variabletype: PrimitiveTypeInfo | None- The type of the variable, orNoneif unknownis_local: bool- Whether the variable is local to the current function
JoinedHandle combines a Readable (read handle) with a VariableHandle (write handle), providing convenient access to both read and write operations for a variable.
- Fields:
r: Readable- The read handle for the variablew: VariableHandle- The write handle for the variable
GraphJunctionBuilder is an abstract base class for building control flow graph junctions. It is used by branching methods to defer the construction of control flow until the branching point is fully specified and to warn about incomplete/hanging builders.
-
Fields: (none)
-
Methods: (none)
IfBuilder constructs an if-then-else control flow block. It is returned by begin_if() and provides methods to specify the then and else branches.
- Methods:
then(action: BranchCallback) -> IfBuilder- Specifies the then branchotherwise(action: BranchCallback) -> IfBuilder- Specifies the else branchend_if() -> None- Finalizes the if block and connects it to the control flow graph
LoopBuilder constructs a while loop control flow block. It is returned by begin_loop() and provides a method to specify the loop body.
- Methods:
body(action: BranchCallback) -> LoopBuilder- Specifies the loop bodyend_loop() -> None- Finalizes the loop and connects it to the control flow graph
SwitchBuilder constructs a switch statement control flow block. It is returned by begin_switch() and provides methods to specify case handlers and a default handler.
- Methods:
when(value: ValueType, handler: BranchCallback) -> SwitchBuilder- Specifies a case handler for a particular valueotherwise(wildcard_handler: BranchCallback) -> SwitchBuilder- Specifies a default handler for unmatched valuesend_switch() -> None- Finalizes the switch and connects it to the control flow graph
TryBuilder constructs a try-catch-finally control flow block. It is returned by begin_try() and provides methods to specify catch handlers and a finally handler.
- Methods:
catch(error_structure_type: str, handler: TypedExceptionHandlerCallback) -> TryBuilder- Specifies a catch handler for a specific error structure typefinal(handler: BranchCallback) -> TryBuilder- Specifies a finally handlerend_try() -> None- Finalizes the try block and connects it to the control flow graph
None.
AI usage disclosure: this document was generated by a large language model, all text has been validated and edited by a human developer.