def vplus(left, right): """ Left+right. """ if left[0] == '$': return vartypes.pack_string( vartypes.pass_string_unpack(left) + vartypes.pass_string_unpack(right)) else: return vartypes.number_add(left, right)
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 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 value_instr(ins): """ INSTR: find substring in string. """ util.require_read(ins, ('(',)) big, small, n = '', '', 1 s = parse_expression(ins, empty_err=error.STX) if s[0] != '$': n = vartypes.pass_int_unpack(s) util.range_check(1, 255, n) util.require_read(ins, (',',)) big = vartypes.pass_string_unpack(parse_expression(ins, allow_empty=True)) else: big = vartypes.pass_string_unpack(s) util.require_read(ins, (',',)) small = vartypes.pass_string_unpack(parse_expression(ins, allow_empty=True)) util.require_read(ins, (')',)) return vartypes.str_instr(big, small, n)
def set_var(name, value): """ Assign a value to a variable. """ name = vartypes.complete_name(name) type_char = name[-1] # check if garbage needs collecting before allocating mem size = (max(3, len(name)) + 1 + byte_size[type_char]) if type_char == '$': unpacked = vartypes.pass_string_unpack(value) size += len(unpacked) if fre() <= size: # TODO: GARBTEST difference is because string literal is currently stored in string space, whereas GW stores it in code space. collect_garbage() if fre() <= size: raise error.RunError(7) # assign variables if type_char == '$': # every assignment to string leads to new pointer being allocated # TODO: string literals in programs have the var ptr point to program space. state.basic_state.variables[name] = state.basic_state.strings.store(bytearray(unpacked[:])) else: # make a copy of the value in case we want to use POKE on it - we would change both values otherwise # NOTE: this is an in-place copy - crucial for FOR! try: state.basic_state.variables[name][:] = vartypes.pass_type_keep(name[-1], value)[1][:] except KeyError: state.basic_state.variables[name] = vartypes.pass_type_keep(name[-1], value)[1][:] # update memory model # first two bytes: chars of name or 0 if name is one byte long if name not in state.basic_state.var_memory: name_ptr = state.basic_state.var_current var_ptr = name_ptr + max(3, len(name)) + 1 # byte_size first_letter second_letter_or_nul remaining_length_or_nul state.basic_state.var_current += max(3, len(name)) + 1 + byte_size[name[-1]] state.basic_state.var_memory[name] = (name_ptr, var_ptr)
def value_instr(ins): """ INSTR: find substring in string. """ util.require_read(ins, ('(', )) big, small, n = '', '', 1 s = parse_expression(ins, empty_err=error.STX) if s[0] != '$': n = vartypes.pass_int_unpack(s) util.range_check(1, 255, n) util.require_read(ins, (',', )) big = vartypes.pass_string_unpack( parse_expression(ins, allow_empty=True)) else: big = vartypes.pass_string_unpack(s) util.require_read(ins, (',', )) small = vartypes.pass_string_unpack(parse_expression(ins, allow_empty=True)) util.require_read(ins, (')', )) return vartypes.str_instr(big, small, n)
def type_converter(self, v_val): if (v_val[0] == '%'): return vartypes.pass_int_unpack(v_val) elif (v_val[0] == '!'): return fp.Single.from_bytes(v_val[1]).to_value() elif (v_val[0] == '#'): return fp.Double.from_bytes(v_val[1]).to_value() elif (v_val[0] == '$'): return str(vartypes.pass_string_unpack(v_val)) else: raise error.RunError(error.TYPE_MISMATCH)
def value_right(ins): """ RIGHT$: get substring at the end of string. """ util.require_read(ins, ('(',)) s = vartypes.pass_string_unpack(parse_expression(ins)) util.require_read(ins, (',',)) stop = vartypes.pass_int_unpack(parse_expression(ins)) util.require_read(ins, (')',)) util.range_check(0, 255, stop) if stop == 0: return vartypes.null['$'] stop = min(stop, len(s)) return vartypes.pack_string(s[-stop:])
def value_right(ins): """ RIGHT$: get substring at the end of string. """ util.require_read(ins, ('(', )) s = vartypes.pass_string_unpack(parse_expression(ins)) util.require_read(ins, (',', )) stop = vartypes.pass_int_unpack(parse_expression(ins)) util.require_read(ins, (')', )) util.range_check(0, 255, stop) if stop == 0: return vartypes.null['$'] stop = min(stop, len(s)) return vartypes.pack_string(s[-stop:])
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 value_mid(ins): """ MID$: get substring. """ util.require_read(ins, ('(',)) s = vartypes.pass_string_unpack(parse_expression(ins)) util.require_read(ins, (',',)) start = vartypes.pass_int_unpack(parse_expression(ins)) if util.skip_white_read_if(ins, (',',)): num = vartypes.pass_int_unpack(parse_expression(ins)) else: num = len(s) util.require_read(ins, (')',)) util.range_check(1, 255, start) util.range_check(0, 255, num) if num == 0 or start > len(s): return vartypes.null['$'] start -= 1 stop = start + num stop = min(stop, len(s)) return vartypes.pack_string(s[start:stop])
def value_mid(ins): """ MID$: get substring. """ util.require_read(ins, ('(', )) s = vartypes.pass_string_unpack(parse_expression(ins)) util.require_read(ins, (',', )) start = vartypes.pass_int_unpack(parse_expression(ins)) if util.skip_white_read_if(ins, (',', )): num = vartypes.pass_int_unpack(parse_expression(ins)) else: num = len(s) util.require_read(ins, (')', )) util.range_check(1, 255, start) util.range_check(0, 255, num) if num == 0 or start > len(s): return vartypes.null['$'] start -= 1 stop = start + num stop = min(stop, len(s)) return vartypes.pack_string(s[start:stop])
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 set_var(name, value): """ Assign a value to a variable. """ name = vartypes.complete_name(name) type_char = name[-1] # check if garbage needs collecting before allocating mem size = (max(3, len(name)) + 1 + byte_size[type_char]) if type_char == '$': unpacked = vartypes.pass_string_unpack(value) size += len(unpacked) if fre() <= size: # TODO: GARBTEST difference is because string literal is currently stored in string space, whereas GW stores it in code space. collect_garbage() if fre() <= size: raise error.RunError(error.OUT_OF_MEMORY) # assign variables if type_char == '$': # every assignment to string leads to new pointer being allocated # TODO: string literals in programs have the var ptr point to program space. state.basic_state.variables[name] = state.basic_state.strings.store( bytearray(unpacked[:])) else: # make a copy of the value in case we want to use POKE on it - we would change both values otherwise # NOTE: this is an in-place copy - crucial for FOR! try: state.basic_state.variables[name][:] = vartypes.pass_type_keep( name[-1], value)[1][:] except KeyError: state.basic_state.variables[name] = vartypes.pass_type_keep( name[-1], value)[1][:] # update memory model # first two bytes: chars of name or 0 if name is one byte long if name not in state.basic_state.var_memory: name_ptr = state.basic_state.var_current var_ptr = name_ptr + max( 3, len(name) ) + 1 # byte_size first_letter second_letter_or_nul remaining_length_or_nul state.basic_state.var_current += max( 3, len(name)) + 1 + byte_size[name[-1]] state.basic_state.var_memory[name] = (name_ptr, var_ptr)
def value_cvs(ins): """ CVS: return the single-precision value of a byte representation. """ cstr = vartypes.pass_string_unpack(parse_bracket(ins)) if len(cstr) < 4: raise error.RunError(error.IFC) return ('!', cstr[:4])
def vplus(left, right): """ Left+right. """ if left[0] == '$': return vartypes.pack_string(vartypes.pass_string_unpack(left) + vartypes.pass_string_unpack(right)) else: return vartypes.number_add(left, right)
def value_cvd(ins): """ CVD: return the double-precision value of a byte representation. """ cstr = vartypes.pass_string_unpack(parse_bracket(ins)) if len(cstr) < 8: raise error.RunError(error.IFC) return ('#', cstr[:8])
def value_asc(ins): """ ASC: ordinal ASCII value of a character. """ s = vartypes.pass_string_unpack(parse_bracket(ins)) if not s: raise error.RunError(error.IFC) return vartypes.pack_int(s[0])
def value_cvi(ins): """ CVI: return the int value of a byte representation. """ cstr = vartypes.pass_string_unpack(parse_bracket(ins)) if len(cstr) < 2: raise error.RunError(error.IFC) return vartypes.pack_int(vartypes.sint_to_value(cstr[:2]))
def value_len(ins): """ LEN: length of string. """ return vartypes.pack_int( len(vartypes.pass_string_unpack(parse_bracket(ins))))
def value_len(ins): """ LEN: length of string. """ return vartypes.pack_int(len(vartypes.pass_string_unpack(parse_bracket(ins))) )