Programmatic access to REPL like Pkg, Debugger, etc

hi. i’d like to interact with the user of my julia program with the input being more “command line” than “function call” like, but take advantage of the command line editing, history, etc., the REPL offers. so, the user would type things like

modify r1.2

rather than

modify("r1.2")

Pkg and Debugger, at least, seem to offer this sort of interface (i don’t know how general their access is). and, i suspect if i had the user type @modify i could probably solve my problem.

my question is, is there a not-too-difficult, more-or-less-documented way of doing this?

thanks!

I recommend writing a macro, though it is unlikely you will be able to get the . in r1.2 to work well. See

julia> t = :(@modify r1.2)
:(#= REPL[4]:1 =# @modify r1 0.2)

It gets parsed as r1 and a number.

You can use REPLMaker.jl to make a custom REPL. Once you write a macro that should be easy to implement.

1 Like

@pdeffebach thanks. i’ll take a look at REPLMaker.jl.

@pdeffebach again thanks. using ReplMaker.jl and Tokenize.jl, it seems i can implement a nice (albeit geeky, as planned) UI.

using Tokenize

@enum State skipping accumulating
"""
vecize(str::String)

split a string into components, returning them in a vector.  similar
to split, but we obey embedded quotes, returning as singletons in the
vector their strings (without the quotes).

"""
function vecize(str::String)
    # shortcut from
    # https://github.com/JuliaLang/Tokenize.jl/blob/master/test/lexer.jl
    local T = Tokenize.Tokens
    local result = []
    # so, " r1.2 " will be WHITESPACE IDENTIFIER FLOAT WHITESPACE, and
    # " r1.l2 " will be WHITESPACE IDENTIFIER OP(.) IDENTIFIER WHITESPACE
    # so, need to concatentate things between WHITESPACE (and/or
    # ENDMARKER).  so, need to do a small amount of yacc'ing.
    local terminators = [T.WHITESPACE T.ENDMARKER]
    local state::State = skipping
    local accumulated::String = "" # position of first non-white-space in current
    local tokened = collect(Tokenize.tokenize(str))

    for tok in tokened
        # make sure we're all on one line
        @assert T.startpos(tok)[1] == 1 "more than one line: $(str)"
        @assert T.endpos(tok)[1] == 1 "more than one line: $(str)"
        local startpos = T.startpos(tok)[2]
        local endpos = T.endpos(tok)[2]
        # '(' to allow the line to wrap
        # http://julia-programming-language.2336112.n4.nabble.com/Multiple-lines-statement-tp8640p20795.html
        @assert T.kind(tok) != T.ERROR (
            "invalid input position $(T.startpos(tok)[2]): $(T.untokenize(tok))")
        if T.kind(tok) in terminators
            if state == accumulating
                # done with a run
                result = vcat(result, [accumulated])
                state = skipping
            end
        else                    # by modus pons, this must be something we want
            if state == skipping
                # first non-blank
                state = accumulating
                accumulated = ""
            end
            local content = T.untokenize(tok)
            if T.kind(tok) == T.STRING
                # get rid of quotes
                content = content[2:(end-1)]
            end
            accumulated *= content
        end
    end
    result
end

# https://github.com/MasonProtter/ReplMaker.jl
using ReplMaker

function parse_to_expr(s)
    quote $s end
end

initrepl(vecize,
         prompt_text="Expr> ",
         prompt_color = :blue, 
         start_key=')', 
         mode_name="Expr_mode")

cheers.