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.

Built With

Share this project:

Updates