def value_lof(ins): """ LOF: get length of file. """ util.skip_white(ins) num = vartypes.pass_int_unpack(parse_bracket(ins), maxint=0xffff) util.range_check(0, 255, num) the_file = devices.get_file(num) return fp.pack(fp.Single.from_int(the_file.lof()))
def value_loc(ins): """ LOC: get file pointer. """ util.skip_white(ins) num = vartypes.pass_int_unpack(parse_bracket(ins), maxint=0xffff) util.range_check(0, 255, num) the_file = devices.get_file(num) return vartypes.pack_int(the_file.loc())
def value_eof(ins): """ EOF: get end-of-file. """ util.skip_white(ins) num = vartypes.pass_int_unpack(parse_bracket(ins), maxint=0xffff) if num == 0: return vartypes.null('%') util.range_check(0, 255, num) the_file = devices.get_file(num, 'IR') return vartypes.bool_to_integer(the_file.eof())
def value_rnd(ins): """ RND: get pseudorandom value. """ if util.skip_white(ins) == '(': return rnd.get_random( fp.unpack(vartypes.pass_single(parse_bracket(ins)))) else: return rnd.get_random_int(1)
def parse_expression(ins, allow_empty=False, empty_err=error.MISSING_OPERAND): """ Compute the value of the expression at the current code pointer. """ units, operators = [], [] while True: d = util.skip_white(ins) if d in tk.end_expression: break units.append(parse_expr_unit(ins)) d = util.skip_white(ins) # string lit breaks expression, number after string lit breaks expression, + or - doesnt (could be an operator... if d not in operator_tokens: break else: ins.read(1) if d in (tk.O_LT, tk.O_EQ, tk.O_GT): nxt = util.skip_white(ins) if nxt in (tk.O_LT, tk.O_EQ, tk.O_GT): ins.read(1) if d == nxt: raise error.RunError(error.STX) else: d += nxt if d[0] == tk.O_EQ: # =>, =< d = d[1] + d[0] elif d == tk.O_GT + tk.O_LT: # >< d = tk.O_LT + tk.O_GT operators.append(d) # empty expression is a syntax error (inside brackets) or Missing Operand (in an assignment) or ok (in print) # PRINT 1+ :err 22 # Print (1+) :err 2 # print 1+) :err 2 if len(units) == 0: if allow_empty: return None else: if d in (')', ']'): ins.read(1) # for positioning of cursor in edit gadget # always 22 here now that the bracket is taken out? raise error.RunError(error.STX if d in (')', ']') else empty_err) if None in units: raise error.RunError(error.STX) if len(units) <= len(operators): if d in (')', ']'): ins.read(1) raise error.RunError(error.STX if d in (')', ']') else error.MISSING_OPERAND) return parse_operators(operators, units)
def parse_expression(ins, allow_empty=False, empty_err=error.MISSING_OPERAND): """ Compute the value of the expression at the current code pointer. """ units, operators = [], [] while True: d = util.skip_white(ins) if d in tk.end_expression: break units.append(parse_expr_unit(ins)) d = util.skip_white(ins) # string lit breaks expression, number after string lit breaks expression, + or - doesnt (could be an operator... if d not in operator_tokens: break else: ins.read(1) if d in (tk.O_LT, tk.O_EQ, tk.O_GT): nxt = util.skip_white(ins) if nxt in (tk.O_LT, tk.O_EQ, tk.O_GT): ins.read(1) if d == nxt: raise error.RunError(error.STX) else: d += nxt if d[0] == tk.O_EQ: # =>, =< d = d[1] + d[0] elif d == tk.O_GT + tk.O_LT: # >< d = tk.O_LT + tk.O_GT operators.append(d) # empty expression is a syntax error (inside brackets) or Missing Operand (in an assignment) or ok (in print) # PRINT 1+ :err 22 # Print (1+) :err 2 # print 1+) :err 2 if len(units) == 0: if allow_empty: return None else: if d in (')', ']'): ins.read(1) # for positioning of cursor in edit gadget # always 22 here now that the bracket is taken out? raise error.RunError(error.STX if d in (')', ']') else empty_err) if len(units) <= len(operators): if d in (')', ']'): ins.read(1) raise error.RunError(error.STX if d in ( ')', ']') else error.MISSING_OPERAND) return parse_operators(operators, units)
def merge(g): """ Merge program from ascii or utf8 (if utf8_files is True) stream. """ while True: line = g.read_line() if line is None: break linebuf = tokenise.tokenise_line(line) if linebuf.read(1) == '\0': # line starts with a number, add to program memory; store_line seeks to 1 first store_line(linebuf) else: # we have read the : if util.skip_white(linebuf) not in tk.end_line: raise error.RunError(error.DIRECT_STATEMENT_IN_FILE)
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 merge(g): """ Merge program from ascii or utf8 (if utf8_files is True) stream. """ while True: line = g.read_line() if line is None: break #line, first_char = first_char + line, '' linebuf = tokenise.tokenise_line(line) if linebuf.read(1) == '\0': # line starts with a number, add to program memory; store_line seeks to 1 first store_line(linebuf) else: # we have read the : if util.skip_white(linebuf) not in util.end_line: # direct statement in file raise error.RunError(66)
def load_ascii_file(g, first_char=''): """ Load an ascii-codepage or utf8 (if utf8_files is True) encoded program from g. """ eof = False lg = LineGetter(g, universal_newline, utf8=utf8_files) while not eof: line, eof = lg.get_line() line, first_char = first_char + line, '' linebuf = tokenise.tokenise_line(line) if linebuf.read(1) == '\0': # line starts with a number, add to program memory; store_line seeks to 1 first store_line(linebuf) else: # we have read the : if util.skip_white(linebuf) not in util.end_line: # direct statement in file raise error.RunError(66)
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_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$',)) util.require_read(ins, ('(',)) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = get_var_or_array_name(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')',)) if var_ptr < 0: raise error.RunError(error.IFC) if dollar: return vartypes.pack_string(bytearray(chr(var.byte_size[name[-1]])) + vartypes.value_to_uint(var_ptr)) else: return vartypes.pack_int(var_ptr)
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$',)) util.require_read(ins, ('(',)) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = parse_variable(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')',)) if var_ptr < 0: raise error.RunError(error.IFC) var_ptr = vartypes.int_to_integer_unsigned(var_ptr) if dollar: return state.basic_state.strings.store(chr(vartypes.byte_size[name[-1]]) + vartypes.integer_to_bytes(var_ptr)) else: return var_ptr
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$', )) util.require_read(ins, ('(', )) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = get_var_or_array_name(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')', )) if var_ptr < 0: raise error.RunError(error.IFC) if dollar: return vartypes.pack_string( bytearray(chr(var.byte_size[name[-1]])) + vartypes.value_to_uint(var_ptr)) else: return vartypes.pack_int(var_ptr)
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 value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$', )) util.require_read(ins, ('(', )) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = parse_variable(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')', )) if var_ptr < 0: raise error.RunError(error.IFC) var_ptr = vartypes.int_to_integer_unsigned(var_ptr) if dollar: return state.basic_state.strings.store( chr(vartypes.byte_size[name[-1]]) + vartypes.integer_to_bytes(var_ptr)) else: return var_ptr
def read_entry(): """ READ a unit of DATA. """ current = state.basic_state.bytecode.tell() state.basic_state.bytecode.seek(state.basic_state.data_pos) if util.peek(state.basic_state.bytecode) in tk.end_statement: # initialise - find first DATA util.skip_to(state.basic_state.bytecode, ('\x84', )) # DATA if state.basic_state.bytecode.read(1) not in ('\x84', ','): raise error.RunError(error.OUT_OF_DATA) vals, word, literal = '', '', False while True: # read next char; omit leading whitespace if not literal and vals == '': c = util.skip_white(state.basic_state.bytecode) else: c = util.peek(state.basic_state.bytecode) # parse char if c == '' or (not literal and c == ',') or ( c in tk.end_line or (not literal and c in tk.end_statement)): break elif c == '"': state.basic_state.bytecode.read(1) literal = not literal if not literal: util.require(state.basic_state.bytecode, tk.end_statement + (',', )) else: state.basic_state.bytecode.read(1) if literal: vals += c else: word += c # omit trailing whitespace if c not in tk.whitespace: vals += word word = '' state.basic_state.data_pos = state.basic_state.bytecode.tell() state.basic_state.bytecode.seek(current) return vals
def read_entry(): """ READ a unit of DATA. """ current = state.basic_state.bytecode.tell() state.basic_state.bytecode.seek(state.basic_state.data_pos) if util.peek(state.basic_state.bytecode) in util.end_statement: # initialise - find first DATA util.skip_to(state.basic_state.bytecode, ('\x84',)) # DATA if state.basic_state.bytecode.read(1) not in ('\x84', ','): # out of DATA raise error.RunError(4) vals, word, literal = '', '', False while True: # read next char; omit leading whitespace if not literal and vals == '': c = util.skip_white(state.basic_state.bytecode) else: c = util.peek(state.basic_state.bytecode) # parse char if c == '' or (not literal and c == ',') or (c in util.end_line or (not literal and c in util.end_statement)): break elif c == '"': state.basic_state.bytecode.read(1) literal = not literal if not literal: util.require(state.basic_state.bytecode, util.end_statement+(',',)) else: state.basic_state.bytecode.read(1) if literal: vals += c else: word += c # omit trailing whitespace if c not in util.whitespace: vals += word word = '' state.basic_state.data_pos = state.basic_state.bytecode.tell() state.basic_state.bytecode.seek(current) return vals
def parse_literal(ins): """ Compute the value of the literal at the current code pointer. """ d = util.skip_white(ins) # string literal if d == '"': ins.read(1) if ins == state.basic_state.bytecode: address = ins.tell() + memory.code_start else: address = None output = bytearray() # while tokenised numbers inside a string literal will be printed as tokenised numbers, they don't actually execute as such: # a \00 character, even if inside a tokenised number, will break a string literal (and make the parser expect a # line number afterwards, etc. We follow this. d = ins.read(1) while d not in tk.end_line + ('"', ): output += d d = ins.read(1) if d == '\0': ins.seek(-1, 1) # store for easy retrieval, but don't reserve space in string memory return state.basic_state.strings.store(output, address) # number literals as ASCII are accepted in tokenised streams. only if they start with a figure (not & or .) # this happens e.g. after non-keywords like AS. They are not acceptable as line numbers. elif d in string.digits: outs = StringIO() representation.tokenise_number(ins, outs) outs.seek(0) return representation.parse_value(outs) # number literals elif d in tk.number: return representation.parse_value(ins) # gw-basic allows adding line numbers to numbers elif d == tk.T_UINT: return vartypes.int_to_integer_unsigned(util.parse_jumpnum(ins)) else: raise error.RunError(error.STX)
def parse_literal(ins): """ Compute the value of the literal at the current code pointer. """ d = util.skip_white(ins) # string literal if d == '"': ins.read(1) if ins == state.basic_state.bytecode: address = ins.tell() + memory.code_start else: address = None output = bytearray() # while tokenised numbers inside a string literal will be printed as tokenised numbers, they don't actually execute as such: # a \00 character, even if inside a tokenised number, will break a string literal (and make the parser expect a # line number afterwards, etc. We follow this. d = ins.read(1) while d not in tk.end_line + ('"',): output += d d = ins.read(1) if d == '\0': ins.seek(-1, 1) # store for easy retrieval, but don't reserve space in string memory return state.basic_state.strings.store(output, address) # number literals as ASCII are accepted in tokenised streams. only if they start with a figure (not & or .) # this happens e.g. after non-keywords like AS. They are not acceptable as line numbers. elif d in string.digits: outs = StringIO() representation.tokenise_number(ins, outs) outs.seek(0) return representation.parse_value(outs) # number literals elif d in tk.number: return representation.parse_value(ins) # gw-basic allows adding line numbers to numbers elif d == tk.T_UINT: return vartypes.int_to_integer_unsigned(util.parse_jumpnum(ins)) else: raise error.RunError(error.STX)
def parse_expression(ins, allow_empty=False): """ Compute the value of the expression at the current code pointer. """ stack = deque() units = deque() d = '' missing_error = error.MISSING_OPERAND # see https://en.wikipedia.org/wiki/Shunting-yard_algorithm while True: last = d d = util.skip_white(ins) # two-byte function tokens if d in tk.twobyte: d = util.peek(ins, n=2) if d == tk.NOT and not (last in operators or last == ''): # unary NOT ends expression except after another operator or at start break elif d in operators: ins.read(len(d)) # get combined operators such as >= if d in combinable: nxt = util.skip_white(ins) if nxt in combinable: d += ins.read(len(nxt)) if last in operators or last == '' or d == tk.NOT: # also if last is ( but that leads to recursive call and last == '' nargs = 1 # zero operands for a binary operator is always syntax error # because it will be seen as an illegal unary if d not in unary: raise error.RunError(error.STX) else: nargs = 2 _evaluate_stack(stack, units, operators[d], error.STX) stack.append((d, nargs)) elif not (last in operators or last == ''): # repeated unit ends expression # repeated literals or variables or non-keywords like 'AS' break elif d == '(': units.append(parse_bracket(ins)) elif d and d in string.ascii_letters: # variable name name, indices = parse_variable(ins) units.append(var.get_variable(name, indices)) elif d in functions: # apply functions ins.read(len(d)) try: units.append(functions[d](ins)) except (ValueError, ArithmeticError) as e: units.append(_handle_math_error(e)) elif d in tk.end_statement: break elif d in tk.end_expression or d in tk.keyword: # missing operand inside brackets or before comma is syntax error missing_error = error.STX break else: # literal units.append(parse_literal(ins)) # empty expression is a syntax error (inside brackets) # or Missing Operand (in an assignment) # or not an error (in print and many functions) if units or stack: _evaluate_stack(stack, units, 0, missing_error) return units[0] elif allow_empty: return None else: raise error.RunError(missing_error)
def parse_expr_unit(ins): """ Compute the value of the expression unit at the current code pointer. """ d = util.skip_white(ins) # string literal if d == '"': ins.read(1) output = bytearray() # while tokenised nmbers inside a string literal will be printed as tokenised numbers, they don't actually execute as such: # a \00 character, even if inside a tokenised number, will break a string literal (and make the parser expect a # line number afterwards, etc. We follow this. d = ins.read(1) while d not in tk.end_line + ('"', ): output += d d = ins.read(1) if d == '\0': ins.seek(-1, 1) return vartypes.pack_string(output) # variable name elif d in string.ascii_uppercase: name, indices = get_var_or_array_name(ins) return var.get_var_or_array(name, indices) # number literals as ASCII are accepted in tokenised streams. only if they start with a figure (not & or .) # this happens e.g. after non-keywords like AS. They are not acceptable as line numbers. elif d in string.digits: outs = StringIO() representation.tokenise_number(ins, outs) outs.seek(0) return representation.parse_value(outs) # number literals elif d in tk.number: return representation.parse_value(ins) # gw-basic allows adding line numbers to numbers elif d == tk.T_UINT: return vartypes.pack_int(util.parse_jumpnum(ins)) # brackets elif d == '(': return parse_bracket(ins) # single-byte tokens else: ins.read(1) if d == tk.INPUT: return value_input(ins) elif d == tk.SCREEN: return value_screen(ins) elif d == tk.USR: return value_usr(ins) elif d == tk.FN: return value_fn(ins) elif d == tk.NOT: return value_not(ins) elif d == tk.ERL: return value_erl(ins) elif d == tk.ERR: return value_err(ins) elif d == tk.STRING: return value_string(ins) elif d == tk.INSTR: return value_instr(ins) elif d == tk.VARPTR: return value_varptr(ins) elif d == tk.CSRLIN: return value_csrlin(ins) elif d == tk.POINT: return value_point(ins) elif d == tk.INKEY: return value_inkey(ins) elif d == tk.O_PLUS: return parse_expr_unit(ins) elif d == tk.O_MINUS: return value_neg(ins) # two-byte tokens elif d == '\xFD': d += ins.read(1) if d == tk.CVI: return value_cvi(ins) elif d == tk.CVS: return value_cvs(ins) elif d == tk.CVD: return value_cvd(ins) elif d == tk.MKI: return value_mki(ins) elif d == tk.MKS: return value_mks(ins) elif d == tk.MKD: return value_mkd(ins) elif d == tk.EXTERR: return value_exterr(ins) # two-byte tokens elif d == '\xFE': d += ins.read(1) if d == tk.DATE: return value_date(ins) elif d == tk.TIME: return value_time(ins) elif d == tk.PLAY: return value_play(ins) elif d == tk.TIMER: return value_timer(ins) elif d == tk.ERDEV: return value_erdev(ins) elif d == tk.IOCTL: return value_ioctl(ins) elif d == tk.ENVIRON: return value_environ(ins) elif d == tk.PMAP: return value_pmap(ins) # two-byte tokens elif d == '\xFF': d += ins.read(1) if d == tk.LEFT: return value_left(ins) elif d == tk.RIGHT: return value_right(ins) elif d == tk.MID: return value_mid(ins) elif d == tk.SGN: return value_sgn(ins) elif d == tk.INT: return value_int(ins) elif d == tk.ABS: return value_abs(ins) elif d == tk.SQR: return value_sqr(ins) elif d == tk.RND: return value_rnd(ins) elif d == tk.SIN: return value_sin(ins) elif d == tk.LOG: return value_log(ins) elif d == tk.EXP: return value_exp(ins) elif d == tk.COS: return value_cos(ins) elif d == tk.TAN: return value_tan(ins) elif d == tk.ATN: return value_atn(ins) elif d == tk.FRE: return value_fre(ins) elif d == tk.INP: return value_inp(ins) elif d == tk.POS: return value_pos(ins) elif d == tk.LEN: return value_len(ins) elif d == tk.STR: return value_str(ins) elif d == tk.VAL: return value_val(ins) elif d == tk.ASC: return value_asc(ins) elif d == tk.CHR: return value_chr(ins) elif d == tk.PEEK: return value_peek(ins) elif d == tk.SPACE: return value_space(ins) elif d == tk.OCT: return value_oct(ins) elif d == tk.HEX: return value_hex(ins) elif d == tk.LPOS: return value_lpos(ins) elif d == tk.CINT: return value_cint(ins) elif d == tk.CSNG: return value_csng(ins) elif d == tk.CDBL: return value_cdbl(ins) elif d == tk.FIX: return value_fix(ins) elif d == tk.PEN: return value_pen(ins) elif d == tk.STICK: return value_stick(ins) elif d == tk.STRIG: return value_strig(ins) elif d == tk.EOF: return value_eof(ins) elif d == tk.LOC: return value_loc(ins) elif d == tk.LOF: return value_lof(ins) else: return None
def value_rnd(ins): """ RND: get pseudorandom value. """ if util.skip_white(ins) == '(': return rnd.get_random(fp.unpack(vartypes.pass_single(parse_bracket(ins)))) else: return rnd.get_random_int(1)
def parse_expr_unit(ins): """ Compute the value of the expression unit at the current code pointer. """ d = util.skip_white(ins) # string literal if d == '"': ins.read(1) output = bytearray() # while tokenised nmbers inside a string literal will be printed as tokenised numbers, they don't actually execute as such: # a \00 character, even if inside a tokenised number, will break a string literal (and make the parser expect a # line number afterwards, etc. We follow this. d = ins.read(1) while d not in util.end_line + ('"',): output += d d = ins.read(1) if d == '\0': ins.seek(-1, 1) return vartypes.pack_string(output) # variable name elif d >= 'A' and d <= 'Z': name, indices = get_var_or_array_name(ins) return var.get_var_or_array(name, indices) # number literals as ASCII are accepted in tokenised streams. only if they start with a figure (not & or .) # this happens e.g. after non-keywords like AS. They are not acceptable as line numbers. elif d >= '0' and d <= '9': outs = StringIO() representation.tokenise_number(ins, outs) outs.seek(0) return util.parse_value(outs) # number literals elif d in token.number: return util.parse_value(ins) # gw-basic allows adding line numbers to numbers elif d == token.T_UINT: return vartypes.pack_int(util.parse_jumpnum(ins)) # brackets elif d == '(': return parse_bracket(ins) # single-byte tokens else: ins.read(1) if d == '\x85': return value_input(ins) elif d == '\xC8': return value_screen(ins) elif d == '\xD0': return value_usr(ins) elif d == '\xD1': return value_fn(ins) elif d == '\xD3': return value_not(ins) elif d == '\xD4': return value_erl(ins) elif d == '\xD5': return value_err(ins) elif d == '\xD6': return value_string(ins) elif d == '\xD8': return value_instr(ins) elif d == '\xDA': return value_varptr(ins) elif d == '\xDB': return value_csrlin(ins) elif d == '\xDC': return value_point(ins) elif d == '\xDE': return value_inkey(ins) elif d == '\xE9': return parse_expr_unit(ins) elif d == '\xEA': return value_neg(ins) # two-byte tokens elif d == '\xFD': d = ins.read(1) if d == '\x81': return value_cvi(ins) elif d =='\x82': return value_cvs(ins) elif d =='\x83': return value_cvd(ins) elif d =='\x84': return value_mki(ins) elif d =='\x85': return value_mks(ins) elif d =='\x86': return value_mkd(ins) elif d == '\x8b': return value_exterr(ins) # two-byte tokens elif d == '\xFE': d = ins.read(1) if d == '\x8D': return value_date(ins) elif d == '\x8E': return value_time(ins) elif d == '\x93': return value_play(ins) elif d == '\x94': return value_timer(ins) elif d == '\x95': return value_erdev(ins) elif d == '\x96': return value_ioctl(ins) elif d == '\x9B': return value_environ(ins) elif d == '\x9E': return value_pmap(ins) # two-byte tokens elif d == '\xFF': d = ins.read(1) if d == '\x81': return value_left(ins) elif d == '\x82': return value_right(ins) elif d == '\x83': return value_mid(ins) elif d == '\x84': return value_sgn(ins) elif d == '\x85': return value_int(ins) elif d == '\x86': return value_abs(ins) elif d == '\x87': return value_sqrt(ins) elif d == '\x88': return value_rnd(ins) elif d == '\x89': return value_sin(ins) elif d == '\x8a': return value_log(ins) elif d == '\x8b': return value_exp(ins) elif d == '\x8c': return value_cos(ins) elif d == '\x8D': return value_tan(ins) elif d == '\x8E': return value_atn(ins) elif d == '\x8F': return value_fre(ins) elif d == '\x90': return value_inp(ins) elif d == '\x91': return value_pos(ins) elif d == '\x92': return value_len(ins) elif d == '\x93': return value_str(ins) elif d == '\x94': return value_val(ins) elif d == '\x95': return value_asc(ins) elif d == '\x96': return value_chr(ins) elif d == '\x97': return value_peek(ins) elif d == '\x98': return value_space(ins) elif d == '\x99': return value_oct(ins) elif d == '\x9A': return value_hex(ins) elif d == '\x9B': return value_lpos(ins) elif d == '\x9C': return value_cint(ins) elif d == '\x9D': return value_csng(ins) elif d == '\x9E': return value_cdbl(ins) elif d == '\x9F': return value_fix(ins) elif d == '\xA0': return value_pen(ins) elif d == '\xA1': return value_stick(ins) elif d == '\xA2': return value_strig(ins) elif d == '\xA3': return value_eof(ins) elif d == '\xA4': return value_loc(ins) elif d == '\xA5': return value_lof(ins) else: return None
def parse_expr_unit(ins): """ Compute the value of the expression unit at the current code pointer. """ d = util.skip_white(ins) # string literal if d == '"': ins.read(1) output = bytearray() # while tokenised nmbers inside a string literal will be printed as tokenised numbers, they don't actually execute as such: # a \00 character, even if inside a tokenised number, will break a string literal (and make the parser expect a # line number afterwards, etc. We follow this. d = ins.read(1) while d not in tk.end_line + ('"',): output += d d = ins.read(1) if d == '\0': ins.seek(-1, 1) return vartypes.pack_string(output) # variable name elif d in string.ascii_uppercase: name, indices = get_var_or_array_name(ins) return var.get_var_or_array(name, indices) # number literals as ASCII are accepted in tokenised streams. only if they start with a figure (not & or .) # this happens e.g. after non-keywords like AS. They are not acceptable as line numbers. elif d in string.digits: outs = StringIO() representation.tokenise_number(ins, outs) outs.seek(0) return representation.parse_value(outs) # number literals elif d in tk.number: return representation.parse_value(ins) # gw-basic allows adding line numbers to numbers elif d == tk.T_UINT: return vartypes.pack_int(util.parse_jumpnum(ins)) # brackets elif d == '(': return parse_bracket(ins) # single-byte tokens else: ins.read(1) if d == tk.INPUT: return value_input(ins) elif d == tk.SCREEN: return value_screen(ins) elif d == tk.USR: return value_usr(ins) elif d == tk.FN: return value_fn(ins) elif d == tk.NOT: return value_not(ins) elif d == tk.ERL: return value_erl(ins) elif d == tk.ERR: return value_err(ins) elif d == tk.STRING: return value_string(ins) elif d == tk.INSTR: return value_instr(ins) elif d == tk.VARPTR: return value_varptr(ins) elif d == tk.CSRLIN: return value_csrlin(ins) elif d == tk.POINT: return value_point(ins) elif d == tk.INKEY: return value_inkey(ins) elif d == tk.O_PLUS: return parse_expr_unit(ins) elif d == tk.O_MINUS: return value_neg(ins) # two-byte tokens elif d == '\xFD': d += ins.read(1) if d == tk.CVI: return value_cvi(ins) elif d == tk.CVS: return value_cvs(ins) elif d == tk.CVD: return value_cvd(ins) elif d == tk.MKI: return value_mki(ins) elif d == tk.MKS: return value_mks(ins) elif d == tk.MKD: return value_mkd(ins) elif d == tk.EXTERR: return value_exterr(ins) # two-byte tokens elif d == '\xFE': d += ins.read(1) if d == tk.DATE: return value_date(ins) elif d == tk.TIME: return value_time(ins) elif d == tk.PLAY: return value_play(ins) elif d == tk.TIMER: return value_timer(ins) elif d == tk.ERDEV: return value_erdev(ins) elif d == tk.IOCTL: return value_ioctl(ins) elif d == tk.ENVIRON: return value_environ(ins) elif d == tk.PMAP: return value_pmap(ins) # two-byte tokens elif d == '\xFF': d += ins.read(1) if d == tk.LEFT: return value_left(ins) elif d == tk.RIGHT: return value_right(ins) elif d == tk.MID: return value_mid(ins) elif d == tk.SGN: return value_sgn(ins) elif d == tk.INT: return value_int(ins) elif d == tk.ABS: return value_abs(ins) elif d == tk.SQR: return value_sqr(ins) elif d == tk.RND: return value_rnd(ins) elif d == tk.SIN: return value_sin(ins) elif d == tk.LOG: return value_log(ins) elif d == tk.EXP: return value_exp(ins) elif d == tk.COS: return value_cos(ins) elif d == tk.TAN: return value_tan(ins) elif d == tk.ATN: return value_atn(ins) elif d == tk.FRE: return value_fre(ins) elif d == tk.INP: return value_inp(ins) elif d == tk.POS: return value_pos(ins) elif d == tk.LEN: return value_len(ins) elif d == tk.STR: return value_str(ins) elif d == tk.VAL: return value_val(ins) elif d == tk.ASC: return value_asc(ins) elif d == tk.CHR: return value_chr(ins) elif d == tk.PEEK: return value_peek(ins) elif d == tk.SPACE: return value_space(ins) elif d == tk.OCT: return value_oct(ins) elif d == tk.HEX: return value_hex(ins) elif d == tk.LPOS: return value_lpos(ins) elif d == tk.CINT: return value_cint(ins) elif d == tk.CSNG: return value_csng(ins) elif d == tk.CDBL: return value_cdbl(ins) elif d == tk.FIX: return value_fix(ins) elif d == tk.PEN: return value_pen(ins) elif d == tk.STICK: return value_stick(ins) elif d == tk.STRIG: return value_strig(ins) elif d == tk.EOF: return value_eof(ins) elif d == tk.LOC: return value_loc(ins) elif d == tk.LOF: return value_lof(ins) else: return None