Compiler API Reference¶
The compiler module (jsl.compiler) transforms JSL S-expressions into JPN (JSL Postfix Notation) for execution by the stack evaluator.
Overview¶
The compiler performs several transformations: - Infix to postfix conversion for operators - Special form compilation with control flow - Arity tracking for n-ary operations - Optimization of common patterns
Functions¶
compile_to_postfix¶
The main compilation function:
jsl.compiler.compile_to_postfix(expr)
¶
Compile a JSL S-expression to JPN (JSL Postfix Notation).
Examples:
['+', 2, 3] → [2, 3, '+'] # Binary (no arity needed) ['', 2, ['+', 1, 2]] → [2, 1, 2, '+', ''] # Nested binary ops ['+', 1, 2, 3, 4] → [1, 2, 3, 4, 4, '+'] # N-ary (arity before op) ['+'] → [0, '+'] # 0-arity ['list', 'a', 'b'] → ['a', 'b', 2, 'list'] # List creation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expr
|
Any
|
S-expression in JSL format |
required |
Returns:
| Type | Description |
|---|---|
List[Any]
|
JPN - list of instructions in postfix order (JSON-compatible) |
Source code in jsl/compiler.py
decompile_from_postfix¶
Reconstructs S-expressions from JPN (for debugging):
jsl.compiler.decompile_from_postfix(postfix)
¶
Convert JPN back to S-expression (for debugging/display).
This is the inverse of compile_to_postfix: compile_to_postfix(decompile_from_postfix(jpn)) == jpn
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
postfix
|
List[Any]
|
List of JPN instructions |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Equivalent S-expression |
Examples:
[2, 3, 2, '+'] → ['+', 2, 3][1, 2, 3, 3, '+'] → ['+', 1, 2, 3]['x', 'y', 2, '+'] → ['+', 'x', 'y'][0, '+'] → ['+'] # 0-arity addition
Source code in jsl/compiler.py
Compilation Examples¶
Simple Expressions¶
from jsl.compiler import compile_to_postfix
# Arithmetic
expr = ["+", 1, 2, 3]
jpn = compile_to_postfix(expr)
print(jpn) # [1, 2, 3, 3, "+"]
# Nested expressions
expr = ["*", ["+", 2, 3], 4]
jpn = compile_to_postfix(expr)
print(jpn) # [2, 3, 2, "+", 4, 2, "*"]
Special Forms¶
from jsl.compiler import compile_to_postfix
from jsl.stack_special_forms import Opcode
# If expression
expr = ["if", ["=", "x", 5], "yes", "no"]
jpn = compile_to_postfix(expr)
# Result includes jump instructions for control flow
# Lambda expression
expr = ["lambda", ["x", "y"], ["+", "x", "y"]]
jpn = compile_to_postfix(expr)
# Result: [["x", "y"], ["+", "x", "y"], Opcode.SPECIAL_FORM, "lambda"]
Function Calls¶
from jsl.compiler import compile_to_postfix
# Simple function call
expr = ["square", 5]
jpn = compile_to_postfix(expr)
print(jpn) # [5, 1, "square"]
# Multiple arguments
expr = ["add", 1, 2, 3]
jpn = compile_to_postfix(expr)
print(jpn) # [1, 2, 3, 3, "add"]
JPN Format¶
Instruction Types¶
-
Literals: Push themselves onto stack
-
Operators with Arity: Arity precedes operator
-
Special Forms: Use
Opcode.SPECIAL_FORMmarker -
Control Flow: Jump instructions
Compilation Rules¶
| S-Expression | JPN Output | Notes |
|---|---|---|
42 |
[42] |
Literals compile to themselves |
"x" |
["x"] |
Variables compile to strings |
["+", 1, 2] |
[1, 2, 2, "+"] |
Args, arity, operator |
["if", c, t, f] |
Complex with jumps | Control flow compilation |
["lambda", params, body] |
[params, body, Opcode.SPECIAL_FORM, "lambda"] |
Closure creation |
Decompilation¶
For debugging, JPN can be decompiled back to S-expressions:
from jsl.compiler import compile_to_postfix, decompile_from_postfix
# Original expression
original = ["*", ["+", 1, 2], 3]
# Compile to JPN
jpn = compile_to_postfix(original)
print(jpn) # [1, 2, 2, "+", 3, 2, "*"]
# Decompile back
reconstructed = decompile_from_postfix(jpn)
print(reconstructed) # ["*", ["+", 1, 2], 3]
assert original == reconstructed # True (for most expressions)
Advanced Topics¶
Optimization Opportunities¶
The compiler could optimize common patterns:
- Constant folding: Evaluate constant expressions at compile time
- Dead code elimination: Remove unreachable code after if
- Tail call optimization: Convert tail calls to jumps
Custom Operators¶
Adding new operators requires: 1. Add to prelude with known arity 2. Compiler automatically handles them 3. No special compilation logic needed
# In prelude
def custom_op(a, b, c):
return a + b * c
# Register in prelude
prelude["my-op"] = custom_op
# Automatically compiles correctly
expr = ["my-op", 1, 2, 3]
jpn = compile_to_postfix(expr) # [1, 2, 3, 3, "my-op"]