While going through some exercises that used libedit, to quickly get something working, I came up with the following:
readline.sats:
#include "share/atspre_staload.hats"
%{#
#include <editline/readline.h>
#include <editline/history.h>
%}
fun readline: string -> Strptr0 = "mac#"
fun add_history: (!Strptr1) -> void = "mac#"
fun free_readline: Strptr0 -> void = "mac#free"
As used:
fun input_loop(): void =
let
val line = readline("input: ")
in
if strptr_isnot_null(line) then (
add_history(line);
println!("you entered: ", line);
free_readline(line);
input_loop()
) else free_readline(line)
end
I have some problems with this solution:
after determining that the strptr is NULL, I still have to pass it to free() as the linear value must be terminated. Is there a nicer way to handle this? Would it be possible to replace the strptr_isnot_null
with a function that terminates NULL but not any other strptr
?
Strptr1
is 99% what I want in this case. That's a very convenient type. However, strptr_free()
is a valid terminator for Strptr1
-- but not for these values, because the editline library is doing its own allocation, outside of the GC. What's a nice way to handle this? Maybe, create a new type and reimplement everything needed for it. Maybe, create a new type but mostly use a zero-cost castfn
to strptr
... which the caller might still mess up with, and try to use with strptr_free()
.
Regarding 1:
I don't see an easy way to avoid calling 'free' on a null string. ATS2 is very explicit as of now. My hope is that more meta-programming support can be built into ATS3.
Maybe you could try to define a macro:
macdef if_line(x, _then) = if strptr_isnot_null(,(x)) then ,(_then) else freeline(,(x))
Regarding 2:
I would introduce an abstract type:
absvtype readline(l:addr) = ptr
fun readline(prompt: string): [l:addr] readline(l)