What I ended up with is all kinds of wrong and was tremendously fun to write. This fits in a project that is bare metal C, but already has a test system written in Lua, so I used Lua as the implementation language.
Conversation
First considered to re-use Lua syntax but couldn't figure that out, so switched to Scheme. The compiler implements a Scheme subset (let*, if, for, and first order functions). A module maps to a C function, tail calls map to goto, and non-tail calls are inlined.
1
1
The main "work" it does is to figure out whether intermediate variables can be compiled to C variables, or need to go into a struct to survive a yield (which is C return).
1
1
The compiler uses A-Normal Form (ANF). Is under 1K lines of Lua.
github.com/zwizwa/uc_tool
1
Still just a toy. Bugs likely. Current "test" is
github.com/zwizwa/uc_tool
1
Anything that crosses an SM_READ macro (the blocking call) is stored in a C struct. The rest goes on the C stack. This is ANF with constants bound to variables as well, to make implementation simpler. C compiler can take care of optimizing those away.
1
The yield/block points are (later) implemented on top of this protothread code. See e.g. SM_WAIT
1
Started adding support for CSP rendezvous (csp.h in uc_tools, written in hard-to-use C macros).
Added support for zero-copy mode. Eventually this needs to be fast (for the app: 137k int/sec on 72MHz CM3). Might need to specialize the scheduler further.
1
BTW doing this in Lua isn't all that bad. Of course refactoring is hard, but doable at this 1kloc complexity level.
However manual let-insertion is annoying. Older Haskell code used a state-continuation monad for that, which is really convenient. Almost magic.
2
For reference: let insertion using delimited continuations okmij.org/ftp/meta-progr
Replying to
Thanks for that link! Fun read (as usual for Oleg's articles).
There's something gross but appealing to "just keep trying outer scopes until the runtime throws an exception".
1

