nexus_parser 1.0.0

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.
@@ -0,0 +1,334 @@
1
+
2
+ class NexusFile::Parser
3
+
4
+ def initialize(lexer, builder)
5
+ @lexer = lexer
6
+ @builder = builder
7
+ end
8
+
9
+ def parse_file
10
+ # nf = @builder.new_nexus_file # create new local NexusFile instance, nf
11
+ blks = []
12
+ @lexer.pop(NexusFile::Tokens::NexusStart)
13
+
14
+ while @lexer.peek(NexusFile::Tokens::BeginBlk)
15
+
16
+ @lexer.pop(NexusFile::Tokens::BeginBlk) # pop it
17
+
18
+ if @lexer.peek(NexusFile::Tokens::AuthorsBlk)
19
+ parse_authors_blk
20
+
21
+ # we parse these below
22
+ elsif @lexer.peek(NexusFile::Tokens::TaxaBlk)
23
+
24
+ @lexer.pop(NexusFile::Tokens::TaxaBlk )
25
+ parse_taxa_blk
26
+
27
+ elsif @lexer.peek(NexusFile::Tokens::ChrsBlk)
28
+ @lexer.pop(NexusFile::Tokens::ChrsBlk)
29
+ parse_characters_blk
30
+
31
+ elsif @lexer.peek(NexusFile::Tokens::NotesBlk)
32
+ @lexer.pop(NexusFile::Tokens::NotesBlk)
33
+ parse_notes_blk
34
+
35
+ # we should parse this
36
+ elsif @lexer.peek(NexusFile::Tokens::SetsBlk)
37
+ @lexer.pop(NexusFile::Tokens::SetsBlk)
38
+
39
+ # we don't parse these
40
+ elsif @lexer.peek(NexusFile::Tokens::TreesBlk)
41
+ @foo = @lexer.pop(NexusFile::Tokens::TreesBlk).value
42
+
43
+ elsif @lexer.peek(NexusFile::Tokens::LabelsBlk)
44
+ @lexer.pop(NexusFile::Tokens::LabelsBlk)
45
+
46
+ elsif @lexer.peek(NexusFile::Tokens::MqCharModelsBlk)
47
+ @lexer.pop(NexusFile::Tokens::MqCharModelsBlk)
48
+
49
+ elsif @lexer.peek(NexusFile::Tokens::AssumptionsBlk)
50
+ @lexer.pop(NexusFile::Tokens::AssumptionsBlk)
51
+
52
+ elsif @lexer.peek(NexusFile::Tokens::CodonsBlk)
53
+ @lexer.pop(NexusFile::Tokens::CodonsBlk)
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ # just removes it for the time being
60
+ def parse_authors_blk
61
+ # thing has non single word key/value pairs, like "AUTHOR NAME", SIGH
62
+ # for now just slurp it all up.
63
+ @lexer.pop(NexusFile::Tokens::AuthorsBlk )
64
+
65
+ #while true
66
+ # if @lexer.peek(NexusFile::Tokens::EndBlk)
67
+ # @lexer.pop(NexusFile::Tokens::EndBlk)
68
+ # break
69
+ # else
70
+
71
+ # while @lexer.peek(NexusFile::Tokens::ValuePair)
72
+ # # IMPORTANT, these are going to a general hash, there may ultimately be overlap of keys used in different blocks, this is ignored at present
73
+ # @builder.add_var(@lexer.pop(NexusFile::Tokens::ValuePair).value)
74
+ # end
75
+
76
+ #@lexer.pop(NexusFile::Tokens::ID) if @lexer.peek(NexusFile::Tokens::ID)
77
+ # end
78
+ #end
79
+ end
80
+
81
+ def parse_taxa_blk
82
+ @lexer.pop(NexusFile::Tokens::Title) if @lexer.peek(NexusFile::Tokens::Title)
83
+
84
+ # need to not ignore to test against
85
+ parse_dimensions if @lexer.peek(NexusFile::Tokens::Dimensions)
86
+
87
+ while true
88
+ if @lexer.peek(NexusFile::Tokens::EndBlk)
89
+ @lexer.pop(NexusFile::Tokens::EndBlk)
90
+ break
91
+ else
92
+
93
+ if @lexer.peek(NexusFile::Tokens::Taxlabels)
94
+ @lexer.pop(NexusFile::Tokens::Taxlabels) if @lexer.peek(NexusFile::Tokens::Taxlabels)
95
+ i = 0
96
+ while @lexer.peek(NexusFile::Tokens::Label)
97
+ @builder.update_taxon(:index => i, :name => @lexer.pop(NexusFile::Tokens::Label).value)
98
+ i += 1
99
+ end
100
+ @lexer.pop(NexusFile::Tokens::SemiColon) if @lexer.peek(NexusFile::Tokens::SemiColon) # close of tax labels, placement of this seems dubious... but tests are working
101
+
102
+ elsif @lexer.peek(NexusFile::Tokens::MesquiteIDs)
103
+
104
+ @lexer.pop(NexusFile::Tokens::MesquiteIDs) # trashing these for now
105
+ elsif @lexer.peek(NexusFile::Tokens::MesquiteBlockID)
106
+ @lexer.pop(NexusFile::Tokens::MesquiteBlockID)
107
+ end
108
+
109
+ end
110
+ end
111
+
112
+
113
+ end
114
+
115
+ def parse_characters_blk
116
+ while true
117
+ if @lexer.peek(NexusFile::Tokens::EndBlk) # we're at the end of the block, exit after geting rid of the semi-colon
118
+ break
119
+ else
120
+ @lexer.pop(NexusFile::Tokens::Title) if @lexer.peek(NexusFile::Tokens::Title) # not used at present
121
+
122
+ parse_dimensions if @lexer.peek(NexusFile::Tokens::Dimensions)
123
+ parse_format if @lexer.peek(NexusFile::Tokens::Format)
124
+
125
+ parse_chr_state_labels if @lexer.peek(NexusFile::Tokens::CharStateLabels)
126
+
127
+ parse_matrix if @lexer.peek(NexusFile::Tokens::Matrix)
128
+
129
+ # handle "\s*OPTIONS MSTAXA = UNCERTAIN;\s\n" within a characters block (sticks in an infinite loop right now)
130
+
131
+ @lexer.pop(NexusFile::Tokens::MesquiteIDs) if @lexer.peek(NexusFile::Tokens::MesquiteIDs) # trashing these for now
132
+ @lexer.pop(NexusFile::Tokens::MesquiteBlockID) if @lexer.peek(NexusFile::Tokens::MesquiteBlockID) # trashing these for now
133
+
134
+ false
135
+ end
136
+ end
137
+ @lexer.pop(NexusFile::Tokens::EndBlk)
138
+ end
139
+
140
+ # prolly pop header then fuse with parse_dimensions
141
+ def parse_format
142
+ @lexer.pop(NexusFile::Tokens::Format)
143
+ while @lexer.peek(NexusFile::Tokens::ValuePair)
144
+ @builder.add_var(@lexer.pop(NexusFile::Tokens::ValuePair).value)
145
+ end
146
+
147
+ check_initialization_of_ntax_nchar
148
+ end
149
+
150
+ def parse_dimensions
151
+ @lexer.pop(NexusFile::Tokens::Dimensions)
152
+ while @lexer.peek(NexusFile::Tokens::ValuePair)
153
+ @builder.add_var(@lexer.pop(NexusFile::Tokens::ValuePair).value)
154
+ end
155
+ # the last value pair with a ; is automagically handled, don't try popping it again
156
+
157
+ check_initialization_of_ntax_nchar
158
+ end
159
+
160
+ def check_initialization_of_ntax_nchar
161
+ # check for character dimensions, if otherwise not set generate them
162
+ if @builder.nexus_file.vars[:nchar] && @builder.nexus_file.characters == []
163
+ (0..(@builder.nexus_file.vars[:nchar].to_i - 1)).each {|i| @builder.stub_chr }
164
+ end
165
+
166
+ # check for taxa dimensions, if otherwise not set generate them
167
+ if @builder.nexus_file.vars[:ntax] && @builder.nexus_file.taxa == []
168
+ (0..(@builder.nexus_file.vars[:ntax].to_i - 1)).each {|i| @builder.stub_taxon }
169
+ end
170
+ end
171
+
172
+ def parse_chr_state_labels
173
+ @lexer.pop(NexusFile::Tokens::CharStateLabels)
174
+
175
+ while true
176
+ if @lexer.peek(NexusFile::Tokens::SemiColon)
177
+ break
178
+ else
179
+ opts = {}
180
+
181
+ name = ""
182
+ index = @lexer.pop(NexusFile::Tokens::Number).value.to_i
183
+ (name = @lexer.pop(NexusFile::Tokens::Label).value) if @lexer.peek(NexusFile::Tokens::Label) # not always given a letter
184
+
185
+ @lexer.pop(NexusFile::Tokens::BckSlash) if @lexer.peek(NexusFile::Tokens::BckSlash)
186
+
187
+ if !@lexer.peek(NexusFile::Tokens::Comma) || !@lexer.peek(NexusFile::Tokens::SemiColon)
188
+ i = 0
189
+
190
+ # three kludge lines, need to figure out the label/number priority, could be issue in list order w/in tokens
191
+ while @lexer.peek(NexusFile::Tokens::Label) || @lexer.peek(NexusFile::Tokens::Number)
192
+ opts.update({i.to_s => @lexer.pop(NexusFile::Tokens::Label).value}) if @lexer.peek(NexusFile::Tokens::Label)
193
+ opts.update({i.to_s => @lexer.pop(NexusFile::Tokens::Number).value.to_s}) if @lexer.peek(NexusFile::Tokens::Number)
194
+
195
+ i += 1
196
+ end
197
+ end
198
+
199
+ @lexer.pop(NexusFile::Tokens::Comma) if @lexer.peek(NexusFile::Tokens::Comma) # we may also have hit semicolon
200
+
201
+ opts.update({:index => (index - 1), :name => name})
202
+
203
+ raise(ParserError, "Error parsing character state labels for (or around) character #{index -1}.") if !opts[:name]
204
+ @builder.update_chr(opts)
205
+ end
206
+
207
+ end
208
+ @lexer.pop(NexusFile::Tokens::SemiColon)
209
+ end
210
+
211
+ def parse_matrix
212
+ @lexer.pop(NexusFile::Tokens::Matrix)
213
+ i = 0
214
+ while true
215
+ if @lexer.peek(NexusFile::Tokens::SemiColon)
216
+ break
217
+ else
218
+ t = @lexer.pop(NexusFile::Tokens::Label).value
219
+
220
+ @builder.update_taxon(:index => i, :name => t) # if it exists its not re-added
221
+
222
+ @builder.code_row(i, @lexer.pop(NexusFile::Tokens::RowVec).value)
223
+
224
+ i += 1
225
+ end
226
+ end
227
+ @lexer.pop(NexusFile::Tokens::SemiColon) # pop the semicolon
228
+ end
229
+
230
+ # this suck(s/ed), it needs work when a better API for Mesquite comes out
231
+ def parse_notes_blk
232
+ # IMPORTANT - we don't parse the (CM <note>), we just strip the "(CM" ... ")" bit for now in NexusFile::Note
233
+
234
+ @vars = {}
235
+ inf = 0
236
+ while true
237
+ inf += 1
238
+ raise "Either you have a gazillion notes or more likely parser is caught in an infinite loop inside parse_notes_block" if inf > 100000
239
+ if @lexer.peek(NexusFile::Tokens::EndBlk)
240
+ @lexer.pop(NexusFile::Tokens::EndBlk)
241
+ @builder.add_note(@vars) # one still left to add
242
+ break
243
+ else
244
+
245
+ if @lexer.peek(NexusFile::Tokens::ValuePair)
246
+ @vars.update(@lexer.pop(NexusFile::Tokens::ValuePair).value)
247
+
248
+ elsif @lexer.peek(NexusFile::Tokens::Label)
249
+ if @vars[:type] # we have the data for this row write it, and start a new one
250
+
251
+ @builder.add_note(@vars)
252
+ @vars = {}
253
+ else
254
+ @vars.update(:type => @lexer.pop(NexusFile::Tokens::Label).value)
255
+ end
256
+ elsif @lexer.peek(NexusFile::Tokens::FileLbl)
257
+ @lexer.pop(NexusFile::Tokens::FileLbl)
258
+ @vars.update(:file => 'file') # we check for whether :file key is present and handle conditionally
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ #@vars = {}
265
+ #while true
266
+
267
+ # break if @lexer.peek(NexusFile::Tokens::EndBlk)
268
+
269
+ # @vars.update(:type => @lexer.pop(NexusFile::Tokens::Label).value)
270
+
271
+ # kludge to get around the funny construct that references file
272
+ # if @lexer.peek(NexusFile::Tokens::FileLbl)
273
+ # @lexer.pop(NexusFile::Tokens::FileLbl)
274
+ # vars.update(:file => 'file') # we check for whether :file key is present and handle conditionally
275
+ # end
276
+
277
+ # while true
278
+
279
+ # meh = @lexer.pop(NexusFile::Tokens::ValuePair)
280
+ # @vars.update(meh.value)
281
+ # break if !@lexer.peek(NexusFile::Tokens::ValuePair)
282
+ # end
283
+ #
284
+ # @builder.add_note(@vars)
285
+ # @vars = {}
286
+ #end
287
+ # @lexer.pop(NexusFile::Tokens::EndBlk)
288
+
289
+
290
+ def parse_trees_blk
291
+ true
292
+ end
293
+
294
+ def parse_labels_blk
295
+
296
+ end
297
+
298
+ def parse_sets_blk
299
+ end
300
+
301
+ def parse_assumptions_blk
302
+ end
303
+
304
+ def parse_codens_blk
305
+ # not likely
306
+ end
307
+
308
+ def parse_mesquitecharmodels_blk
309
+ # nor this
310
+ end
311
+
312
+
313
+ def parse_mesquite_blk
314
+
315
+ end
316
+
317
+
318
+
319
+ # def parse_children(parent)
320
+ # parse a comma-separated list of nodes
321
+ # while true
322
+ # parse_node(parent)
323
+ # if @lexer.peek(NexusFile::Tokens::Comma)
324
+ # @lexer.pop(NexusFile::Tokens::Comma)
325
+ # else
326
+ # break
327
+ # end
328
+ # end
329
+ # end
330
+
331
+ end
332
+
333
+
334
+
@@ -0,0 +1,269 @@
1
+ module NexusFile::Tokens
2
+
3
+ class Token
4
+ # this allows access the the class attribute regexp, without using a class variable
5
+ class << self; attr_reader :regexp; end
6
+ attr_reader :value
7
+ def initialize(str)
8
+ @value = str
9
+ end
10
+ end
11
+
12
+ # in ruby, \A is needed if you want to only match at the beginning of the string, we need this everywhere, as we're
13
+ # moving along popping off
14
+
15
+ class NexusStart < Token
16
+ @regexp = Regexp.new(/\A.*(\#nexus)\s*/i)
17
+ end
18
+
19
+ # at present we strip comments pre-parser initialization, because they can be placed anywhere it gets tricky to parse otherwise, and besides, they are non-standard
20
+ # class NexusComment < Token
21
+ # @regexp = Regexp.new(/\A\s*(\[[^\]]*\])\s*/i)
22
+ # def initialize(str)
23
+ # str = str[1..-2] # strip the []
24
+ # str.strip!
25
+ # @value = str
26
+ # end
27
+ # end
28
+
29
+ class BeginBlk < Token
30
+ @regexp = Regexp.new(/\A\s*(\s*Begin\s*)/i)
31
+ end
32
+
33
+ class EndBlk < Token
34
+ @regexp = Regexp.new(/\A\s*([\s\n]*End[\s\n]*;[\s\n]*)/i)
35
+ end
36
+
37
+ # label
38
+ class AuthorsBlk < Token
39
+ @regexp = Regexp.new(/\A\s*(Authors;.*?END;)\s*/im)
40
+ end
41
+
42
+ # label
43
+ class TaxaBlk < Token
44
+ @regexp = Regexp.new(/\A\s*(\s*Taxa\s*;)\s*/i)
45
+ end
46
+
47
+ # label
48
+ class NotesBlk < Token
49
+ @regexp = Regexp.new(/\A\s*(\s*Notes\s*;)\s*/i)
50
+ end
51
+
52
+ class FileLbl < Token
53
+ @regexp = Regexp.new(/\A\s*(\s*File\s*)\s*/i)
54
+ end
55
+
56
+ # label and content
57
+ class Title < Token
58
+ @regexp = Regexp.new(/\A\s*(title[^\;]*;)\s*/i)
59
+ end
60
+
61
+ class Dimensions < Token
62
+ @regexp = Regexp.new(/\A\s*(DIMENSIONS)\s*/i)
63
+ end
64
+
65
+ class Format < Token
66
+ @regexp = Regexp.new(/\A\s*(format)\s*/i)
67
+ end
68
+
69
+ # label
70
+ class Taxlabels < Token
71
+ @regexp = Regexp.new(/\A\s*(\s*taxlabels\s*)\s*/i)
72
+ end
73
+
74
+ # same as ID
75
+ class Label < Token
76
+ @regexp = Regexp.new('\A\s*((\'+[^\']+\'+)|(\"+[^\"]+\"+)|(\w[^,:(); \t\n]*|_)+)\s*') # matches "foo and stuff", foo, 'stuff or foo', '''foo''', """bar""" BUT NOT ""foo" " # choking on 'Foo_stuff_things'
77
+ def initialize(str)
78
+ str.strip!
79
+ str = str[1..-2] if str[0..0] == "'" # get rid of quote marks
80
+ str = str[1..-2] if str[0..0] == '"'
81
+ str.strip!
82
+ @value = str
83
+ end
84
+ end
85
+
86
+ class ChrsBlk < Token
87
+ @regexp = Regexp.new(/\A\s*(characters\s*;)\s*/i)
88
+ end
89
+
90
+ # note we grab EOL and ; here
91
+ class ValuePair < Token
92
+ @regexp = Regexp.new(/\A\s*([\w\d\_\&]+\s*=\s*((\'[^\']+\')|(\(.*\))|(\"[^\"]+\")|([^\s\n\t;]+)))[\s\n\t;]+/i) # returns key => value hash for tokens like 'foo=bar' or foo = 'b a ar'
93
+ def initialize(str)
94
+ str.strip!
95
+ str = str.split(/=/)
96
+ str[1].strip!
97
+ str[1] = str[1][1..-2] if str[1][0..0] == "'"
98
+ str[1] = str[1][1..-2] if str[1][0..0] == "\""
99
+ @value = {str[0].strip.downcase.to_sym => str[1].strip}
100
+ end
101
+ end
102
+
103
+ class Matrix < Token
104
+ @regexp = Regexp.new(/\A\s*(matrix)\s*/i)
105
+ end
106
+
107
+ class RowVec < Token
108
+ @regexp = Regexp.new(/\A\s*(.+)\s*\n/i)
109
+ def initialize(str)
110
+ # meh! Ruby is simpler to read than Perl?
111
+ # handles both () and {} style multistates
112
+ s = str.split(/\(|\)|\}|\{/).collect{|s| s=~ /[\,|\s]/ ? s.split(/[\,|\s]/) : s}.inject([]){|sum, x| x.class == Array ? sum << x.delete_if {|y| y == "" } : sum + x.strip.split(//)}
113
+ @value = s
114
+ end
115
+ end
116
+
117
+ class CharStateLabels < Token
118
+ @regexp = Regexp.new(/\A\s*(CHARSTATELABELS)\s*/i)
119
+ end
120
+
121
+ class MesquiteIDs < Token
122
+ @regexp = Regexp.new(/\A\s*(IDS[^;]*;)\s*/i)
123
+ end
124
+
125
+ class MesquiteBlockID < Token
126
+ @regexp = Regexp.new(/\A\s*(BLOCKID[^;]*;)\s*/i)
127
+ end
128
+
129
+ # unparsed blocks
130
+
131
+ class TreesBlk < Token
132
+ @regexp = Regexp.new(/\A\s*(trees;.*?END;)\s*/im) # note the multi-line /m
133
+ end
134
+
135
+ class SetsBlk < Token
136
+ @regexp = Regexp.new(/\A\s*(sets;.*?END;)\s*/im)
137
+ end
138
+
139
+ class MqCharModelsBlk < Token
140
+ @regexp = Regexp.new(/\A\s*(MESQUITECHARMODELS;.*?END;)\s*/im)
141
+ end
142
+
143
+ class LabelsBlk < Token
144
+ @regexp = Regexp.new(/\A\s*(LABELS;.*?END;)\s*/im)
145
+ end
146
+
147
+ class AssumptionsBlk < Token
148
+ @regexp = Regexp.new(/\A\s*(ASSUMPTIONS;.*?END;)\s*/im)
149
+ end
150
+
151
+ class CodonsBlk < Token
152
+ @regexp = Regexp.new(/\A\s*(CODONS;.*?END;)\s*/im)
153
+ end
154
+
155
+ class MesquiteBlk < Token
156
+ @regexp = Regexp.new(/\A\s*(Mesquite;.*?END;)\s*/im)
157
+ end
158
+
159
+ class BlkEnd < Token
160
+ @regexp = Regexp.new(/\A[\s\n]*(END;)\s*/i)
161
+ end
162
+
163
+ class LBracket < Token
164
+ @regexp = Regexp.new('\A\s*(\[)\s*')
165
+ end
166
+
167
+ class RBracket < Token
168
+ @regexp = Regexp.new('\A\s*(\])\s*')
169
+ end
170
+
171
+ class LParen < Token
172
+ @regexp = Regexp.new('\A\s*(\()\s*')
173
+ end
174
+
175
+ class RParen < Token
176
+ @regexp = Regexp.new('\A\s*(\))\s*')
177
+ end
178
+
179
+ class Equals < Token
180
+ @regexp = Regexp.new('\A\s*(=)\s*')
181
+ end
182
+
183
+ class BckSlash < Token
184
+ @regexp = Regexp.new('\A\s*(\/)\s*')
185
+ end
186
+
187
+ # labels
188
+ class ID < Token
189
+ @regexp = Regexp.new('\A\s*((\'[^\']+\')|(\w[^,:(); \t\n]*|_)+)\s*')
190
+ def initialize(str)
191
+ str.strip!
192
+ str = str[1..-2] if str[0..0] == "'" # get rid of quote marks
193
+ @value = str
194
+ end
195
+ end
196
+
197
+ class Colon < Token
198
+ @regexp = Regexp.new('\A\s*(:)\s*')
199
+ end
200
+
201
+ class SemiColon < Token
202
+ @regexp = Regexp.new('\A\s*(;)\s*')
203
+ end
204
+
205
+ class Comma < Token
206
+ @regexp = Regexp.new('\A\s*(\,)\s*')
207
+ end
208
+
209
+ class Number < Token
210
+ @regexp = Regexp.new('\A\s*(-?\d+(\.\d+)?([eE][+-]?\d+)?)\s*')
211
+ def initialize(str)
212
+ # a little oddness here, in some case we don't want to include the .0
213
+ # see issues with numbers as labels
214
+ if str =~ /\./
215
+ @value = str.to_f
216
+ else
217
+ @value = str.to_i
218
+ end
219
+
220
+ end
221
+ end
222
+
223
+ # NexusFile::Tokens::NexusComment
224
+
225
+ # this list also defines priority, i.e. if tokens have overlap (which they shouldn't!!) then the earlier indexed token will match first
226
+ def self.nexus_file_token_list
227
+ [ NexusFile::Tokens::NexusStart,
228
+ NexusFile::Tokens::BeginBlk,
229
+ NexusFile::Tokens::EndBlk,
230
+ NexusFile::Tokens::AuthorsBlk,
231
+ NexusFile::Tokens::SetsBlk,
232
+ NexusFile::Tokens::MqCharModelsBlk,
233
+ NexusFile::Tokens::AssumptionsBlk,
234
+ NexusFile::Tokens::CodonsBlk,
235
+ NexusFile::Tokens::MesquiteBlk,
236
+ NexusFile::Tokens::TreesBlk,
237
+ NexusFile::Tokens::LabelsBlk,
238
+ NexusFile::Tokens::TaxaBlk,
239
+ NexusFile::Tokens::NotesBlk,
240
+ NexusFile::Tokens::Title,
241
+ NexusFile::Tokens::Taxlabels,
242
+ NexusFile::Tokens::Dimensions,
243
+ NexusFile::Tokens::FileLbl,
244
+ NexusFile::Tokens::Format,
245
+ NexusFile::Tokens::Equals,
246
+ NexusFile::Tokens::ValuePair, # this has bad overlap with Label and likely IDs (need to kill the latter, its a lesser Label)
247
+ NexusFile::Tokens::CharStateLabels,
248
+ NexusFile::Tokens::ChrsBlk,
249
+ NexusFile::Tokens::Number,
250
+ NexusFile::Tokens::Matrix,
251
+ NexusFile::Tokens::SemiColon,
252
+ NexusFile::Tokens::MesquiteIDs,
253
+ NexusFile::Tokens::MesquiteBlockID,
254
+ NexusFile::Tokens::BlkEnd,
255
+ NexusFile::Tokens::Colon,
256
+ NexusFile::Tokens::BckSlash,
257
+ NexusFile::Tokens::Comma,
258
+ NexusFile::Tokens::LParen,
259
+ NexusFile::Tokens::RParen,
260
+ NexusFile::Tokens::LBracket,
261
+ NexusFile::Tokens::RBracket,
262
+ NexusFile::Tokens::Label, # must be before RowVec
263
+ NexusFile::Tokens::RowVec,
264
+ NexusFile::Tokens::ID # need to trash this
265
+ ]
266
+ end
267
+
268
+ end
269
+