Exemple #1
0
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()))
Exemple #2
0
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())
Exemple #3
0
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())
Exemple #4
0
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()))
Exemple #5
0
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())
Exemple #6
0
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())
Exemple #7
0
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)
Exemple #8
0
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)
Exemple #9
0
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)
Exemple #10
0
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)
Exemple #11
0
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)
Exemple #12
0
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
Exemple #13
0
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
Exemple #14
0
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)   
Exemple #16
0
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
Exemple #17
0
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)
Exemple #18
0
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
Exemple #19
0
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)
Exemple #20
0
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
Exemple #21
0
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
Exemple #22
0
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
Exemple #23
0
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
Exemple #24
0
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)
Exemple #25
0
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)
Exemple #26
0
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)
Exemple #27
0
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
Exemple #28
0
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)
Exemple #29
0
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
Exemple #30
0
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
Exemple #31
0
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)