Solisp is a LISP-dialect interpreter and sBPF compiler for Solana blockchain automation and on-chain program development.
src/
├── lib.rs # Public API exports
├── error.rs # Error types (SolispError, Result)
├── lexer/ # S-expression tokenizer
│ └── sexpr_scanner.rs
├── parser/ # AST construction
│ └── sexpr_parser.rs
├── runtime/ # Interpreter
│ └── lisp_evaluator.rs
├── compiler/ # sBPF code generation
│ ├── mod.rs # Compiler orchestration
│ └── ir.rs # IR generation (4800+ lines, main logic)
├── decompiler/ # sBPF to OVSM reverse engineering
├── ai/ # AI-assisted code generation
├── metrics/ # Performance metrics
├── parallel/ # Concurrent execution
└── tools/ # Utility functions
- Tokenizes LISP S-expressions
- Handles:
(,), strings, numbers, symbols, comments - Line/column tracking for error messages
- Builds AST from tokens
- Expression types:
ToolCall,Variable,StringLiteral,IntLiteral, etc. - Validates syntax structure
- Interprets Solisp code in-memory
- Built-in functions: arithmetic, control flow, data structures
- MCP tool integration for blockchain operations
- Generates sBPF bytecode from Solisp source
- 6100+ lines containing ALL macro implementations
- Key sections:
- Struct operations (lines ~1100-1600)
- CPI helpers (lines ~1900-3000)
- SPL Token CPIs (lines ~3000-3400)
- Round 4 macros (lines ~3380-4300)
- Anchor error handling (lines ~4290-4400)
- PDA operations (lines ~4400-4600)
- Event emission (lines ~4800-4900)
- Sysvar access (lines ~4900-5100)
- PDA caching (lines ~5100-5300)
# Build the crate
cargo build -p solisp
# Run all tests
cargo test -p solisp
# Run specific test module
cargo test -p solisp sexpr # Lexer/parser tests
cargo test -p solisp lisp_evaluator # Evaluator tests
# Compile a Solisp file to sBPF
solisp compile input.solisp -o output.soWhen implementing new macros in compiler/ir.rs:
- Location: Add after existing similar macros (e.g., CPI macros near other CPIs)
- Pattern: Match on
name == "macro-name" && args.len() == N - Registers: Use
self.alloc_reg()for temp registers - Instructions: Emit via
self.emit(IrInstruction::*) - Return: Always
return Ok(Some(result_reg))orOk(Some(zero))
Example:
if name == "my-macro" && args.len() == 2 {
let arg1 = self.generate_expr(&args[0].value)?
.ok_or_else(|| Error::runtime("arg1 has no result"))?;
let result = self.alloc_reg();
self.emit(IrInstruction::ConstI64(result, 42));
return Ok(Some(result));
}- Account size: 10336 bytes in serialized input
- Account offsets:
- is_signer: +1
- is_writable: +2
- owner: +40
- lamports: +56
- data_len: +64
- data: +72
- Input pointer (R1): 1
- Heap base: 0x300000000
define-struct,struct-size,struct-offset,struct-field-sizestruct-get,struct-set,struct-ptr,struct-idl
account-data-ptr,account-data-len,account-lamportsis-signer,is-writable,assert-signer,assert-writable,assert-ownerzerocopy-load,zerocopy-store
system-transfer,system-create-accountspl-token-transfer,spl-token-transfer-signedspl-token-mint-to,spl-token-burn
spl-close-account- Close token account, reclaim lamportsspl-close-account-signed- Close with PDA authoritysystem-allocate- Allocate space in account (System Program)system-allocate-signed- Allocate with PDA signersystem-assign- Assign account ownership to programsystem-assign-signed- Assign with PDA signeranchor-error- Return Anchor-compatible error (6000 + code)require- Assert condition or abort with Anchor errormsg- Log message (Anchor-style, usessol_log_)
derive-pda,create-pda,get-atapda-cache-init,pda-cache-store,pda-cache-lookup
emit-event,emit-logclock-unix-timestamp,clock-epoch,rent-minimum-balanceinstruction-count,assert-not-cpi
borsh-serialize,borsh-deserialize,borsh-size
- 469/469 tests passing (100%)
- Integration tests in
tests/lisp_e2e_tests.rs - Compilation tests in
/tmp/*.solispduring development