About the Project
I built BASIS because embedded software kept running into the same frustrating problem: the code would compile cleanly, look fine in review, and still fail later in the real world due to hidden stack growth, unpredictable execution paths, unsafe interrupt behavior, or foreign code behaving differently than expected. In embedded systems, these failures are not just annoying but also they are expensive, hard to reproduce, and sometimes impossible to diagnose once deployed.
BASIS is my attempt to move these failures from runtime to compile time. It is a deterministic, resource-aware systems programming language for embedded development, featuring compile-time analysis, explicit memory budgets, and backends that generate C and LLVM IR for integration with existing toolchains.
Inspiration
What inspired this project was the gap between how embedded software is expected to behave and how it actually behaves as systems grow in complexity. I wanted a language that does not merely assist with safety, but enforces it.
This led to a design centered around:
- bounded control flow
- explicit memory constraints
- whole-program call-graph analysis
- interrupt safety validation
- task entry-point validation
- strict foreign-function contracts
Instead of relying entirely on developer discipline, the compiler becomes part of the safety model.
How I Built It
Building BASIS required designing a full compiler pipeline rather than just a language syntax. The system includes:
- Lexer and parser
- Abstract Syntax Tree (AST)
- Semantic analysis and type checking
- Constant evaluation
- Loop and recursion analysis
- Whole-program resource tracking
- Backend code generation
The compiler lowers programs through a custom intermediate representation (BIR) before targeting:
- C (for embedded integration)
- LLVM IR (.ll) (for optimization and portability)
This architecture ensures that high-level constraints are preserved throughout the compilation process.
What I Learned
One of the biggest lessons was that safety is only valuable if it is explainable. A compiler can reject code for the right reason and still frustrate users if the feedback is unclear.
I focused on making diagnostics meaningful:
- identifying where memory budgets are exceeded
- showing the exact call path causing issues
- explaining why loops are rejected
- enforcing explicit contracts for external functions
I also learned that developers do not want to abandon their existing ecosystems. This is why BASIS integrates with C and LLVM instead of introducing an isolated runtime.
Challenges Faced
The hardest challenge was balancing strictness with usability.
- Too permissive → loses safety guarantees
- Too restrictive → becomes unusable
BASIS addresses this by enforcing determinism while still providing practical standard libraries for:
- math
- I/O and printing
- string handling
- time and scheduling
- memory operations
- MMIO access
- bit manipulation
- CRC and queue utilities
Another major challenge was supporting real-world deployment. The compiler generates:
- target build manifests
- validation scripts
- embedded scaffolding for ESP32, STM32, and RP2040
This ensures the system works beyond theoretical examples.
Key Insight
This project changed how I think about reliability.
Instead of testing for safety after development, safety should be enforced during compilation.
If a program declares a memory budget ( M ), and the compiler proves that usage ( U ) satisfies:
[ U \leq M ]
then an entire class of runtime failures is eliminated before execution.
Conclusion
BASIS is still evolving, but the core idea is clear:
Embedded software should be predictable by construction, not trusted by assumption.
This project transformed my understanding of systems programming by showing that a compiler can act not just as a translator, but as a verifier of system behavior.
Log in or sign up for Devpost to join the conversation.