S-expressions

func sexpr(txt) {
    txt.trim!

    if (txt.match(/^\((.*)\)$/s)) {|m|
        txt = m[0]
    }
    else {
        die "Invalid: <<#{txt}>>"
    }

    var w
    var ret = []

    while (!txt.is_empty) {
        given (txt.first) {
            when('(') {
                (w, txt) = txt.extract_bracketed('()');
                w = sexpr(w)
            }
            when ('"') {
                (w, txt) = txt.extract_delimited('"')
                w.sub!(/^"(.*)"/, {|s1| s1 })
            }
            else {
                txt.sub!(/^(\S+)/, {|s1| w = s1; '' })
            }
        }
        ret << w
        txt.trim_beg!
    }
    return ret
}

func sexpr2txt(String e) {
    e ~~ /[\s"\(\)]/ ? do { e.gsub!('"', '\\"'); %Q("#{e}") } : e
}

func sexpr2txt(expr) {
    '(' + expr.map {|e| sexpr2txt(e) }.join(' ') + ')'
}

var s = sexpr(%q{

((data "quoted data" 123 4.5)
 (data (!@# (4.5) "(more" "data)")))

})

say s               # dump structure
say sexpr2txt(s)    # convert back

Output:

[["data", "quoted data", "123", "4.5"], ["data", ["!@#", ["4.5"], "(more", "data)"]]]
((data "quoted data" 123 4.5) (data (!@# (4.5) "(more" "data)")))

Last updated