A Lisp implemented in AWK
# SPDX-License-Identifier: BSD-2-Clause

function _print_with_no_ors(x,      sors) {
    sors=ORS
    ORS=""
    print x
    ORS=sors
    fflush()
}

function _prompt() {
    if(!FILENAME || (FILENAME == "-")) _print_with_no_ors(PROMPT)
}

function _print_eval_all(to_eval,     i, l) {
    l = length(to_eval)
    for(i=1; i<=l; i++) {
        _print(_eval(to_eval[i]))
    }
}

function _just_eval_all(to_eval,      i, l) {
    l = length(to_eval)
    for(i=1; i<=l; i++) {
        _eval(to_eval[i])
    }
}

function _repl(     tokens, where, inside_string, to_eval) {
    delete tokens
    delete where
    delete inside_string
    delete to_eval
    if(PROMPT == 0) PROMPT = "* "
    _forget_parse_state(tokens, where, inside_string)
    # maybe we are running from a BEGIN and so FILENAME is not set
    # yet. instead we have to find whether any filename was specified.
    if(length(ARGV) < 2) _prompt()
    while((getline) > 0) {
        # the reason to keep parser state outside tokenize and read is
        # so that we can have forms that span lines, rather than
        # requiring each line of input to be exactly one complete
        # form.
        tokenize($0, tokens, where, inside_string)
        read_forms_into(to_eval, tokens, where, inside_string)

        if(!FILENAME || (FILENAME == "-")) _print_eval_all(to_eval)
        else                               _just_eval_all(to_eval)
        delete to_eval

        if(length(tokens) == 0 && length(to_eval) == 0) {
            # we have come to the end of all complete expressions in the
            # input so far. this is important so that we don't GC data
            # that we have read but not yet eval'd.
            _maybe_gc()

            # and also, all previous input having completed, this is
            # when we should print the next prompt.
            _prompt()
        }
    }
    # after all input has ended -
    _incomplete_parse_at_end(tokens, where, inside_string)
}