diff --git a/io/newick/newick_lexer.go b/io/newick/newick_lexer.go index 9642431..0f001e7 100644 --- a/io/newick/newick_lexer.go +++ b/io/newick/newick_lexer.go @@ -55,6 +55,8 @@ func (s *Scanner) Scan() (tok Token, lit string) { return OPENBRACK, string(ch) case ']': return CLOSEBRACK, string(ch) + case '\'': + return LABEL, string(ch) case ',': return NEWSIBLING, string(ch) case ';': diff --git a/io/newick/newick_nodestack.go b/io/newick/newick_nodestack.go index cabae70..764195f 100644 --- a/io/newick/newick_nodestack.go +++ b/io/newick/newick_nodestack.go @@ -27,7 +27,8 @@ func (ns *NodeStack) Push(n *tree.Node, e *tree.Edge) { ns.elt = append(ns.elt, nodeStackElt{n, e}) } -/** +/* +* Pops and returns the head of the Stack. The calling function is responsible for freeing the elt: free(elt). @@ -36,7 +37,7 @@ Returns an error if the stack is empty func (ns *NodeStack) Pop() (n *tree.Node, e *tree.Edge, err error) { var last nodeStackElt if len(ns.elt) == 0 { - err = errors.New("Cannot Pop an empty stack") + err = errors.New("cannot pop an empty stack") return } last, ns.elt = ns.elt[len(ns.elt)-1], ns.elt[:len(ns.elt)-1] @@ -46,12 +47,13 @@ func (ns *NodeStack) Pop() (n *tree.Node, e *tree.Edge, err error) { return } -/** +/* +* Returns the head of the Stack, and an error if the stack is empty */ func (ns *NodeStack) Head() (n *tree.Node, e *tree.Edge, err error) { if len(ns.elt) == 0 { - err = errors.New("An empty stack has no head") + err = errors.New("an empty stack has no head") return } head := ns.elt[len(ns.elt)-1] diff --git a/io/newick/newick_parser.go b/io/newick/newick_parser.go index c5b14be..5c85150 100644 --- a/io/newick/newick_parser.go +++ b/io/newick/newick_parser.go @@ -60,7 +60,7 @@ func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) { func (p *Parser) Parse() (newtree *tree.Tree, err error) { // May have information inside [] before the tree tok, lit := p.scanIgnoreWhitespace() - if tok == OPENBRACK { + if tok == OPENBRACK || tok == LABEL { if _, err = p.consumeComment(tok, lit); err != nil { return } @@ -83,7 +83,7 @@ func (p *Parser) Parse() (newtree *tree.Tree, err error) { return } if level != 0 { - err = fmt.Errorf("Newick Error : Mismatched parenthesis after parsing") + err = fmt.Errorf("newick error : mismatched parenthesis after parsing") return } tok, lit = p.scanIgnoreWhitespace() @@ -125,7 +125,7 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) case OPENPAR: if node == nil { if *level > 0 { - err = errors.New("Nil node at depth > 0") + err = errors.New("nil node at depth > 0") return } node = t.NewNode() @@ -135,7 +135,7 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) t.SetRoot(node) } else { if *level == 0 { - err = errors.New("Newick Error: An open parenthesis while the stack is empty... Forgot a ';' at the end of previous tree?") + err = errors.New("newick Error: An open parenthesis while the stack is empty... Forgot a ';' at the end of previous tree?") return } newNode = t.NewNode() @@ -153,11 +153,11 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) prevTok = tok (*level)-- if _, _, err = nodeStack.Pop(); err != nil { - err = errors.New("Newick Error: Closing parenthesis while the stack is already empty") + err = errors.New("newick Error: Closing parenthesis while the stack is already empty") return } node, edge, _ = nodeStack.Head() - case OPENBRACK: + case OPENBRACK, LABEL: var comment string //if prevTok == OPENPAR || prevTok == NEWSIBLING || prevTok == -1 { if comment, err = p.consumeComment(tok, lit); err != nil { @@ -172,17 +172,17 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) // Else we add comment to node node.AddComment(comment) } else { - err = errors.New("Newick Error: Comment should not be located here: " + lit) + err = errors.New("newick error: comment should not be located here: " + lit) return } prevTok = CLOSEBRACK case CLOSEBRACK: // Error here should not have - err = errors.New("Newick Error: Mismatched ] here...") + err = errors.New("newick error: mismatched ] here") return case STARTLEN: if tok, lit = p.scanIgnoreWhitespace(); tok != NUMERIC { - err = errors.New("Newick Error: No numeric value after :") + err = errors.New("newick error: no numeric value after ':'") return } // We skip length if the length is assigned to the root node @@ -275,7 +275,7 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) case EOT: p.unscan() if (*level) != 0 { - err = errors.New("Newick Error: Mismatched parenthesis at ;") + err = errors.New("newick Error: Mismatched parenthesis at ;") return } prevTok = tok @@ -291,11 +291,11 @@ func (p *Parser) parseIter(t *tree.Tree, level *int) (prevTok Token, err error) // At the end returns the matching ] token and lit. // If the given token is not a [, then returns an error func (p *Parser) consumeComment(curtoken Token, curlit string) (comment string, err error) { - if curtoken == OPENBRACK { + if curtoken == OPENBRACK || curtoken == LABEL { commenttoken, commentlit := p.scanIgnoreWhitespace() - for commenttoken != CLOSEBRACK { + for (curtoken == LABEL && commenttoken != LABEL) || (curtoken == OPENBRACK && commenttoken != CLOSEBRACK) { if commenttoken == EOF || commenttoken == ILLEGAL { - err = fmt.Errorf("Unmatched bracket") + err = fmt.Errorf("unmatched bracket") return } else { comment += commentlit @@ -303,7 +303,7 @@ func (p *Parser) consumeComment(curtoken Token, curlit string) (comment string, commenttoken, commentlit = p.scanIgnoreWhitespace() } } else { - err = fmt.Errorf("A comment must start with [") + err = fmt.Errorf("a comment must start with [") } return } diff --git a/io/newick/newick_token.go b/io/newick/newick_token.go index a070c21..4cc1ffe 100644 --- a/io/newick/newick_token.go +++ b/io/newick/newick_token.go @@ -15,6 +15,7 @@ const ( STARTLEN // : OPENBRACK // [ : For comment CLOSEBRACK // ] : For comment + LABEL // ' : For comment associated to nodes/edges NEWSIBLING // , EOT // ; ) @@ -26,5 +27,6 @@ func isWhitespace(ch rune) bool { func isIdent(ch rune) bool { return ch != '[' && ch != ']' && ch != '(' && ch != ')' && - ch != ',' && ch != ':' && ch != ';' + ch != ',' && ch != ':' && + ch != ';' && ch != '\'' }