-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathallegrord.cpp
817 lines (762 loc) · 28.4 KB
/
allegrord.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
#include "assert.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "trace.h"
#include <string>
#include <fstream>
#include <algorithm>
#include "strparse.h"
#include "allegro.h"
#include "algrd_internal.h"
using namespace std;
#define streql(s1, s2) (strcmp(s1, s2) == 0)
#define field_max 80
class Alg_reader {
public:
istream *file;
string input_line;
int line_no;
String_parse line_parser;
bool line_parser_flag;
string field;
bool error_flag;
Alg_seq_ptr seq;
double tsnum;
double tsden;
double offset;
bool offset_found;
Alg_reader(istream *a_file, Alg_seq_ptr new_seq);
void readline();
Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes,
double time);
bool parse();
int parse_chan(string &field);
int64 parse_int(string &field);
int find_real_in(string &field, int n);
double parse_real(string &field);
void parse_error(string &field, int offset, const char *message);
double parse_dur(string &field, double base);
double parse_after_dur(double dur, string &field, int n, double base);
double parse_loud(string &field);
int parse_key(string &field);
double parse_pitch(string &field);
int parse_after_key(int key, string &field, int n);
int find_int_in(string &field, int n);
bool parse_attribute(string &field, Alg_parameter_ptr parm);
bool parse_val(Alg_parameter_ptr param, string &s, int i);
bool check_type(char type_char, Alg_parameter_ptr param);
};
double Alg_reader::parse_pitch(string &field)
{
if (isdigit(field[1])) {
int last = find_real_in(field, 1);
string real_string = field.substr(1, last - 1);
return atof(real_string.c_str());
} else {
return (double) parse_key(field);
}
}
// it is the responsibility of the caller to delete
// the seq
Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq)
{
file = a_file; // save the file
line_parser_flag = false;
line_no = 0;
tsnum = 4; // default time signature
tsden = 4;
seq = new_seq;
offset = 0.0;
offset_found = false;
}
Alg_error alg_read(istream &file, Alg_seq_ptr new_seq, double *offset_ptr)
// read a sequence from allegro file
{
assert(new_seq);
Alg_reader alg_reader(&file, new_seq);
bool err = alg_reader.parse();
if (!err && offset_ptr) {
*offset_ptr = alg_reader.offset;
}
return (err ? alg_error_syntax : alg_no_error);
}
void Alg_reader::readline()
{
// a word about memory management: this Alg_reader has a
// member variable input_line that holds a line of input.
// It is reused for each line. input_line is parsed by
// line_parser, which holds a reference to input_line
line_parser_flag = false;
if (getline(*file, input_line)) {
line_parser.init(&input_line);
line_parser_flag = true;
error_flag = false;
}
}
Alg_parameters_ptr Alg_reader::process_attributes(
Alg_parameters_ptr attributes, double time)
{
// print "process_attributes:", attributes
// bool ts_flag = false;
if (attributes) {
Alg_parameters_ptr a;
bool in_seconds = seq->get_units_are_seconds();
if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) {
double tempo = a->parm.r;
seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time));
}
if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) {
double beat = a->parm.r;
seq->insert_beat(time, beat);
}
// if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) {
// tsnum = a->parm.r;
// ts_flag = true;
// }
// if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) {
// tsden = a->parm.r;
// ts_flag = true;
// }
// if (ts_flag) {
// seq->set_time_sig(seq->get_time_map()->time_to_beat(time),
// tsnum, tsden);
// }
if (in_seconds) seq->convert_to_seconds();
}
return attributes; // in case it was modified
}
bool Alg_reader::parse()
{
int voice = 0;
int key = 60;
double loud = 100.0;
double pitch = 60.0;
double dur = 1.0;
double time = 0.0;
int track_num = 0;
Alg_attribute timesig_num_attr = symbol_table.insert_string("timesig_numr");
Alg_attribute timesig_den_attr = symbol_table.insert_string("timesig_denr");
double timesig_time = -9999;
double timesig_num = 0;
double timesig_den = 0;
seq->convert_to_seconds();
//seq->set_real_dur(0.0); // just in case it's not initialized already
readline();
bool valid = false; // ignore blank lines
while (line_parser_flag) {
bool time_flag = false;
bool next_flag = false;
double next;
bool voice_flag = false;
bool loud_flag = false;
bool dur_flag = false;
bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax
double new_pitch = 0.0;
bool new_key_flag = false; // "K" syntax
int new_key = 0;
Alg_parameters_ptr attributes = NULL;
if (line_parser.peek() == '#') {
// look for #track
line_parser.get_nonspace_quoted(field);
if (streql(field.c_str(), "#track")) {
line_parser.get_nonspace_quoted(field); // number
field.insert(0, " "); // need char at beginning because
// parse_int ignores the first character of the argument
track_num = (int) parse_int(field);
seq->add_track(track_num);
// maybe we have a sequence or track name
line_parser.get_remainder(field);
// if there is a non-space character after #track n then
// use it as sequence or track name. Note that because we
// skip over spaces, a sequence or track name cannot begin
// with leading blanks. Another decision is that the name
// must be at time zero
if (field.length() > 0) {
// insert the field as sequence name or track name
Alg_update_ptr update = new Alg_update;
update->chan = -1;
update->time = 0;
update->set_identifier(-1);
// sequence name is whatever is on track 0
// other tracks have track names
const char *attr =
(track_num == 0 ? "seqnames" : "tracknames");
update->parameter.set_attr(
symbol_table.insert_string(attr));
update->parameter.s = heapify(field.c_str());
seq->add_event(update, track_num);
}
} else if (streql(field.c_str(), "#offset")) {
if (offset_found) {
parse_error(field, 0, "#offset specified twice");
}
offset_found = true;
line_parser.get_nonspace_quoted(field); // number
field.insert(0, " "); // need char at beginning because
// parse_real ignores first character in the argument
offset = parse_real(field);
}
} else {
// we must have a track to insert into
if (seq->tracks() == 0) seq->add_track(0);
line_parser.get_nonspace_quoted(field);
char pk = line_parser.peek();
// attributes are parsed as two adjacent nonspace_quoted tokens
// so we have to conditionally call get_nonspace_quoted() again
if (pk && !isspace(pk)) {
string field2;
line_parser.get_nonspace_quoted(field2);
field.append(field2);
}
while (field[0]) {
char first = toupper(field[0]);
if (strchr("ABCDEFGKLPUSIQHW-", first)) {
valid = true; // it's a note or event
}
if (first == 'V') {
if (voice_flag) {
parse_error(field, 0, "Voice specified twice");
} else {
voice = parse_chan(field);
}
voice_flag = true;
} else if (first == 'T') {
if (time_flag) {
parse_error(field, 0, "Time specified twice");
} else {
time = parse_dur(field, 0.0);
}
time_flag = true;
} else if (first == 'N') {
if (next_flag) {
parse_error(field, 0, "Next specified twice");
} else {
next = parse_dur(field, time);
}
next_flag = true;
} else if (first == 'K') {
if (new_key_flag) {
parse_error(field, 0, "Key specified twice");
} else {
new_key = parse_key(field);
new_key_flag = true;
}
} else if (first == 'L') {
if (loud_flag) {
parse_error(field, 0, "Loudness specified twice");
} else {
loud = parse_loud(field);
}
loud_flag = true;
} else if (first == 'P') {
if (new_pitch_flag) {
parse_error(field, 0, "Pitch specified twice");
} else {
new_pitch = parse_pitch(field);
new_pitch_flag = true;
}
} else if (first == 'U') {
if (dur_flag) {
parse_error(field, 0, "Dur specified twice");
} else {
dur = parse_dur(field, time);
dur_flag = true;
}
} else if (strchr("SIQHW", first)) {
if (dur_flag) {
parse_error(field, 0, "Dur specified twice");
} else {
// prepend 'U' to field, copy EOS too
field.insert((unsigned int) 0, 1, 'U');
dur = parse_dur(field, time);
dur_flag = true;
}
} else if (strchr("ABCDEFG", first)) {
if (new_pitch_flag) {
parse_error(field, 0, "Pitch specified twice");
} else {
// prepend 'P' to field
field.insert((unsigned int) 0, 1, 'P');
new_pitch = parse_pitch(field);
new_pitch_flag = true;
}
} else if (first == '-') {
Alg_parameter parm;
if (parse_attribute(field, &parm)) { // enter attribute-value pair
attributes = new Alg_parameters(attributes);
attributes->parm = parm;
parm.s = NULL; // protect string from deletion by destructor
}
} else if (first == '#') { // ignore after comment character
input_line[0] = 0; // empty the string
line_parser.init(&input_line);
} else {
parse_error(field, 0, "Unknown field");
}
if (error_flag) {
field[0] = 0; // exit the loop
} else {
line_parser.get_nonspace_quoted(field);
pk = line_parser.peek();
// attributes are parsed as two adjacent nonspace_quoted
// tokens so we have to conditionally call
// get_nonspace_quoted() again
if (pk && !isspace(pk)) {
string field2;
line_parser.get_nonspace_quoted(field2);
field.append(field2);
}
}
}
// a case analysis:
// Key < 128 implies pitch unless pitch is explicitly given
// Pitch implies Key unless key is explicitly given,
// Pitch is rounded to nearest integer to determine the Key
// if necessary, so MIDI files will lose the pitch fraction
// A-G is a Pitch specification (therefore it implies Key)
// K60 P60 -- both are specified, use 'em
// K60 P60 C4 -- overconstrained, an error
// K60 C4 -- OK, but K60 is already implied by C4
// K60 -- OK, pitch is 60
// C4 P60 -- over constrained
// P60 -- OK, key is 60
// P60.1 -- OK, key is 60
// C4 -- OK, key is 60, pitch is 60
// <nothing> -- OK, key and pitch from before
// K200 P60 -- ok, pitch is 60
// K200 with neither P60 nor C4 uses
// pitch from before
// figure out what the key/instance is:
if (new_key_flag) { // it was directly specified
key = new_key;
} else if (new_pitch_flag) {
// pitch was specified, but key was not; get key from pitch
key = (int) (new_pitch + 0.5); // round to integer key number
}
if (new_pitch_flag) {
pitch = new_pitch;
} else if (key < 128 && new_key_flag) {
// no explicit pitch, but key < 128, so it implies pitch
pitch = key;
new_pitch_flag = true;
}
// now we've acquired new parameters
// if it is a note, then enter the note
if (valid) {
// change tempo or beat
attributes = process_attributes(attributes, time);
// if there's a duration or pitch, make a note:
if (new_pitch_flag || dur_flag) {
Alg_note_ptr note_ptr = new Alg_note;
note_ptr->chan = voice;
note_ptr->time = time;
note_ptr->dur = dur;
note_ptr->set_identifier(key);
note_ptr->pitch = (float) pitch;
note_ptr->loud = (float) loud;
note_ptr->parameters = attributes;
seq->add_event(note_ptr, track_num); // sort later
if (seq->get_real_dur() < (time + dur)) {
seq->set_real_dur(time + dur);
}
} else {
int update_key = -1;
// key must appear explicitly; otherwise
// update applies to channel
if (new_key_flag) {
update_key = key;
}
if (loud_flag) {
Alg_update_ptr new_upd = new Alg_update;
new_upd->chan = voice;
new_upd->time = time;
new_upd->set_identifier(update_key);
new_upd->parameter.set_attr(symbol_table.insert_string("loudr"));
new_upd->parameter.r = pitch;
seq->add_event(new_upd, track_num);
if (seq->get_real_dur() < time) seq->set_real_dur(time);
}
while (attributes) {
Alg_parameters_ptr p = attributes;
attributes = attributes->next;
// If we get timesig_numr and timesig_denr at the
// same time, we combine them into a time signature.
// If only one is found, it is ignored.
Alg_attribute attr = p->parm.attr;
if (attr == timesig_num_attr) {
if (timesig_time == -9999) {
timesig_time = time;
} else if (timesig_time != time) {
timesig_time = time;
timesig_den = 0;
}
timesig_num = p->parm.r;
} else if (attr == timesig_den_attr) {
if (timesig_time == -9999) {
timesig_time = time;
} else if (timesig_time != time) {
timesig_time = time;
timesig_num = 0;
}
timesig_den = p->parm.r;
}
if (attr == timesig_num_attr ||
attr == timesig_den_attr) {
if (timesig_num > 0 and timesig_den > 0) {
seq->set_time_sig(
seq->get_time_map()->time_to_beat(time),
timesig_num, timesig_den);
timesig_num = 0;
timesig_den = 0;
timesig_time = -9999;
}
} else {
Alg_update_ptr new_upd = new Alg_update;
new_upd->chan = voice;
new_upd->time = time;
new_upd->set_identifier(update_key);
new_upd->parameter = p->parm;
seq->add_event(new_upd, track_num);
p->parm.s = NULL; // so we don't delete the string
}
delete p;
}
}
if (next_flag) {
time = time + next;
} else if (dur_flag || new_pitch_flag) { // a note: incr by dur
time = time + dur;
}
}
}
readline();
}
if (!error_flag) { // why not convert even if there was an error? -RBD
seq->convert_to_seconds(); // make sure format is correct
}
// real_dur is valid, translate to beat_dur
seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur()));
return error_flag;
}
int Alg_reader::parse_chan(string &field)
{
const char *int_string = field.c_str() + 1;
const char *msg = "Integer or - expected";
const char *p = int_string;
char c;
// check that all chars in int_string are digits or '-':
while ((c = *p++)) {
if (!isdigit(c) && c != '-') {
parse_error(field, (int) (p - field.c_str() - 1), msg);
return 0;
}
}
p--; // p now points to end-of-string character
if (p - int_string == 0) {
// bad: string length is zero
parse_error(field, 1, msg);
return 0;
}
if (p - int_string == 1 && int_string[0] == '-') {
// special case: entire string is "-", interpret as -1
return -1;
}
return atoi(int_string);
}
int64 Alg_reader::parse_int(string &field)
{
const char *int_string = field.c_str() + 1;
const char *msg = "Integer expected";
const char *p = int_string;
char c;
// check that all chars in int_string are digits:
while ((c = *p++)) {
if (!isdigit(c)) {
parse_error(field, (int) (p - field.c_str() - 1), msg);
return 0;
}
}
p--; // p now points to end-of-string character
if (p - int_string == 0) {
// bad: string length is zero
parse_error(field, 1, msg);
return 0;
}
return atoll(int_string);
}
int Alg_reader::find_real_in(string &field, int n)
{
// scans from offset n to the end of a real constant
bool decimal = false;
int len = (int) field.length();
if (n < len && field[n] == '-') n += 1; // parse one minus sign
for (int i = n; i < len; i++) {
char c = field[i];
if (!isdigit(c)) {
if (c == '.' && !decimal) {
decimal = true;
} else {
return i;
}
}
}
return len;
}
double Alg_reader::parse_real(string &field)
{
const char *msg = "Real expected";
int last = find_real_in(field, 1);
string real_string = field.substr(1, last - 1);
if (last <= 1 || last < (int) field.length()) {
parse_error(field, 1, msg);
return 0;
}
return atof(real_string.c_str());
}
void Alg_reader::parse_error(string &field, int offset, const char *message)
{
int position = line_parser.pos - (int) field.length() + offset;
error_flag = true;
puts(line_parser.str->c_str());
for (int i = 0; i < position; i++) {
putc(' ', stdout);
}
putc('^', stdout);
printf(" %s\n", message);
}
double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 };
// returns duration in seconds (using timemap)
double Alg_reader::parse_dur(string &field, double base)
{
const char *msg = "Duration expected";
const char *durs = "SIQHW";
const char *p;
int last;
double dur;
if (field.length() < 2) {
// fall through to error message
return -1;
} else if (isdigit(field[1])) {
last = find_real_in(field, 1);
string real_string = field.substr(1, last - 1);
dur = atof(real_string.c_str());
// convert dur from seconds to beats
dur = seq->get_time_map()->time_to_beat(base + dur) -
seq->get_time_map()->time_to_beat(base);
} else if ((p = strchr(durs, toupper(field[1])))) {
dur = duration_lookup[p - durs];
last = 2;
} else {
parse_error(field, 1, msg);
return 0;
}
dur = parse_after_dur(dur, field, last, base);
dur = seq->get_time_map()->beat_to_time(
seq->get_time_map()->time_to_beat(base) + dur) - base;
return dur;
}
double Alg_reader::parse_after_dur(double dur, string &field,
int n, double base)
{
if ((int) field.length() == n) {
return dur;
}
if (toupper(field[n]) == 'T') {
return parse_after_dur(dur * 2/3, field, n + 1, base);
}
if (field[n] == '.') {
return parse_after_dur(dur * 1.5, field, n + 1, base);
}
if (isdigit(field[n])) {
int last = find_real_in(field, n);
string a_string = field.substr(n, last - n);
double f = atof(a_string.c_str());
return parse_after_dur(dur * f, field, last, base);
}
if (field[n] == '+') {
string a_string = field.substr(n + 1);
return dur + parse_dur(
a_string, seq->get_time_map()->beat_to_time(
seq->get_time_map()->time_to_beat(base) + dur));
}
parse_error(field, n, "Unexpected character in duration");
return dur;
}
struct loud_lookup_struct {
const char *str;
int val;
} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100},
{"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60},
{NULL, 0} };
double Alg_reader::parse_loud(string &field)
{
const char *msg = "Loudness expected";
if (isdigit(field[1])) {
return parse_int(field);
} else {
string dyn = field.substr(1);
transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper);
for (int i = 0; loud_lookup[i].str; i++) {
if (streql(loud_lookup[i].str, dyn.c_str())) {
return (double) loud_lookup[i].val;
}
}
}
parse_error(field, 1, msg);
return 100.0;
}
int key_lookup[] = {21, 23, 12, 14, 16, 17, 19};
// the field can be K<number> or K[A-G]<number> or P[A-G]<number>
// (this can be called from parse_pitch() to handle [A-G])
// Notice that the routine ignores the first character: K or P
//
int Alg_reader::parse_key(string &field)
{
const char *msg = "Pitch expected";
const char *pitches = "ABCDEFG";
const char *p;
if (isdigit(field[1])) {
// This routine would not have been called if field = "P<number>"
// so it must be "K<number>" so <number> must be an integer.
return (int) parse_int(field);
} else if ((p = strchr(pitches, toupper(field[1])))) {
int key = key_lookup[p - pitches];
key = parse_after_key(key, field, 2);
return key;
}
parse_error(field, 1, msg);
return 0;
}
int Alg_reader::parse_after_key(int key, string &field, int n)
{
if ((int) field.length() == n) {
return key;
}
char c = toupper(field[n]);
if (c == 'S') {
return parse_after_key(key + 1, field, n + 1);
}
if (c == 'F') {
return parse_after_key(key - 1, field, n + 1);
}
if (isdigit(field[n])) {
int last = find_int_in(field, n);
string octave = field.substr(n, last - n);
int oct = atoi(octave.c_str());
return parse_after_key(key + oct * 12, field, last);
}
parse_error(field, n, "Unexpected character in pitch");
return key;
}
int Alg_reader::find_int_in(string &field, int n)
{
while ((int) field.length() > n && isdigit(field[n])) {
n = n + 1;
}
return n;
}
bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param)
{
int i = 1;
while (i < (int) field.length()) {
if (field[i] == ':') {
string attr = field.substr(1, i - 1);
char type_char = field[i - 1];
if (strchr("iarsl", type_char)) {
param->set_attr(symbol_table.insert_string(attr.c_str()));
parse_val(param, field, i + 1);
} else {
parse_error(field, 0,
"attribute needs to end with typecode: i,a,r,s, or l");
}
return !error_flag;
}
i = i + 1;
}
return false;
}
bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i)
{
int len = (int) s.length();
if (i >= len) {
return false;
}
if (s[i] == '"') {
if (!check_type('s', param)) {
return false;
}
// note: (len - i) includes 2 quote characters but no EOS character
// so total memory to allocate is (len - i) - 1
char *r = new char[(len - i) - 1];
strncpy(r, s.c_str() + i + 1, (len - i) - 2);
r[(len - i) - 2] = 0; // terminate the string
param->s = r;
} else if (s[i] == '\'') {
if (!check_type('a', param)) {
return false;
}
string r = s.substr(i + 1, len - i - 2);
param->a = symbol_table.insert_string(r.c_str());
} else if (param->attr_type() == 'l') {
if (streql(s.c_str() + i, "true") ||
streql(s.c_str() + i, "t")) {
param->l = true;
} else if (streql(s.c_str() + i, "false") ||
streql(s.c_str() + i, "nil")) {
param->l = false;
} else return false;
} else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') {
int pos = i;
bool period = false;
int sign = 1;
if (s[pos] == '-') {
sign = -1;
pos++;
}
while (pos < len) {
if (isdigit(s[pos])) {
;
} else if (!period && s[pos] == '.') {
period = true;
} else {
parse_error(s, pos, "Unexpected char in number");
return false;
}
pos = pos + 1;
}
string r = s.substr(i, len - i);
if (period) {
if (!check_type('r', param)) {
return false;
}
param->r = atof(r.c_str());
} else {
if (param->attr_type() == 'r') {
param->r = atoi(r.c_str());
} else if (!check_type('i', param)) {
return false;
} else {
param->i = atoi(r.c_str());
}
}
} else {
parse_error(s, i, "invalid value");
return false;
}
return true;
}
bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param)
{
return param->attr_type() == type_char;
}
//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4}
//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23}
/*
def test():
reader = Alg_reader(open("data\\test.gro", "r"))
reader.parse()
score = reader->seq.notes
print "score:", score
reader = nil
*/