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

function awkescape(string) {
    gsub(/\\/, "\\\\", string)
    gsub(/"/,  "\\\"", 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 {
        awkescape(v)
        return "\"" v "\""
    }
}

function _dump(filename,   i, t, v, s, line) {
    logg_dbg("_dump", "dumping to " 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]
            awkescape(v)
            line = line                                 \
                "_SYM_NUMBERS[" i "] = \"" v "\"; "     \
                "_SYM_NAMES[\"" v "\"] = " i "; "
        } else if(t == "s") {
            v = _STRING[i]
            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
    print "}" >>filename
    close(filename)
    return _true()
}


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()
}