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 vartypes.int_to_integer_signed(int(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 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_indices(gmls): """ Parse constant array indices. """ indices = [] c = util.skip(gmls, ml_whitepace) if c in ('[', '('): gmls.read(1) while True: indices.append(ml_parse_const_int(gmls)) c = util.skip(gmls, ml_whitepace) if c == ',': gmls.read(1) else: break util.require_read(gmls, (']', ')')) return indices
def str_to_number(strval, allow_nonnum=True): """ Convert Python str to BASIC value. """ ins = StringIO(strval) outs = StringIO() # skip spaces and line feeds (but not NUL). util.skip(ins, (' ', '\n')) tokenise_number(ins, outs) outs.seek(0) value = parse_value(outs) if not allow_nonnum and util.skip_white(ins) != '': # not everything has been parsed - error return None if not value: return vartypes.null('%') return value
def ml_parse_value(gmls, default=None): """ Parse a value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) sgn = -1 if c == '-' else 1 if c in ('+', '-'): gmls.read(1) c = util.peek(gmls) # don't allow default if sign is given default = None if c == '=': gmls.read(1) c = util.peek(gmls) if len(c) == 0: raise error.RunError(error.IFC) elif ord(c) > 8: name = util.get_var_name(gmls) indices = ml_parse_indices(gmls) step = var.get_var_or_array(name, indices) util.require_read(gmls, (';',), err=error.IFC) else: # varptr$ step = get_value_for_varptrstr(gmls.read(3)) elif c and c in string.digits: step = ml_parse_const(gmls) elif default is not None: step = default else: raise error.RunError(error.IFC) if sgn == -1: step = vartypes.number_neg(step) return step
def ml_parse_value(gmls, default=None): """ Parse a value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) sgn = -1 if c == '-' else 1 if not c: raise error.RunError(error.IFC) if c in ('+', '-'): gmls.read(1) c = util.peek(gmls) # don't allow default if sign is given default = None if c == '=': gmls.read(1) c = util.peek(gmls) if len(c) == 0: raise error.RunError(error.IFC) elif ord(c) > 8: name = util.get_var_name(gmls) indices = ml_parse_indices(gmls) step = var.get_var_or_array(name, indices) util.require_read(gmls, (';',), err=error.IFC) else: # varptr$ step = get_value_for_varptrstr(gmls.read(3)) elif c in representation.ascii_digits: step = ml_parse_const(gmls) elif default is not None: step = default else: raise error.RunError(error.IFC) if sgn == -1: step = vartypes.number_neg(step) return step
def str_to_value_keep(strval, allow_nonnum=True): """ Convert BASIC string to BASIC value. """ if strval == ('$', ''): return vartypes.null['%'] strval = str(vartypes.pass_string_unpack(strval)) ins = StringIO(strval) outs = StringIO() # skip spaces and line feeds (but not NUL). util.skip(ins, (' ', '\n')) tokenise_number(ins, outs) outs.seek(0) value = util.parse_value(outs) if not allow_nonnum: if util.skip_white(ins) != '': # not everything has been parsed - error return None return value
def ld_subsets(mat): A = sympy.Matrix(mat).transpose().echelon_form() % 2 n = A.shape[1] b = sympy.zeros(n, 1) solutions = sympy.linsolve((A, b), sympy.symbols(f't:{n}')) parameters = solutions.free_symbols for solution in solutions: for values in skip(1, product(*([range(2)] * len(parameters)))): subs = {t: v for (t, v) in zip(parameters, values)} subbed = [a % 2 for a in solution.subs(subs)] yield list(i for (i, bit) in enumerate(subbed) if bit)
def str_to_value_keep(strval, allow_nonnum=True): """ Convert BASIC string to BASIC value (VAL). """ if strval == ('$', ''): if allow_nonnum: return vartypes.null['%'] else: return None strval = str(vartypes.pass_string_unpack(strval)) ins = StringIO(strval) outs = StringIO() # skip spaces and line feeds (but not NUL). util.skip(ins, (' ', '\n')) tokenise_number(ins, outs) outs.seek(0) value = parse_value(outs) if not allow_nonnum: if util.skip_white(ins) != '': # not everything has been parsed - error return None return value
def tokenise_word(ins, outs): """ Convert a keyword to tokenised form. """ word = '' while True: c = ins.read(1).upper() word += c # special cases 'GO TO' -> 'GOTO', 'GO SUB' -> 'GOSUB' if word == 'GO': pos = ins.tell() # GO SUB allows 1 space if util.peek(ins, 4) == ' SUB': word = 'GOSUB' ins.read(4) else: # GOTO allows any number of spaces nxt = util.skip(ins, whitespace) if ins.read(2) == 'TO': word = 'GOTO' else: ins.seek(pos) if word in ('GOTO', 'GOSUB'): nxt = util.peek(ins).upper() if nxt in name_chars: ins.seek(pos) word = 'GO' else: pass if word in keyword_to_token: # ignore if part of a longer name, except FN, SPC(, TAB(, USR if word not in ('FN', 'SPC(', 'TAB(', 'USR'): nxt = util.peek(ins).upper() if nxt in name_chars: continue token = keyword_to_token[word] # handle special case ELSE -> :ELSE if word == 'ELSE': outs.write(':' + token) # handle special case WHILE -> WHILE+ elif word == 'WHILE': outs.write(token + tk.O_PLUS) else: outs.write(token) break # allowed names: letter + (letters, numbers, .) elif not(c in name_chars): if c!='': word = word[:-1] ins.seek(-1, 1) outs.write(word) break return word
def tokenise_word(ins, outs): """ Convert a keyword to tokenised form. """ word = '' while True: c = ins.read(1).upper() word += c # special cases 'GO TO' -> 'GOTO', 'GO SUB' -> 'GOSUB' if word == 'GO': pos = ins.tell() # GO SUB allows 1 space if util.peek(ins, 4) == ' SUB': word = 'GOSUB' ins.read(4) else: # GOTO allows any number of spaces nxt = util.skip(ins, ascii_whitespace) if ins.read(2) == 'TO': word = 'GOTO' else: ins.seek(pos) if word in ('GOTO', 'GOSUB'): nxt = util.peek(ins).upper() if nxt and nxt in name_chars: ins.seek(pos) word = 'GO' if word in keyword_to_token: # ignore if part of a longer name, except FN, SPC(, TAB(, USR if word not in ('FN', 'SPC(', 'TAB(', 'USR'): nxt = util.peek(ins).upper() if nxt and nxt in name_chars: continue token = keyword_to_token[word] # handle special case ELSE -> :ELSE if word == 'ELSE': outs.write(':' + token) # handle special case WHILE -> WHILE+ elif word == 'WHILE': outs.write(token + tk.O_PLUS) else: outs.write(token) break # allowed names: letter + (letters, numbers, .) elif not c: outs.write(word) break elif c not in name_chars: word = word[:-1] ins.seek(-1, 1) outs.write(word) break return word
def ml_parse_string(gmls): """ Parse a string value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) if len(c) == 0: raise error.RunError(error.IFC) elif ord(c) > 8: name = util.get_var_name(gmls, err=error.IFC) indices = ml_parse_indices(gmls) sub = var.get_var_or_array(name, indices) util.require_read(gmls, (';',), err=error.IFC) return vartypes.pass_string_unpack(sub, err=error.IFC) else: # varptr$ return vartypes.pass_string_unpack(get_value_for_varptrstr(gmls.read(3)))
def ml_parse_string(gmls): """ Parse a string value in a macro-language string. """ c = util.skip(gmls, ml_whitepace) if len(c) == 0: raise error.RunError(error.IFC) elif ord(c) > 8: name = util.get_var_name(gmls) indices = ml_parse_indices(gmls) sub = var.get_var_or_array(name, indices) util.require_read(gmls, (';',), err=error.IFC) return vartypes.pass_string_unpack(sub, err=error.IFC) else: # varptr$ return vartypes.pass_string_unpack(get_value_for_varptrstr(gmls.read(3)))
def draw(self, gml): """ DRAW: Execute a Graphics Macro Language string. """ # don't convert to uppercase as VARPTR$ elements are case sensitive gmls = StringIO(gml) plot, goback = True, False while True: c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == '': break elif c == ';': continue elif c == 'B': # do not draw plot = False elif c == 'N': # return to postiton after move goback = True elif c == 'X': # execute substring sub = draw_and_play.ml_parse_string(gmls) self.draw(str(sub)) elif c == 'C': # set foreground colour # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.last_attr = 0 else: self.last_attr = draw_and_play.ml_parse_number(gmls) elif c == 'S': # set scale self.draw_scale = draw_and_play.ml_parse_number(gmls) elif c == 'A': # set angle # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.draw_angle = 0 else: self.draw_angle = 90 * draw_and_play.ml_parse_number(gmls) elif c == 'T': # 'turn angle' - set (don't turn) the angle to any value if gmls.read(1).upper() != 'A': raise error.RunError(error.IFC) # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.draw_angle = 0 else: self.draw_angle = draw_and_play.ml_parse_number(gmls) # one-variable movement commands: elif c in ('U', 'D', 'L', 'R', 'E', 'F', 'G', 'H'): step = draw_and_play.ml_parse_number( gmls, default=vartypes.pack_int(1)) x0, y0 = self.last_point x1, y1 = 0, 0 if c in ('U', 'E', 'H'): y1 -= step elif c in ('D', 'F', 'G'): y1 += step if c in ('L', 'G', 'H'): x1 -= step elif c in ('R', 'E', 'F'): x1 += step self.draw_step(x0, y0, x1, y1, plot, goback) plot = True goback = False # two-variable movement command elif c == 'M': relative = util.skip(gmls, draw_and_play.ml_whitepace) in ('+', '-') x = draw_and_play.ml_parse_number(gmls) if util.skip(gmls, draw_and_play.ml_whitepace) != ',': raise error.RunError(error.IFC) else: gmls.read(1) y = draw_and_play.ml_parse_number(gmls) x0, y0 = self.last_point if relative: self.draw_step(x0, y0, x, y, plot, goback) else: if plot: self.draw_line(x0, y0, x, y, self.last_attr) self.last_point = x, y if goback: self.last_point = x0, y0 plot = True goback = False elif c == 'P': # paint - flood fill colour = draw_and_play.ml_parse_number(gmls) if util.skip_read(gmls, draw_and_play.ml_whitepace) != ',': raise error.RunError(error.IFC) bound = draw_and_play.ml_parse_number(gmls) x, y = self.get_window_logical(*self.last_point) self.paint((x, y, False), None, colour, bound, None)
def draw(self, gml): """ DRAW: Execute a Graphics Macro Language string. """ # don't convert to uppercase as VARPTR$ elements are case sensitive gmls = StringIO(gml) plot, goback = True, False while True: c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == '': break elif c == ';': continue elif c == 'B': # do not draw plot = False elif c == 'N': # return to postiton after move goback = True elif c == 'X': # execute substring sub = draw_and_play.ml_parse_string(gmls) self.draw(str(sub)) elif c == 'C': # set foreground colour # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.last_attr = 0 else: self.last_attr = draw_and_play.ml_parse_number(gmls) elif c == 'S': # set scale self.draw_scale = draw_and_play.ml_parse_number(gmls) elif c == 'A': # set angle # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.draw_angle = 0 else: self.draw_angle = 90 * draw_and_play.ml_parse_number(gmls) elif c == 'T': # 'turn angle' - set (don't turn) the angle to any value if gmls.read(1).upper() != 'A': raise error.RunError(error.IFC) # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ';': self.draw_angle = 0 else: self.draw_angle = draw_and_play.ml_parse_number(gmls) # one-variable movement commands: elif c in ('U', 'D', 'L', 'R', 'E', 'F', 'G', 'H'): step = draw_and_play.ml_parse_number(gmls, default=vartypes.int_to_integer_signed(1)) x0, y0 = self.last_point x1, y1 = 0, 0 if c in ('U', 'E', 'H'): y1 -= step elif c in ('D', 'F', 'G'): y1 += step if c in ('L', 'G', 'H'): x1 -= step elif c in ('R', 'E', 'F'): x1 += step self.draw_step(x0, y0, x1, y1, plot, goback) plot = True goback = False # two-variable movement command elif c == 'M': relative = util.skip(gmls, draw_and_play.ml_whitepace) in ('+','-') x = draw_and_play.ml_parse_number(gmls) if util.skip(gmls, draw_and_play.ml_whitepace) != ',': raise error.RunError(error.IFC) else: gmls.read(1) y = draw_and_play.ml_parse_number(gmls) x0, y0 = self.last_point if relative: self.draw_step(x0, y0, x, y, plot, goback) else: if plot: self.draw_line(x0, y0, x, y, self.last_attr) self.last_point = x, y if goback: self.last_point = x0, y0 plot = True goback = False elif c =='P': # paint - flood fill colour = draw_and_play.ml_parse_number(gmls) if util.skip_read(gmls, draw_and_play.ml_whitepace) != ',': raise error.RunError(error.IFC) bound = draw_and_play.ml_parse_number(gmls) x, y = self.get_window_logical(*self.last_point) self.paint((x, y, False), None, colour, bound, None)
def draw(self, gml): """ DRAW: Execute a Graphics Macro Language string. """ # don't convert to uppercase as VARPTR$ elements are case sensitive gmls = StringIO(gml) plot, goback = True, False while True: c = util.skip_read(gmls, draw_and_play.ml_whitepace).upper() if c == "": break elif c == ";": continue elif c == "B": # do not draw plot = False elif c == "N": # return to postiton after move goback = True elif c == "X": # execute substring sub = draw_and_play.ml_parse_string(gmls) self.draw(str(sub)) elif c == "C": # set foreground colour # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ";": self.last_attr = 0 else: self.last_attr = draw_and_play.ml_parse_number(gmls) elif c == "S": # set scale self.draw_scale = draw_and_play.ml_parse_number(gmls) elif c == "A": # set angle # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ";": self.draw_angle = 0 else: self.draw_angle = 90 * draw_and_play.ml_parse_number(gmls) elif c == "T": # 'turn angle' - set (don't turn) the angle to any value if gmls.read(1).upper() != "A": raise error.RunError(5) # allow empty spec (default 0), but only if followed by a semicolon if util.skip(gmls, draw_and_play.ml_whitepace) == ";": self.draw_angle = 0 else: self.draw_angle = draw_and_play.ml_parse_number(gmls) # one-variable movement commands: elif c in ("U", "D", "L", "R", "E", "F", "G", "H"): step = draw_and_play.ml_parse_number(gmls, default=vartypes.pack_int(1)) x0, y0 = self.last_point x1, y1 = 0, 0 if c in ("U", "E", "H"): y1 -= step elif c in ("D", "F", "G"): y1 += step if c in ("L", "G", "H"): x1 -= step elif c in ("R", "E", "F"): x1 += step self.draw_step(x0, y0, x1, y1, plot, goback) plot = True goback = False # two-variable movement command elif c == "M": relative = util.skip(gmls, draw_and_play.ml_whitepace) in ("+", "-") x = draw_and_play.ml_parse_number(gmls) if util.skip(gmls, draw_and_play.ml_whitepace) != ",": raise error.RunError(5) else: gmls.read(1) y = draw_and_play.ml_parse_number(gmls) x0, y0 = self.last_point if relative: self.draw_step(x0, y0, x, y, plot, goback) else: if plot: self.draw_line(x0, y0, x, y, self.last_attr) self.last_point = x, y if goback: self.last_point = x0, y0 plot = True goback = False elif c == "P": # paint - flood fill colour = draw_and_play.ml_parse_number(gmls) if util.skip_read(gmls, draw_and_play.ml_whitepace) != ",": raise error.RunError(5) bound = draw_and_play.ml_parse_number(gmls) x, y = self.get_window_logical(*self.last_point) self.paint((x, y, False), None, colour, bound, None)
def test_transf_not_implemented(): meths = ['fourier', 'taylor'] kernel = _kernels.Maternp(p=0) for meth in meths: with pytest.raises(NotImplementedError): getattr(kernel, meth)(True, None) with pytest.raises(NotImplementedError): getattr(kernel, meth)(None, True) with pytest.raises(NotImplementedError): getattr(kernel, meth)(True, True) ##################### XFAILS/SKIPS ##################### util.skip(TestAR, 'test_normalized') util.skip(TestMA, 'test_normalized') # TODO These are isotropic kernels with the input='soft' option. The problems # arise where x == y. => use make_jaxpr to debug? util.xfail(TestWendland, 'test_positive_deriv2_nd') util.xfail(TestWendland, 'test_double_diff_nd_second_chopped') util.xfail(TestWendland, 'test_continuous_at_zero_2') util.xfail(TestWendland, 'test_jit_deriv2_nd') util.xfail(TestCausalExpQuad, 'test_positive_deriv2_nd') util.xfail(TestCausalExpQuad, 'test_double_diff_nd_second_chopped') util.xfail(TestCausalExpQuad, 'test_continuous_at_zero_2') # TODO some xpass, likely numerical precision problems util.xfail(TestWendland, 'test_positive_deriv2') # normally xpasses util.xfail(TestCausalExpQuad, 'test_positive_deriv2') # NOT 1 - erf cancel
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 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 string.digits: numstr = '' while c and c in string.digits: gmls.read(1) numstr += c c = util.skip(gmls, draw_and_play.ml_whitepace) # NOT ml_parse_number, only literals allowed here! length = int(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 tokenise_line(line): """ Convert an ascii program line to tokenised form. """ ins = StringIO(line) outs = StringIO() # skip whitespace at start of line d = util.skip(ins, ascii_whitespace) if d == '': # empty line at EOF return outs # read the line number tokenise_line_number(ins, outs) # expect line number allow_jumpnum = False # expect number (6553 6 -> the 6 is encoded as \x17) allow_number = True # flag for SPC( or TAB( as numbers can follow the closing bracket spc_or_tab = False # parse through elements of line while True: # peek next character, convert to uppercase c = util.peek(ins).upper() # anything after NUL is ignored till EOL if c == '\0': ins.read(1) ascii_read_to(ins, ('', '\r')) break # end of line elif c in ('', '\r'): break # handle whitespace elif c in ascii_whitespace: ins.read(1) outs.write(c) # handle string literals elif util.peek(ins) == '"': tokenise_literal(ins, outs) # handle jump numbers elif allow_number and allow_jumpnum and c in ascii_digits + '.': tokenise_jump_number(ins, outs) # handle numbers # numbers following var names with no operator or token in between # should not be parsed, eg OPTION BASE 1 # note we don't include leading signs, encoded as unary operators # number starting with . or & are always parsed elif c in ('&', '.') or (allow_number and not allow_jumpnum and c in ascii_digits): representation.tokenise_number(ins, outs) # operator keywords ('+', '-', '=', '/', '\\', '^', '*', '<', '>'): elif c in ascii_operators: ins.read(1) # operators don't affect line number mode - can do line number # arithmetic and RENUM will do the strangest things # this allows for 'LIST 100-200' etc. outs.write(keyword_to_token[c]) allow_number = True # special case ' -> :REM' elif c == "'": ins.read(1) outs.write(':' + tk.REM + tk.O_REM) tokenise_rem(ins, outs) # special case ? -> PRINT elif c == '?': ins.read(1) outs.write(tk.PRINT) allow_number = True # keywords & variable names elif c in ascii_uppercase: word = tokenise_word(ins, outs) # handle non-parsing modes if (word in ('REM', "'") or (word == 'DEBUG' and word in keyword_to_token)): tokenise_rem(ins, outs) elif word == "DATA": tokenise_data(ins, outs) else: allow_jumpnum = (word in linenum_words) # numbers can follow tokenised keywords # (which does not include the word 'AS') allow_number = (word in keyword_to_token) if word in ('SPC(', 'TAB('): spc_or_tab = True else: ins.read(1) if c in (',', '#', ';'): # can separate numbers as well as jumpnums allow_number = True elif c in ('(', '['): allow_jumpnum, allow_number = False, True elif c == ')' and spc_or_tab: spc_or_tab = False allow_jumpnum, allow_number = False, True else: allow_jumpnum, allow_number = False, False # replace all other nonprinting chars by spaces; # HOUSE 0x7f is allowed. outs.write(c if ord(c) >= 32 and ord(c) <= 127 else ' ') outs.seek(0) return outs
def tokenise_line(line): """ Convert an ascii program line to tokenised form. """ ins = StringIO(line) outs = StringIO() # skip whitespace at start of line d = util.skip(ins, whitespace) if d == '': # empty line at EOF return outs # read the line number tokenise_line_number(ins, outs) # expect line number allow_jumpnum = False # expect number (6553 6 -> the 6 is encoded as \x17) allow_number = True # flag for SPC( or TAB( as numbers can follow the closing bracket spc_or_tab = False # parse through elements of line while True: # peek next character, convert to uppercase c = util.peek(ins).upper() # anything after NUL is ignored till EOL if c == '\0': ins.read(1) ascii_read_to(ins, ('', '\r')) break # end of line elif c in ('', '\r'): break # handle whitespace elif c in whitespace: ins.read(1) outs.write(c) # handle string literals elif util.peek(ins) == '"': tokenise_literal(ins, outs) # handle jump numbers elif allow_number and allow_jumpnum and c in ascii_digits + ('.',): tokenise_jump_number(ins, outs) # handle numbers # numbers following var names with no operator or token in between # should not be parsed, eg OPTION BASE 1 # note we don't include leading signs, encoded as unary operators # number starting with . or & are always parsed elif c in ('&', '.') or (allow_number and not allow_jumpnum and c in ascii_digits): representation.tokenise_number(ins, outs) # operator keywords ('+', '-', '=', '/', '\\', '^', '*', '<', '>'): elif c in ascii_operators: ins.read(1) # operators don't affect line number mode - can do line number # arithmetic and RENUM will do the strangest things # this allows for 'LIST 100-200' etc. outs.write(keyword_to_token[c]) allow_number = True # special case ' -> :REM' elif c == "'": ins.read(1) outs.write(':' + tk.REM + tk.O_REM) tokenise_rem(ins, outs) # special case ? -> PRINT elif c == '?': ins.read(1) outs.write(tk.PRINT) allow_number = True # keywords & variable names elif c in ascii_uppercase: word = tokenise_word(ins, outs) # handle non-parsing modes if (word in ('REM', "'") or (word == 'DEBUG' and word in keyword_to_token)): tokenise_rem(ins, outs) elif word == "DATA": tokenise_data(ins, outs) else: allow_jumpnum = (word in linenum_words) # numbers can follow tokenised keywords # (which does not include the word 'AS') allow_number = (word in keyword_to_token) if word in ('SPC(', 'TAB('): spc_or_tab = True else: ins.read(1) if c in (',', '#', ';'): # can separate numbers as well as jumpnums allow_number = True elif c in ('(', '['): allow_jumpnum, allow_number = False, True elif c == ')' and spc_or_tab: spc_or_tab = False allow_jumpnum, allow_number = False, True else: allow_jumpnum, allow_number = False, False # replace all other nonprinting chars by spaces; # HOUSE 0x7f is allowed. outs.write(c if ord(c) >= 32 and ord(c) <= 127 else ' ') outs.seek(0) return outs
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 string.digits: numstr = '' while c and c in string.digits: gmls.read(1) numstr += c c = util.skip(gmls, draw_and_play.ml_whitepace) # NOT ml_parse_number, only literals allowed here! length = int(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 (self.capabilities == 'tandy' or (self.capabilities == '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()