link ximage procedure main() in := "((data 'quoted data' 123 4.5) (data (!@# (4.5) '(more' 'data)')))" # in := map(in,"'","\"") # uncomment to put back double quotes if desired write("Input: ",image(in)) write("Structure: \n",ximage(S := string2sexp(in))) write("Output: ",image(sexp2string(S))) end procedure sexp2string(S) #: return a string representing the s-expr s := "" every t := !S do { if type(t) == "list" then s ||:= "(" || trim(sexp2string(t)) || ")" else if upto('() \t\r\n',t) then s ||:= "'" || t || "'" else s ||:= t s ||:= " " } return trim(s) end procedure string2sexp(s) #: return a s-expression nested list if s ? ( sexptokenize(T := []), pos(0) ) then return sexpnest(T) else write("Malformed: ",s) end procedure sexpnest(T,L) #: transform s-expr token list to nested list /L := [] while t := get(T) do case t of { "(" : { put(L,[]) sexpnest(T,L[*L]) } ")" : return L default : put(L, numeric(t) | t) } return L end procedure sexptokenize(T) #: return list of tokens parsed from an s-expr string static sym initial sym := &letters++&digits++'~`!@#$%^&*_-+|;:.,<>[]{}' until pos(0) do case &subject[&pos] of { " " : tab(many(' \t\r\n')) # consume whitespace "'"|"\"" : (q := move(1)) & put(T,tab(find(q))) & move(1) # quotes "(" : put(T,move(1)) & sexptokenize(T) # open ")" : put(T,move(1)) &return T # close default : put(T, tab(many(sym))) # other symbols } return T end