Hi All, I’d like to report on building and using command-line interpreter (CLI) that talks to Elm code. The bottom line is that is that it is quite easy to do using Platform.Worker
. One writes a headless Elm
program that communicates via ports with a 17-line Javascript program cli.js
. The javascript program uses the Node repl
module to provide a convenient user interface. To run the CLI, just say node cli.js
. The code is on GitHub.
(This post expands on an earlier article on the subject Platform.Worker
.)
A Skeleton App
What I will describe here is a skeleton CLI that I derived from something I am using to do real work on another project. It has a simple structure that can be easily bent to other ends. Below is the help screen, which says pretty much all there is to say about how to use the program.
And here is a short session:
> add 2 3 -- the result is stored register M
5
> mul 4 -- only one operand given, so we use the contents of M for the other
20
> pow 1.1 10 -- exponential growth -- beware!
2.5937
> sto a -- store the contents of register M in register A
M > A
> r a -- read the contents of A
A: 2.5937
> log 10 23 -- compute the base-10 logarithm of 23
1.3617
> rcl a -- recall the contents of A, store in M
A > M
> r m. -- yes, it is there
M: 2.5937
ETC.
RPN Version (Note added)
There is now a stack-based version of the calculator. This screenshot explains it:
The Github Repo now has three branches: master, original, and stack, so you can look at the code that interests you. Generally speaking, master = stack.
Code
The organization of the code is pretty well described in the README so I won’t repeat it here. Better just to look at it if you are interested. Here is a birds-eye-view:
-------------------------------------------------------------------------------
File blank comment code
-------------------------------------------------------------------------------
src/Command.elm 99 10 212
src/Main.elm 27 3 71
src/Model.elm 8 0 24
src/ArgList.elm 14 1 19
src/cli.js 6 3 17
-------------------------------------------------------------------------------
SUM: 154 17 343
-------------------------------------------------------------------------------
To customize things, you will need to do two things. First is to
adapt the dispatcher function in Main.elm
:
executeCommand : Model -> String -> ArgList -> String -> ( Model, Cmd Msg )
executeCommand model cmd args input =
case cmd of
"add" ->
Command.op model (+) args
"mul" ->
Command.op model (*) args
...
"exp" ->
Command.f1 model (\x -> e ^ x) args
"ln" ->
Command.f1 model (logBase e) args
"pow" ->
Command.f2 model (\a b -> a ^ b) args
...
The second is to implement code in Command.elm
that carries out your commands. This is the biggest file in project and is mostly specific to the particular functions of the program.
Addendum
The skeleton app described above is a distillation of work on another project. The CLI can read source text in some mythical markup language like this:
$ run cli
> .load source/t1 -- load source into register M
> d -- display it
Now parse it and display a representation of the parse tree:
A node in the tree takes up a single line, with the symbol • standing for newlines. The depth of a node in the parse tree is indicated both by the displayed integer and by indentation,