Algorithm for parsing input data into a tree

Given an input array of values ​​as a string:

(p (b "sometext")(p (table (columns (column "One") (column "Two") (column "Three"))  
(body (row (cell "111.1") (cell "111.2") (cell "111.3") nil nil)  
(row (cell "222.1") (cell "222.2") (cell "222.3") nil nil)  
(row (cell "333.1") (cell "333.2") (cell "333.3") nil nil)))))

That’s all. It needs to be parsed into XML or HTML using javascript or php. I can’t find an algorithm that will help me build a tree from the list and work out this tree in the future. The given line for parsing is a fragment, there is actually more data, but their structure in general is as follows.
Tell me with the algorithm or give a link to the implementation, what I found – I can’t stick to my task. It is advisable not to use complex treeview components, but to get by with arrays or lists.


Answer 1, authority 100%

Just a couple of seconds of googling and the websiteis located. There is a list2xml converter, although it is in python.
Finished with a little file and you’re done!

upd:
And here is the php code. I know PHP very poorly, therefore, the code will need to be finished to the desired state. Based on the provided piece of Lisp code, it generates valid xml.

<?php
class Lisp2XML_FST {
    public $_prev;
    public $_tags;
    public function __construct($root="root", $empty="empty", $readfrom='') {
        $this->emptytag = $empty;
        $this->roottag = $root;
        $this->readfrom = $readfrom;
        $this->_tags = array();
        $this->_prev = '';
    }
    public function is_startparens() {
        $this->is_tag();
        array_push($this->_tags, "");
    }
    public function is_tag() {
        $l = count($this->_tags);
        if ($this->_tags[$l -1] != "") {
            $prevtag = $this->_tags[$l -1];
        } else {
            $prevtag = $this->emptytag;
        }
        $this->_tags[$l - 1] = $prevtag;
        $this->write("<".$prevtag.">");
    }
    public function is_endparens() {
        $tag = array_pop($this->_tags);
        $this->write("</".$tag.">\n");
    }
    public function write($something) {
        echo($something); #  -    
    }
    public function comment($char) {
        if (char == "\n") {
            return $this->_prev;
        } else {
            return "comment";
        }
    }
    public function starttag($char) {
        $this->_prev = "starttag";
        if (($char == " ") or ($char == "\n") or ($char == "\t")) {
            return "starttag";
        } else if ($char == "(") {
            array_push($this->_tags, "");
            return "intag";
        } else if ($char == "%") {
            return "comment";
        } else {
            return "error";
        }
    }
    public function intag($char) {
        $this->_prev = "intag";
        if ($char == "(") {
            $this->is_startparens();
            return "intag";
        } elseif (($char == " ") or ($char == "\n") or ($char == "\t")) {
            $l = count($this->_tags);
            if ($this->_tags[$l-1] == "") {
                echo("tag\n");
                return "intag";
            } else {
                $this->is_tag();
                return "indata";
            }
        } else if ($char == "%") {
            return "comment";
        } else if ($char == ")") {
            $this->is_endparens();
            return "indata";
        } else {
            $l = count($this->_tags);
            $this->_tags[$l-1] .= $char;
            return "intag";
        }
    }
    public function indata($char) {
        $this->_prev = "indata";
        $default = "indata";
        if ($char == "(") {
            array_push($this->_tags, "");
            return "intag";
        } else if ($char == ")") {
            $this->is_endparens();
            return $default;
        } else if (($char == " ") or ($char == "\n") or ($char == "\t")) {
            $this->write($char);
            return $default;
        } else if ($char == "%") {
            return "comment";
        } else {
            $this->write($char);
            return $default;
        }
    }
    public function error($char) {
        return "";
    }
};
function convert($root="root", $empty="empty", $readfrom="") {
    $fst = new Lisp2XML_FST($root, $empty, $readfrom);
    $fst->write("<".$fst->roottag.">");
    for ($i = 0; $i < strlen($readfrom); $i++) {
        $char = $readfrom[$i];
        if (count($fst->_tags) == 0) {
            $next = "starttag";
            $fst->write("\n");
        }
        $prev = $next;
        $next = $fst->$next($char);
        if ($next == "") {
            if (count($fst->_tags) != 0) {
                $fst->is_endparens();
            }
            break;
        }
    }
    if (count($fst->_tags)) {
        $fst->is_endparens();
    }
    $fst->write("</".$fst->roottag.">\n");
}
?>
<?php
# 
$s = '(p (b "sometext")(p (table (columns (column "One") (column "Two") (column "Three"))'.
     '(body (row (cell "111.1") (cell "111.2") (cell "111.3") nil nil)'.
     '(row (cell "222.1") (cell "222.2") (cell "222.3") nil nil)'.
     '(row (cell "333.1") (cell "333.2") (cell "333.3") nil nil)))))';
convert('root', 'empty', $s);
?>