# microbasic-compiler

A from-scratch Python compiler for Roboteq's MicroBasic scripting language,
targeting Intel HEX bytecode files loadable onto SDC2160-family drives.

## Pipeline

```
source.mb -> lexer -> tokens -> parser -> AST -> codegen -> bytecode -> hexfile -> script.hex
```

| Stage | File | Status |
|---|---|---|
| Lexer | `src/microbasic_compiler/lexer.py` | Implemented -- keywords, operators, comments, decimal/hex/binary numbers, string escapes, labels |
| AST | `src/microbasic_compiler/ast_nodes.py` | Skeleton -- shapes defined, will grow with the parser |
| Parser | `src/microbasic_compiler/parser.py` | Not implemented -- entry point + token-stream helpers only |
| Codegen | `src/microbasic_compiler/codegen.py` | Blocked -- see [`docs/reverse_engineering.md`](docs/reverse_engineering.md) |
| Intel HEX I/O | `src/microbasic_compiler/hexfile.py` | Implemented -- standard format, read + write |
| CLI | `src/microbasic_compiler/cli.py` | Implemented (`mbc build script.mb -o script.hex`) |

## Important: the bytecode format is not publicly documented

Roboteq publishes the MicroBasic *language* grammar in full (see below), but
not the bytecode it compiles to. Only their RoborunPlus PC utility can
currently produce bytecode that runs on real hardware. Getting from AST to
correct bytes for an SDC2160 will require reverse-engineering that format --
see [`docs/reverse_engineering.md`](docs/reverse_engineering.md) for the plan.
Until that's done, `codegen.generate_bytecode` intentionally raises
`NotImplementedError` rather than emitting bytes that look plausible but
won't execute correctly on a drive.

Everything upstream of codegen (lexer, and eventually the parser/AST) is
useful independently of that gap, since the language grammar itself is fully
documented.

## Language reference

Source: *MicroBasic Scripting Language User & Reference Manual*, V2.0, July 8
2019 (Roboteq). Key facts baked into the lexer:

- Comments start with `'` and run to end of line.
- Numbers: decimal (`120`), hex (`0xA1`), binary (`0b101`); range is signed 32-bit.
- Strings: double-quoted, with `\n \t \r \a \b \f \v \' \" \\` and `\xHH` escapes. No string variables -- strings are only usable with `Print`.
- Variables: `Integer` or `Boolean` only (no floats), plus 1-D arrays of either. `Dim` required for `Boolean`/arrays; `Integer` only requires `Dim` under `Option Explicit`.
- Labels: identifier at start of line + `:`, target of `GoTo`/`GoSub`/`Return`.
- Keywords: `#define And AndWhile As Boolean Continue Dim Do Else ElseIf End Evaluate Exit Explicit False For GoSub GoTo If Integer Loop Mod Next Not Option Or Print Return Step Terminate Then To ToBool True Until While XOr`
- Operators: arithmetic `+ - * / Mod`, logical `And Or XOr Not True False`, increment/decrement `++ --`, shift `<< >>`, relational `= <> < > <= >=`, assignment `= += -= *= /= <<= >>=`, indexing `[]`.
- Device I/O is exposed through four functions rather than dedicated syntax: `GetConfig`, `SetConfig`, `GetValue`, `SetCommand` (plus per-function timer commands `SetTimerCount`/`SetTimerState`/`GetTimerCount`/`GetTimerState`).

## Getting started

```bash
pip install -e ".[dev]"
pytest
mbc build examples/blink.mb   # currently fails at codegen -- expected, see above
```

## Next steps

1. Build out the parser (`parser.py` has a suggested build order in its docstring).
2. Continue the bytecode reverse-engineering (`docs/reverse_engineering.md`) -- a real corpus of source/hex pairs exists at `X:\ESP\FIDO\PumpController\testdata`, and `tools/hexdiff.py` decodes/diffs them. Several opcodes are already confirmed (string literals, small int literals, `Dim` is a no-op); most of the ISA is still open.
3. Once both land, wire real `generate_bytecode`.
