def get_var_name(ins, allow_empty=False): """ Get variable name from token stream. """ name = '' d = skip_white_read(ins).upper() if not d: pass elif d not in string.ascii_uppercase: # variable name must start with a letter ins.seek(-len(d), 1) else: while d and d in name_chars: name += d d = ins.read(1).upper() if d in '$%!#': name += d else: ins.seek(-len(d), 1) if not name and not allow_empty: raise error.RunError(error.STX) # append type specifier name = vartypes.complete_name(name) # only the first 40 chars are relevant in GW-BASIC, rest is discarded if len(name) > 41: name = name[:40] + name[-1] return name
def get_var_name(ins, allow_empty=False): """ Get variable name from token stream. """ name = '' d = skip_white_read(ins).upper() if not d: pass elif d not in string.ascii_uppercase: # variable name must start with a letter ins.seek(-len(d), 1) else: while d and d in name_chars: name += d d = ins.read(1).upper() if d in '$%!#': name += d else: ins.seek(-len(d), 1) if not name and not allow_empty: raise error.RunError(error.STX) # append type specifier name = vartypes.complete_name(name) # only the first 40 chars are relevant in GW-BASIC, rest is discarded if len(name) > 41: name = name[:40]+name[-1] return name
def dim_array(name, dimensions): """ Allocate array space for an array of given dimensioned size. Raise errors if duplicate name or illegal index value. """ if state.basic_state.array_base == None: state.basic_state.array_base = 0 name = vartypes.complete_name(name) if name in state.basic_state.arrays: # duplicate definition raise error.RunError(10) for d in dimensions: if d < 0: # illegal function call raise error.RunError(5) elif d < state.basic_state.array_base: # subscript out of range raise error.RunError(9) size = array_len(dimensions) try: state.basic_state.arrays[name] = [ dimensions, bytearray(size*var_size_bytes(name)), 0 ] except OverflowError: # out of memory raise error.RunError(7) except MemoryError: # out of memory raise error.RunError(7) # update memory model # first two bytes: chars of name or 0 if name is one byte long name_ptr = state.basic_state.array_current record_len = 1 + max(3, len(name)) + 3 + 2*len(dimensions) array_ptr = name_ptr + record_len state.basic_state.array_current += record_len + array_size_bytes(name) state.basic_state.array_memory[name] = (name_ptr, array_ptr)
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 dim_array(name, dimensions): """ Allocate array space for an array of given dimensioned size. Raise errors if duplicate name or illegal index value. """ if state.basic_state.array_base is None: state.basic_state.array_base = 0 name = vartypes.complete_name(name) if name in state.basic_state.arrays: raise error.RunError(error.DUPLICATE_DEFINITION) for d in dimensions: if d < 0: raise error.RunError(error.IFC) elif d < state.basic_state.array_base: raise error.RunError(error.SUBSCRIPT_OUT_OF_RANGE) size = array_len(dimensions) # update memory model # first two bytes: chars of name or 0 if name is one byte long name_ptr = state.basic_state.array_current record_len = 1 + max(3, len(name)) + 3 + 2*len(dimensions) array_ptr = name_ptr + record_len array_bytes = size*var_size_bytes(name) check_free_memory(record_len + array_bytes, error.OUT_OF_MEMORY) state.basic_state.array_current += record_len + array_bytes state.basic_state.array_memory[name] = (name_ptr, array_ptr) try: state.basic_state.arrays[name] = [ dimensions, bytearray(array_bytes), 0 ] except OverflowError: # out of memory raise error.RunError(error.OUT_OF_MEMORY) except MemoryError: # out of memory raise error.RunError(error.OUT_OF_MEMORY)
def set_scalar(name, value=None): """ Assign a value to a variable. """ name = vartypes.complete_name(name) type_char = name[-1] if value is not None: value = vartypes.pass_type(type_char, value) # update memory model # check if garbage needs collecting before allocating memory if name not in state.basic_state.var_memory: # don't add string length, string already stored size = (max(3, len(name)) + 1 + vartypes.byte_size[type_char]) check_free_memory(size, error.OUT_OF_MEMORY) # first two bytes: chars of name or 0 if name is one byte long name_ptr = state.basic_state.var_current # byte_size first_letter second_letter_or_nul remaining_length_or_nul var_ptr = name_ptr + max(3, len(name)) + 1 state.basic_state.var_current += max(3, len(name)) + 1 + vartypes.byte_size[name[-1]] state.basic_state.var_memory[name] = (name_ptr, var_ptr) # don't change the value if just checking allocation if value is None: if name in state.basic_state.variables: return else: value = vartypes.null(type_char) # copy buffers try: # in-place copy is crucial for FOR state.basic_state.variables[name][:] = value[1][:] except KeyError: # copy into new buffer if not existing state.basic_state.variables[name] = value[1][:]
def get_scalar(name): """ Retrieve the value of a scalar variable. """ name = vartypes.complete_name(name) try: return (name[-1], state.basic_state.variables[name]) except KeyError: return vartypes.null(name[-1])
def get_var(name): """ Retrieve the value of a variable. """ name = vartypes.complete_name(name) try: if name[-1] == '$': return get_string_copy_packed(state.basic_state.variables[name]) else: return (name[-1], state.basic_state.variables[name]) except KeyError: return vartypes.null[name[-1]]
def varptr(name, indices): """Get address of variable. """ name = vartypes.complete_name(name) if indices == []: try: _, var_ptr = state.basic_state.var_memory[name] return var_ptr except KeyError: return -1 else: try: dimensions, _, _ = state.basic_state.arrays[name] _, array_ptr = state.basic_state.array_memory[name] # arrays are kept at the end of the var list return state.basic_state.var_current + array_ptr + var.var_size_bytes(name) * var.index_array(indices, dimensions) except KeyError: return -1
def varptr(name, indices): """Get address of variable. """ name = vartypes.complete_name(name) if indices == []: try: _, var_ptr = state.basic_state.var_memory[name] return var_ptr except KeyError: return -1 else: try: dimensions, _, _ = state.basic_state.arrays[name] _, array_ptr = state.basic_state.array_memory[name] # arrays are kept at the end of the var list return state.basic_state.var_current + array_ptr + var.var_size_bytes( name) * var.index_array(indices, dimensions) except KeyError: return -1
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)