def _input_entry(self, typechar, allow_past_end): """ Read a number or string entry for INPUT """ word, blanks = '', '' last = self._skip_whitespace(self.whitespace_input) # read first non-whitespace char c = self.read(1) # LF escapes quotes # may be true if last == '', hence "in ('\n', '\0')" not "in '\n0'" quoted = (c == '"' and typechar == '$' and last not in ('\n', '\0')) if quoted: c = self.read(1) # LF escapes end of file, return empty string if not c and not allow_past_end and last not in ('\n', '\0'): raise error.RunError(error.INPUT_PAST_END) # we read the ending char before breaking the loop # this may raise FIELD OVERFLOW # on reading from a KYBD: file, control char replacement takes place # which means we need to use read() not read_raw() while c and not ((typechar != '$' and c in self.soft_sep) or (c in ',\r' and not quoted)): if c == '"' and quoted: # whitespace after quote will be skipped below break elif c == '\n' and not quoted: # LF, LFCR are dropped entirely c = self.read(1) if c == '\r': c = self.read(1) continue elif c == '\0': # NUL is dropped even within quotes pass elif c in self.whitespace_input and not quoted: # ignore whitespace in numbers, except soft separators # include internal whitespace in strings if typechar == '$': blanks += c else: word += blanks + c blanks = '' if len(word) + len(blanks) >= 255: break if not quoted: c = self.read(1) else: # no CRLF replacement inside quotes. # -- but should there be KYBD: control char replacement? c = self.read_raw(1) # if separator was a whitespace char or closing quote # skip trailing whitespace before any comma or hard separator if c and c in self.whitespace_input or (quoted and c == '"'): self._skip_whitespace(' ') if (self.next_char in ',\r'): c = self.read(1) # file position is at one past the separator char # convert result to requested type, be strict about non-numeric chars value = vartypes.pack_string(bytearray(word)) if typechar != '$': value = representation.str_to_value_keep(value, allow_nonnum=False) return value, c
def _input_entry(self, typechar, allow_past_end): """ Read a number or string entry for INPUT """ word, blanks = '', '' last = self._skip_whitespace(self.whitespace_input) # read first non-whitespace char c = self.read(1) # LF escapes quotes # may be true if last == '', hence "in ('\n', '\0')" not "in '\n0'" quoted = (c == '"' and typechar == '$' and last not in ('\n', '\0')) if quoted: c = self.read(1) # LF escapes end of file, return empty string if not c and not allow_past_end and last not in ('\n', '\0'): raise error.RunError(error.INPUT_PAST_END) # we read the ending char before breaking the loop # this may raise FIELD OVERFLOW # on reading from a KYBD: file, control char replacement takes place # which means we need to use read() not read_raw() while c and not ((typechar != '$' and c in self.soft_sep) or (c in ',\r' and not quoted)): if c == '"' and quoted: # whitespace after quote will be skipped below break elif c == '\n' and not quoted: # LF, LFCR are dropped entirely c = self.read(1) if c == '\r': c = self.read(1) continue elif c == '\0': # NUL is dropped even within quotes pass elif c in self.whitespace_input and not quoted: # ignore whitespace in numbers, except soft separators # include internal whitespace in strings if typechar == '$': blanks += c else: word += blanks + c blanks = '' if len(word) + len(blanks) >= 255: break if not quoted: c = self.read(1) else: # no CRLF replacement inside quotes. # -- but should there be KYBD: control char replacement? c = self.read_raw(1) # if separator was a whitespace char or closing quote # skip trailing whitespace before any comma or hard separator if c and c in self.whitespace_input or (quoted and c == '"'): self._skip_whitespace(' ') if (self.next_char in ',\r'): c = self.read(1) # file position is at one past the separator char # convert result to requested type, be strict about non-numeric chars value = vartypes.pack_string(bytearray(word)) if typechar != '$': value = representation.str_to_value_keep(value, allow_nonnum=False) return value, c
def _input_entry(self, typechar, allow_past_end): """ Read a number or string entry from KYBD: for INPUT# """ word, blanks = '', '' if self.input_last: c, self.input_last = self.input_last, '' else: last = self._skip_whitespace(self.whitespace_input) # read first non-whitespace char c = self.read(1) # LF escapes quotes # may be true if last == '', hence "in ('\n', '\0')" not "in '\n0'" quoted = (c == '"' and typechar == '$' and last not in ('\n', '\0')) if quoted: c = self.read(1) # LF escapes end of file, return empty string if not c and not allow_past_end and last not in ('\n', '\0'): raise error.RunError(error.INPUT_PAST_END) # we read the ending char before breaking the loop # this may raise FIELD OVERFLOW # on reading from a KYBD: file, control char replacement takes place # which means we need to use read() not read_raw() parsing_trail = False while c and not (c in ',\r' and not quoted): if c == '"' and quoted: parsing_trail = True elif c == '\n' and not quoted: # LF, LFCR are dropped entirely c = self.read(1) if c == '\r': c = self.read(1) continue elif c == '\0': # NUL is dropped even within quotes pass elif c in self.whitespace_input and not quoted: # ignore whitespace in numbers, except soft separators # include internal whitespace in strings if typechar == '$': blanks += c else: word += blanks + c blanks = '' if len(word) + len(blanks) >= 255: break # there should be KYBD: control char replacement here even if quoted c = self.read(1) if parsing_trail: if c not in self.whitespace_input: if c not in (',', '\r'): self.input_last = c break parsing_trail = parsing_trail or (typechar != '$' and c == ' ') # file position is at one past the separator char # convert result to requested type, be strict about non-numeric chars value = vartypes.pack_string(bytearray(word)) if typechar != '$': value = representation.str_to_value_keep(value, allow_nonnum=False) return value, c
def ml_parse_const(gmls): """ Parse and return a constant value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) if c and c in string.digits: numstr = '' while c and c in string.digits: gmls.read(1) numstr += c c = util.skip(gmls, ml_whitepace) return representation.str_to_value_keep(('$', numstr)) else: raise error.RunError(error.IFC)
def ml_parse_const(gmls): """ Parse and return a constant value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) if c and c in representation.ascii_digits: numstr = '' while c and c in representation.ascii_digits: gmls.read(1) numstr += c c = util.skip(gmls, ml_whitepace) return representation.str_to_value_keep(('$', numstr)) else: raise error.RunError(error.IFC)
def value_val(ins): """ VAL: number value of a string. """ val = representation.str_to_value_keep(parse_bracket(ins)) return val if val else vartypes.null['%']
def play(self, mml_list): """ Parse a list of Music Macro Language strings. """ gmls_list = [] for mml in mml_list: gmls = StringIO() # don't convert to uppercase as VARPTR$ elements are case sensitive gmls.write(str(mml)) gmls.seek(0) gmls_list.append(gmls) next_oct = 0 total_time = [0, 0, 0, 0] voices = range(3) while True: if not voices: break for voice in voices: vstate = self.play_state[voice] gmls = gmls_list[voice] c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == '': voices.remove(voice) continue elif c == ';': continue elif c == 'X': # execute substring sub = draw_and_play.ml_parse_string(gmls) pos = gmls.tell() rest = gmls.read() gmls.truncate(pos) gmls.write(str(sub)) gmls.write(rest) gmls.seek(pos) elif c == 'N': note = draw_and_play.ml_parse_number(gmls) dur = vstate.length c = util.skip(gmls, draw_and_play.ml_whitepace).upper() if c == '.': gmls.read(1) dur *= 1.5 if note > 0 and note <= 84: self.play_sound(note_freq[note-1], dur*vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice) total_time[voice] += dur*vstate.tempo elif note == 0: self.play_sound(0, dur*vstate.tempo, vstate.speed, volume=0, voice=voice) total_time[voice] += dur*vstate.tempo elif c == 'L': vstate.length = 1./draw_and_play.ml_parse_number(gmls) elif c == 'T': vstate.tempo = 240./draw_and_play.ml_parse_number(gmls) elif c == 'O': vstate.octave = min(6, max(0, draw_and_play.ml_parse_number(gmls))) elif c == '>': vstate.octave += 1 if vstate.octave > 6: vstate.octave = 6 elif c == '<': vstate.octave -= 1 if vstate.octave < 0: vstate.octave = 0 elif c in ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'P'): note = c dur = vstate.length while True: c = util.skip(gmls, draw_and_play.ml_whitepace).upper() if not c: break elif c == '.': gmls.read(1) dur *= 1.5 elif c in representation.ascii_digits: numstr = '' while c and c in representation.ascii_digits: gmls.read(1) numstr += c c = util.skip(gmls, draw_and_play.ml_whitepace) length = vartypes.pass_int_unpack(representation.str_to_value_keep(('$', numstr))) dur = 1. / float(length) elif c in ('#', '+'): gmls.read(1) note += '#' elif c == '-': gmls.read(1) note += '-' else: break if note == 'P': self.play_sound(0, dur * vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice) total_time[voice] += dur*vstate.tempo else: try: self.play_sound( note_freq[(vstate.octave+next_oct)*12 + notes[note]], dur * vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice) total_time[voice] += dur*vstate.tempo except KeyError: raise error.RunError(error.IFC) next_oct = 0 elif c == 'M': c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == 'N': vstate.speed = 7./8. elif c == 'L': vstate.speed = 1. elif c == 'S': vstate.speed = 3./4. elif c == 'F': self.foreground = True elif c == 'B': self.foreground = False else: raise error.RunError(error.IFC) elif c == 'V' and (pcjr_sound == 'tandy' or (pcjr_sound == 'pcjr' and self.sound_on)): vstate.volume = min(15, max(0, draw_and_play.ml_parse_number(gmls))) else: raise error.RunError(error.IFC) max_time = max(total_time) for voice in range(3): if total_time[voice] < max_time: self.play_sound(0, max_time - total_time[voice], 1, 0, voice) if self.foreground: self.wait_all_music()
def play(self, mml_list): """ Parse a list of Music Macro Language strings. """ gmls_list = [] for mml in mml_list: gmls = StringIO() # don't convert to uppercase as VARPTR$ elements are case sensitive gmls.write(str(mml)) gmls.seek(0) gmls_list.append(gmls) next_oct = 0 total_time = [0, 0, 0, 0] voices = range(3) while True: if not voices: break for voice in voices: vstate = self.play_state[voice] gmls = gmls_list[voice] c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == "": voices.remove(voice) continue elif c == ";": continue elif c == "X": # execute substring sub = draw_and_play.ml_parse_string(gmls) pos = gmls.tell() rest = gmls.read() gmls.truncate(pos) gmls.write(str(sub)) gmls.write(rest) gmls.seek(pos) elif c == "N": note = draw_and_play.ml_parse_number(gmls) dur = vstate.length c = util.skip(gmls, draw_and_play.ml_whitepace).upper() if c == ".": gmls.read(1) dur *= 1.5 if note > 0 and note <= 84: self.play_sound( note_freq[note - 1], dur * vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice ) total_time[voice] += dur * vstate.tempo elif note == 0: self.play_sound(0, dur * vstate.tempo, vstate.speed, volume=0, voice=voice) total_time[voice] += dur * vstate.tempo elif c == "L": vstate.length = 1.0 / draw_and_play.ml_parse_number(gmls) elif c == "T": vstate.tempo = 240.0 / draw_and_play.ml_parse_number(gmls) elif c == "O": vstate.octave = min(6, max(0, draw_and_play.ml_parse_number(gmls))) elif c == ">": vstate.octave += 1 if vstate.octave > 6: vstate.octave = 6 elif c == "<": vstate.octave -= 1 if vstate.octave < 0: vstate.octave = 0 elif c in ("A", "B", "C", "D", "E", "F", "G", "P"): note = c dur = vstate.length while True: c = util.skip(gmls, draw_and_play.ml_whitepace).upper() if not c: break elif c == ".": gmls.read(1) dur *= 1.5 elif c in representation.ascii_digits: numstr = "" while c and c in representation.ascii_digits: gmls.read(1) numstr += c c = util.skip(gmls, draw_and_play.ml_whitepace) length = vartypes.pass_int_unpack(representation.str_to_value_keep(("$", numstr))) dur = 1.0 / float(length) elif c in ("#", "+"): gmls.read(1) note += "#" elif c == "-": gmls.read(1) note += "-" else: break if note == "P": self.play_sound(0, dur * vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice) total_time[voice] += dur * vstate.tempo else: try: self.play_sound( note_freq[(vstate.octave + next_oct) * 12 + notes[note]], dur * vstate.tempo, vstate.speed, volume=vstate.volume, voice=voice, ) total_time[voice] += dur * vstate.tempo except KeyError: raise error.RunError(error.IFC) next_oct = 0 elif c == "M": c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == "N": vstate.speed = 7.0 / 8.0 elif c == "L": vstate.speed = 1.0 elif c == "S": vstate.speed = 3.0 / 4.0 elif c == "F": self.foreground = True elif c == "B": self.foreground = False else: raise error.RunError(error.IFC) elif c == "V" and (pcjr_sound == "tandy" or (pcjr_sound == "pcjr" and self.sound_on)): vstate.volume = min(15, max(0, draw_and_play.ml_parse_number(gmls))) else: raise error.RunError(error.IFC) max_time = max(total_time) for voice in range(3): if total_time[voice] < max_time: self.play_sound(0, max_time - total_time[voice], 1, 0, voice) if self.foreground: self.wait_all_music()
def value_val(ins): """ VAL: number value of a string. """ val = representation.str_to_value_keep(parse_bracket(ins)) return val if val else vartypes.null['%']