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