def number_inc_gt(typechar, loopvar, stop, step, sgn): """ Increase number and check if it exceeds a limit. """ if sgn == 0: return False if typechar in ('#', '!'): fp_left = fp.from_bytes(loopvar).iadd(step) loopvar[:] = fp_left.to_bytes() return fp_left.gt(stop) if sgn > 0 else stop.gt(fp_left) else: int_left = vartypes.sint_to_value(loopvar) + step loopvar[:] = vartypes.value_to_sint(int_left) return int_left > stop if sgn > 0 else stop > int_left
def value_oct(ins): """ OCT$: octal representation of int. """ # allow range -32768 to 65535 val = vartypes.pass_int_unpack(parse_bracket(ins), 0xffff) return vartypes.pack_string(representation.oct_to_str(vartypes.value_to_sint(val))[2:])
def value_hex(ins): """ HEX$: hexadecimal representation of int. """ # allow range -32768 to 65535 val = vartypes.pass_int_unpack(parse_bracket(ins), 0xffff) return vartypes.pack_string(representation.hex_to_str(vartypes.value_to_sint(val))[2:])
def tokenise_dec(ins, outs): """ Convert decimal expression in Python string to number token. """ have_exp = False have_point = False word = '' kill = False while True: c = ins.read(1).upper() if not c: break elif c in '\x1c\x1d\x1f': # ASCII separator chars invariably lead to zero result kill = True elif c == '.' and not have_point and not have_exp: have_point = True word += c elif c in 'ED' and not have_exp: have_exp = True # there's a special exception for number followed by EL or EQ # presumably meant to protect ELSE and maybe EQV ? if c == 'E' and util.peek(ins).upper() in ('L', 'Q'): ins.seek(-1, 1) break else: word += c elif c in '-+' and (not word or word[-1] in 'ED'): # must be first token or in exponent word += c elif c in ascii_digits: word += c elif c in ascii_whitespace: # we'll remove this later but need to keep it for now # so we can reposition the stream on removing trailing whitespace word += c elif c in '!#' and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: ins.seek(-1, 1) break # ascii separators encountered: zero output if kill: word = '0' # don't claim trailing whitespace while len(word) > 0 and (word[-1] in ascii_whitespace): word = word[:-1] ins.seek(-1, 1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in ascii_whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in ascii_digits: # digit outs.write(chr(0x11 + int(word))) elif (not (have_exp or have_point or word[-1] in '!#') and int(word) <= 0x7fff and int(word) >= -0x8000): if int(word) <= 0xff and int(word) >= 0: # one-byte constant outs.write(tk.T_BYTE + chr(int(word))) else: # two-byte constant outs.write(tk.T_INT + str(vartypes.value_to_sint(int(word)))) else: mbf = str(str_to_float(word).to_bytes()) if len(mbf) == 4: outs.write(tk.T_SINGLE + mbf) else: outs.write(tk.T_DOUBLE + mbf)
def value_mki(ins): """ MKI$: return the byte representation of an int. """ return vartypes.pack_string(vartypes.value_to_sint(vartypes.pass_int_unpack(parse_bracket(ins))))
def tokenise_number(ins, outs): """ Convert Python-string number representation to number token. """ c = util.peek(ins) # handle hex or oct constants if c == '&': ins.read(1) nxt = util.peek(ins).upper() if nxt == 'H': # hex constant ins.read(1) word = '' while True: if not util.peek(ins).upper() in ascii_hexits: break else: word += ins.read(1).upper() val = int(word, 16) if word else 0 outs.write('\x0C' + str(vartypes.value_to_uint(val))) else: # nxt == 'O': # octal constant if nxt == 'O': ins.read(1) word = '' while True: if not util.peek(ins).upper() in ascii_octits: break else: word += ins.read(1).upper() val = int(word, 8) if word else 0 outs.write('\x0B' + str(vartypes.value_to_uint(val))) # handle other numbers # note GW passes signs separately as a token and only stores positive numbers in the program elif (c in ascii_digits or c=='.' or c in ('+','-')): have_exp = False have_point = False word = '' while True: c = ins.read(1).upper() if c == '.' and not have_point and not have_exp: have_point = True word += c elif c in ('E', 'D') and not have_exp: have_exp = True word += c elif c in ('-','+') and word=='': # must be first token word += c elif c in ('+', '-') and word[-1] in ('E', 'D'): word += c elif c in ascii_digits: # (c >='0' and numc <='9'): word += c elif c in whitespace: # we'll remove this later but need to keep it for now so we can reposition the stream on removing trainling whitespace word += c elif c in ('!', '#') and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: if c != '': ins.seek(-1,1) break # don't claim trailing whitespace, don't end in D or E while len(word)>0 and (word[-1] in whitespace + ('D', 'E')): if word[-1] in ('D', 'E'): have_exp = False word = word[:-1] ins.seek(-1,1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in ascii_digits: # digit outs.write(chr(0x11+int(word))) elif not (have_exp or have_point or word[-1] in ('!', '#')) and int(word) <= 0x7fff and int(word) >= -0x8000: if int(word) <= 0xff and int(word)>=0: # one-byte constant outs.write('\x0f'+chr(int(word))) else: # two-byte constant outs.write('\x1c'+str(vartypes.value_to_sint(int(word)))) else: mbf = str(from_str(word).to_bytes()) if len(mbf) == 4: # single outs.write('\x1d'+mbf) else: # double outs.write('\x1f'+mbf) elif c!='': ins.seek(-1,1)
def value_hex(ins): """ HEX$: hexadecimal representation of int. """ # allow range -32768 to 65535 val = vartypes.pass_int_unpack(parse_bracket(ins), 0xffff) return vartypes.pack_string( representation.hex_to_str(vartypes.value_to_sint(val))[2:])
def value_oct(ins): """ OCT$: octal representation of int. """ # allow range -32768 to 65535 val = vartypes.pass_int_unpack(parse_bracket(ins), 0xffff) return vartypes.pack_string( representation.oct_to_str(vartypes.value_to_sint(val))[2:])
def value_mki(ins): """ MKI$: return the byte representation of an int. """ return vartypes.pack_string( vartypes.value_to_sint(vartypes.pass_int_unpack(parse_bracket(ins))))
def tokenise_dec(ins, outs): """ Convert decimal expression in Python string to number token. """ have_exp = False have_point = False word = '' kill = False while True: c = ins.read(1).upper() if not c: break elif c in '\x1c\x1d\x1f': # ASCII separator chars invariably lead to zero result kill = True elif c == '.' and not have_point and not have_exp: have_point = True word += c elif c in 'ED' and not have_exp: # there's a special exception for number followed by EL or EQ # presumably meant to protect ELSE and maybe EQV ? if c == 'E' and util.peek(ins).upper() in ('L', 'Q'): ins.seek(-1, 1) break else: have_exp = True word += c elif c in '-+' and (not word or word[-1] in 'ED'): # must be first token or in exponent word += c elif c in string.digits: word += c elif c in number_whitespace: # we'll remove this later but need to keep it for now # so we can reposition the stream on removing trailing whitespace word += c elif c in '!#' and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: ins.seek(-1, 1) break # ascii separators encountered: zero output if kill: word = '0' # don't claim trailing whitespace while len(word)>0 and (word[-1] in number_whitespace): word = word[:-1] ins.seek(-1,1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in number_whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in string.digits: # digit outs.write(chr(0x11+int(word))) elif (not (have_exp or have_point or word[-1] in '!#') and int(word) <= 0x7fff and int(word) >= -0x8000): if int(word) <= 0xff and int(word)>=0: # one-byte constant outs.write(tk.T_BYTE + chr(int(word))) else: # two-byte constant outs.write(tk.T_INT + str(vartypes.value_to_sint(int(word)))) else: mbf = str(str_to_float(word).to_bytes()) if len(mbf) == 4: outs.write(tk.T_SINGLE + mbf) else: outs.write(tk.T_DOUBLE + mbf)