@euphrasiologist/lwphylo 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,74 +1,64 @@
1
1
  /**
2
- * Parse a Newick tree string into a doubly-linked
3
- * list of JS Objects. Assigns node labels, branch
4
- * lengths and node IDs (numbering terminal before
5
- * internal nodes).
2
+ * Parse a Newick tree string into a doubly-linked list of JS Objects.
3
+ * Assigns labels, branch lengths, and node IDs (tips before internals if input emits them that way).
4
+ *
5
+ * Notes / limitations:
6
+ * - Quoted labels and NHX annotations are not fully supported.
7
+ * - Branch lengths in scientific notation are supported (parseFloat).
6
8
  */
7
9
 
8
- export default function (text) {
9
- // remove whitespace
10
- text = text.replace(/ \t/g, '');
10
+ export default function readTree(text) {
11
+ // Remove all whitespace (space, tabs, newlines)
12
+ text = String(text).replace(/\s+/g, '');
11
13
 
12
- var tokens = text.split(/(;|\(|\)|,)/),
13
- root = { 'parent': null, 'children': [] },
14
- curnode = root,
15
- nodeId = 0;
14
+ const tokens = text.split(/(;|\(|\)|,)/);
15
+ const root = { parent: null, children: [] };
16
+ let curnode = root;
17
+ let nodeId = 0;
16
18
 
17
- for (const token of tokens) {
18
- if (token == "" || token == ';') {
19
- continue
20
- }
21
- if (token == '(') {
22
- // add a child to current node
23
- let child = {
24
- 'parent': curnode,
25
- 'children': []
26
- };
27
- curnode.children.push(child);
28
- curnode = child; // climb up
29
- }
30
- else if (token == ',') {
31
- // climb down, add another child to parent
32
- curnode = curnode.parent;
33
- let child = {
34
- 'parent': curnode,
35
- 'children': []
36
- }
37
- curnode.children.push(child);
38
- curnode = child; // climb up
39
- }
40
- else if (token == ')') {
41
- // climb down twice
42
- curnode = curnode.parent;
43
- if (curnode === null) {
44
- break;
45
- }
46
- }
47
- else {
48
- var nodeinfo = token.split(':');
19
+ for (const token of tokens) {
20
+ if (!token || token === ';') continue;
49
21
 
50
- if (nodeinfo.length == 1) {
51
- if (token.startsWith(':')) {
52
- curnode.label = "";
53
- curnode.branchLength = parseFloat(nodeinfo[0]);
54
- } else {
55
- curnode.label = nodeinfo[0];
56
- curnode.branchLength = null;
57
- }
58
- }
59
- else if (nodeinfo.length == 2) {
60
- curnode.label = nodeinfo[0];
61
- curnode.branchLength = parseFloat(nodeinfo[1]);
62
- }
63
- else {
64
- // TODO: handle edge cases with >1 ":"
65
- console.warn(token, "I don't know what to do with two colons!");
66
- }
67
- curnode.id = nodeId++; // assign then increment
22
+ if (token === '(') {
23
+ const child = { parent: curnode, children: [] };
24
+ curnode.children.push(child);
25
+ curnode = child; // descend
26
+ } else if (token === ',') {
27
+ // back to parent, then create sibling
28
+ curnode = curnode.parent;
29
+ const child = { parent: curnode, children: [] };
30
+ curnode.children.push(child);
31
+ curnode = child;
32
+ } else if (token === ')') {
33
+ // ascend one level
34
+ curnode = curnode.parent;
35
+ if (curnode === null) break;
36
+ } else {
37
+ // label/branch-length chunk (e.g., "A:0.01" or "A")
38
+ const nodeinfo = token.split(':');
39
+ if (nodeinfo.length === 1) {
40
+ if (token.startsWith(':')) {
41
+ curnode.label = '';
42
+ curnode.branchLength = parseFloat(nodeinfo[0]);
43
+ } else {
44
+ curnode.label = nodeinfo[0];
45
+ curnode.branchLength = null;
68
46
  }
47
+ } else if (nodeinfo.length === 2) {
48
+ curnode.label = nodeinfo[0];
49
+ curnode.branchLength = parseFloat(nodeinfo[1]);
50
+ } else {
51
+ console.warn(token, "Unhandled token with multiple ':' characters");
52
+ curnode.label = nodeinfo[0] || '';
53
+ curnode.branchLength = parseFloat(nodeinfo[nodeinfo.length - 1]);
54
+ }
55
+ curnode.id = nodeId++; // assign then increment
69
56
  }
57
+ }
70
58
 
71
- curnode.id = nodeId;
59
+ // Ensure root has an id if not assigned during parsing
60
+ if (root.id == null) root.id = nodeId;
72
61
 
73
- return (root);
62
+ return root;
74
63
  }
64
+