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

BEGIN {
    _IMAGE_BEGIN = "# IMAGE BEGINS QMGFAMRISGQJ48IWDOWPHOOGW3MLKKGSPXD4DTFIG0"
}

function awkescape(string) {
    gsub(/\\/, "\\\\", string)
    gsub(/"/,  "\\\"", string)
    gsub(/\n/, "\\n", string)
    return string
}

function awkrepr(v) {
    if(v == _nil())
        return "_nil()"
    else if(v == _true())
        return "_true()"
    else if(v == _false())
        return "_false()"
    else if(v+0 == v)
        return v
    else {
        v = awkescape(v)
        return "\"" v "\""
    }
}

function _dump(filename, append, main,   i, t, v, s, line) {
    # This is what code to execute when this image is started. It has
    # to be a glotawk form with no parameters. Usually it is the repl

    # special form, so that when we start we'll do the REPL. But maybe
    # you want some other function to happen at startup.
    if(!main) main = "repl"
    logg_dbg("_dump", "dumping " (append ? ">> " : "> ") filename ". main symbol is " main)
    if(append)
        print _IMAGE_BEGIN >>filename
    else
        print _IMAGE_BEGIN >filename        
    print "BEGIN {" >>filename
    print "    N = " N " # next cell number" >>filename
    print "    N_at_last_gc = " N_at_last_gc >> filename
    for(i in _TYPE) {
        # i is not a number but a string. somewhere in the code we
        # ask if _TYPE[something] == bla, where something may be
        # true, or false, or nil, or a literal number ("# 123");
        # and by asking about _TYPE[something], we make
        # _TYPE[something] exist, and be "". we don't want to dump
        # those here because _TYPE[# 123] is not valid awk syntax.
        if((i+0)!=i) continue
        t = _TYPE[i]
        line = "    _TYPE[" i "] = \"" t "\"; "
        if(t == "'") {
            v = _SYM_NUMBERS[i]
            v = awkescape(v)
            line = line                                 \
                "_SYM_NUMBERS[" i "] = \"" v "\"; "     \
                "_SYM_NAMES[\"" v "\"] = " i "; "
        } else if(t == "s") {
            v = _STRING[i]
            v = awkescape(v)
            line = line "_STRING[" i "] = \"" v "\"; "
        } else if(t == "(") {
            line = line                         \
                "_CAR[" i "] = " awkrepr(_CAR[i]) "; "  \
                "_CDR[" i "] = " awkrepr(_CDR[i]) "; "
        }
        print line >>filename
    }
    print "    _GLOBALS = " _GLOBALS " # global environment " >>filename
    print "    _MACROS = " _MACROS >>filename
    print "    _COMPARED_SYMBOLS = " _COMPARED_SYMBOLS >>filename
    # we are storing here the name of the _MAIN symbol as an awk
    # string, which we may have received in the main parameter. that's
    # why it's a little awkward
    print "    _MAIN = \"" awkescape(main) "\"" >>filename
    print "    main_expression = _cons(_symbol(_MAIN), _nil())" >>filename
    print "    _eval(main_expression)" >>filename
    print "}" >>filename
    close(filename)
    return _true()
}

function _dump_overwrite(filename) {
    return _dump(filename)
}

function _dump_append(filename) {
    logg_dbg("_dump_append", "filename is " filename)
    return _dump(filename, 1)
}

function _dump_append_changing_main(g_filename, g_main,      filename, main) {
    if(_TYPE[g_filename] == "s") {
        if(_TYPE[g_main] == "'") {
            filename = _STRING[g_filename]
            main = _SYM_NUMBERS[g_main]
            logg_dbg("_dump_append_changing_main", "filename is " filename " and the form to evaluate at startup will be " main)
            return _dump(filename, 1, main)
        } else {
            logg_err("_dump_append_changing_main", "the value of the second parameter, main, must be a symbol")
            return _nil()
        }
    } else {
        logg_err("_dump_append_changing_main", "the value of the first parameter, filename, must be a string")
        return _nil()
    }
}

function dotescape(string) {
    gsub(/\\/, "\\\\", string)
    gsub(/"/,  "\\\"", string)
    return string
}

function dotrepr(v) {
    if(v == _nil())
        return _nil()
    else if(v == _true())
        return _true()
    else if(v == _false())
        return _false()
    else if(v+0 == v)
        return v
    else {
        dotescape(v)
        return v
    }
}

function _dump_dot(filename,    i, t, v, s, n, node, edge, edge2) {
    logg_dbg("_dump_dot", "dumping Graphviz to " filename)
    print "digraph g {" >filename
    print "    rankdir=LR" >>filename
    print "    overlap=false" >>filename
    print "    node [style=filled]" >>filename
    for(i in _TYPE) {
        # i is not a number but a string. somewhere in the code we
        # ask if _TYPE[something] == bla, where something may be
        # true, or false, or nil, or a literal number ("# 123");
        # and by asking about _TYPE[something], we make
        # _TYPE[something] exist, and be "". we don't want to dump
        # those here.
        if((i+0)!=i) continue

        t = _TYPE[i]
        print "/*  " i " is a " _TYPE[i] " : " _repr(i) " */" >>filename
        if(t == "(") {
            print "/*  -- " _CAR[i] " is a " _TYPE[_CAR[i]] " : " _repr(_CAR[i]) "  */" >>filename
            t = _TYPE[_CAR[i]]
            node = "    n" i " [shape=record,label=\"{<n> " i "|"
            edge = ""
            edge2 = ""
            if(t == "(") {
                node = node "<car> ·|"
                edge = "    n" i ":car -> n" _CAR[i] ":n;"
            } else if(t == "'") {
                v = _SYM_NUMBERS[_CAR[i]]
                dotescape(v)
                node = node v "|"
                #edge ="    n" i ":car -> n" _CAR[i] " [constraint=false];"
            } else if(t == "s") {
                v = _STRING[_CAR[i]]
                dotescape(v)
                node = node "\\\"" v "\\\"|"
            } else {
                node = node dotrepr(_CAR[i]) "|"
            }
            if(_is_null(_CDR[i])) {
                node = node "<cdr> X}\"];"
            } else {
                node = node "<cdr> ·}\"];"
                edge2 = "    n" i ":cdr -> n" _CDR[i] ":n [color=blue];"
            }
            print node >>filename
            print edge >>filename
            print edge2 >>filename
        } else if(t == "'") {
            print "    n" i " [shape=Mrecord,label=\"{<n> " i "|" _SYM_NUMBERS[i] "}\"];" >>filename
        } else if(t == "s") {
            print "    n" i " [shape=record,label=\"{<n> " i "|" _STRING[i] "}\"];" >>filename
        }
    }
    print "    nGlobals [label=\"_GLOBALS\"];" >>filename
    print "    nGlobals -> n" _GLOBALS ";" >>filename
    print "    nMacros [label=\"_MACROS\"];" >>filename
    print "    nMacros -> n" _MACROS ";" >>filename
    print "    nCompared [label=\"_COMPARED_SYMBOLS\"];" >>filename
    print "    nCompared -> n" _COMPARED_SYMBOLS ";" >>filename
    print "/*sweep colors*/" >>filename
    print "/*mark colors*/" >>filename
    print "}" >>filename
    close(filename)
    logg_dbg("_dump_dot", "done")
    return _true()
}