Beispiel #1
0
def check_pending_labels(ast):
    """ Iteratively traverses the node looking for ID with no class set,
    marks them as labels, and check they've been declared.

    This way we avoid stack overflow for high line-numbered listings.
    """
    result = True
    visited = set()
    pending = [ast]

    while pending:
        node = pending.pop()

        if node is None or node in visited:  # Avoid recursive infinite-loop
            continue

        visited.add(node)
        for x in node.children:
            pending.append(x)

        if node.token != 'VAR' or (node.token == 'VAR' and node.class_ is not CLASS.unknown):
            continue

        tmp = global_.SYMBOL_TABLE.get_entry(node.name)
        if tmp is None or tmp.class_ is CLASS.unknown:
            errmsg.error(node.lineno, f'Undeclared identifier "{node.name}"')
        else:
            assert tmp.class_ == CLASS.label
            node.to_label(node)

        result = result and tmp is not None

    return result
Beispiel #2
0
    def argval(self):
        """ Solve args values or raise errors if not
        defined yet
        """
        if gl.has_errors:
            return [None]

        if self.asm in ('DEFB', 'DEFS', 'DEFW'):
            result = tuple(
                [x.eval() if isinstance(x, Expr) else x for x in self.arg])
            if self.asm == 'DEFB' and any(x > 255 for x in result):
                errmsg.warning_value_will_be_truncated(self.lineno)
            return result

        self.arg = tuple(
            [x if not isinstance(x, Expr) else x.eval() for x in self.arg])
        if gl.has_errors:
            return [None]

        if self.asm.split(' ')[0] in ('JR', 'DJNZ'):  # A relative jump?
            if self.arg[0] < -128 or self.arg[0] > 127:
                error(self.lineno, 'Relative jump out of range')
                return [None]

        return super(Asm, self).argval()
Beispiel #3
0
    def exit_proc(self, lineno: int):
        """ Exits current procedure. Local labels are transferred to global
        scope unless they have been marked as local ones.

        Raises an error if no current local context (stack underflow)
        """
        __DEBUG__('Exiting current scope from lineno %i' % lineno)

        if len(self.local_labels) <= 1:
            error(lineno, 'ENDP in global scope (with no PROC)')
            return

        for label in self.local_labels[-1].values():
            if label.local:
                if not label.defined:
                    error(lineno, "Undefined LOCAL label '%s'" % label.name)
                    return
                continue

            name = label.name
            _lineno = label.lineno
            value = label.value

            if name not in self.global_labels.keys():
                self.global_labels[name] = label
            else:
                self.global_labels[name].define(value, _lineno)

        self.local_labels.pop()  # Removes current context
        self.scopes.pop()
Beispiel #4
0
    def __init__(self, lineno, asm, arg=None):
        self.lineno = lineno

        if asm not in ('DEFB', 'DEFS', 'DEFW'):
            try:
                super(Asm, self).__init__(asm, arg)
            except Error as v:
                error(lineno, v.msg)
                return

            self.pending = len([
                x for x in self.arg
                if isinstance(x, Expr) and x.try_eval() is None
            ]) > 0

            if not self.pending:
                self.arg = self.argval()
        else:
            self.asm = asm
            self.pending = True

            if isinstance(arg, str):
                self.arg = tuple(
                    [Expr(Container(ord(x), lineno)) for x in arg])
            else:
                self.arg = arg

            self.arg_num = len(self.arg)
Beispiel #5
0
def p_asm_ld8(p):
    """ asm : LD reg8 COMMA reg8_hl
            | LD reg8_hl COMMA reg8
            | LD reg8 COMMA reg8
            | LD SP COMMA HL
            | LD SP COMMA reg16i
            | LD A COMMA reg8
            | LD reg8 COMMA A
            | LD reg8_hl COMMA A
            | LD A COMMA reg8_hl
            | LD A COMMA A
            | LD A COMMA I
            | LD I COMMA A
            | LD A COMMA R
            | LD R COMMA A
            | LD A COMMA reg8i
            | LD reg8i COMMA A
            | LD reg8 COMMA reg8i
            | LD reg8i COMMA regBCDE
            | LD reg8i COMMA reg8i
    """
    if p[2] in ('H', 'L') and p[4] in ('IXH', 'IXL', 'IYH', 'IYL'):
        p[0] = None
        error(p.lineno(0), "Unexpected token '%s'" % p[4])
    else:
        p[0] = Asm(p.lineno(1), 'LD %s,%s' % (p[2], p[4]))
Beispiel #6
0
    def make_node(lower, upper, lineno):
        """ Creates an array bound
        """
        if not check.is_static(lower, upper):
            error(lineno, 'Array bounds must be constants')
            return None

        if isinstance(lower, SymbolVAR):
            lower = lower.value
            if lower is None:  # semantic error
                error(lineno, "Unknown lower bound for array dimension")
                return

        if isinstance(upper, SymbolVAR):
            upper = upper.value
            if upper is None:  # semantic error
                error(lineno, "Unknown upper bound for array dimension")
                return

        lower.value = int(lower.value)
        upper.value = int(upper.value)

        if lower.value < 0:
            error(lineno, 'Array bounds must be greater than 0')
            return None

        if lower.value > upper.value:
            error(lineno,
                  'Lower array bound must be less or equal to upper one')
            return None

        return SymbolBOUND(lower.value, upper.value)
Beispiel #7
0
def t_MACROS(t):
    r'__[a-zA-Z]+__'

    if t.value in macros:
        t.type = t.value
        return t

    error(t.lexer.lineno, "unknown macro '%s'" % t.value)
Beispiel #8
0
    def __set_byte(self, byte: int, lineno: int):
        """ Sets a byte at the current location,
        and increments org in one. Raises an error if org > MAX_MEMORY
        """
        if byte < 0 or byte > 255:
            error(lineno, 'Invalid byte value %i' % byte)

        self.memory_bytes[self.org] = byte
        self.index += 1  # Increment current memory pointer
Beispiel #9
0
def p_im(p):
    """ asm : IM expr
    """
    val = p[2].eval()
    if val not in (0, 1, 2):
        error(p.lineno(1), 'Invalid IM number %i' % val)
        p[0] = None
        return

    p[0] = Asm(p.lineno(1), 'IM %i' % val)
Beispiel #10
0
    def resolve(self, lineno):
        """ Evaluates label value. Exits with error (unresolved) if value is none
        """
        if not self.defined:
            error(lineno, "Undeclared label '%s'" % self.name)

        if isinstance(self.value, Expr):
            return self.value.eval()

        return self.value
Beispiel #11
0
    def set_org(self, value: int, lineno: int):
        """ Sets a new ORG value
        """
        if value < 0 or value > MAX_MEM:
            error(
                lineno,
                "Memory ORG out of range [0 .. 65535]. Current value: %i" %
                value)

        self.index = self.ORG = value
Beispiel #12
0
def p_DEFS(p):  # Define bytes
    """ asm : DEFS number_list
    """
    if len(p[2]) > 2:
        error(p.lineno(1), "too many arguments for DEFS")

    if len(p[2]) < 2:
        num = Expr.makenode(Container(0, p.lineno(1)))  # Defaults to 0
        p[2] = p[2] + (num, )

    p[0] = Asm(p.lineno(1), 'DEFS', p[2])
Beispiel #13
0
def p_align(p):
    """ asm : ALIGN expr
            | ALIGN pexpr
    """
    align = p[2].eval()
    if align < 2:
        error(p.lineno(1), "ALIGN value must be greater than 1")
        return

    MEMORY.set_org(MEMORY.org + (align - MEMORY.org % align) % align,
                   p.lineno(1))
Beispiel #14
0
def p_BIT_ix(p):
    """ asm : bitop expr COMMA reg8_I
            | bitop pexpr COMMA reg8_I
    """
    bit = p[2].eval()
    if bit < 0 or bit > 7:
        error(p.lineno(3), 'Invalid bit position %i. Must be in [0..7]' % bit)
        p[0] = None
        return

    p[0] = Asm(p.lineno(3), '%s %i,%s' % (p[1], bit, p[4][0]), p[4][1])
Beispiel #15
0
    def define(self, value, lineno, namespace=None):
        """ Defines label value. It can be anything. Even an AST
        """
        if self.defined:
            error(
                lineno, "label '%s' already defined at line %i" %
                (self.name, self.lineno))

        self.value = value
        self.lineno = lineno
        self.namespace = NAMESPACE if namespace is None else namespace
Beispiel #16
0
def p_rst(p):
    """ asm : RST expr
    """
    val = p[2].eval()

    if val not in (0, 8, 16, 24, 32, 40, 48, 56):
        error(p.lineno(1), 'Invalid RST number %i' % val)
        p[0] = None
        return

    p[0] = Asm(p.lineno(1), 'RST %XH' % val)
Beispiel #17
0
def p_pop_namespace(p):
    """ asm : POP NAMESPACE
    """
    global NAMESPACE

    if not NAMESPACE_STACK:
        error(
            p.lineno(2),
            f"Stack underflow. No more Namespaces to pop. Current namespace is {NAMESPACE}"
        )
    else:
        NAMESPACE = NAMESPACE_STACK.pop()
Beispiel #18
0
def p_error(p):
    if p is not None:
        if p.type != 'NEWLINE':
            error(
                p.lineno,
                "Syntax error. Unexpected token '%s' [%s]" % (p.value, p.type))
        else:
            error(p.lineno, "Syntax error. Unexpected end of line [NEWLINE]")
    else:
        OPTIONS.stderr.write(
            "General syntax error at assembler (unexpected End of File?)")
        gl.has_errors += 1
Beispiel #19
0
    def try_eval(self):
        """ Recursively evals the node. Returns None
        if it is still unresolved.
        """
        item = self.symbol.item

        if isinstance(item, int):
            return item

        if isinstance(item, Label):
            if item.defined:
                if isinstance(item.value, Expr):
                    return item.value.try_eval()
                else:
                    return item.value
            else:
                if Expr.ignore:
                    return None

                # Try to resolve into the global namespace
                error(self.symbol.lineno, "Undefined label '%s'" % item.name)
                return None

        try:
            if isinstance(item, tuple):
                return tuple([x.try_eval() for x in item])

            if isinstance(item, list):
                return [x.try_eval() for x in item]

            if item == '-' and len(self.children) == 1:
                return -self.left.try_eval()

            if item == '+' and len(self.children) == 1:
                return self.left.try_eval()

            try:
                return self.funct[item](self.left.try_eval(),
                                        self.right.try_eval())
            except ZeroDivisionError:
                error(self.symbol.lineno, 'Division by 0')
            except KeyError:
                pass

        except TypeError:
            pass

        return None
Beispiel #20
0
def p_incbin(p):
    """ asm : INCBIN STRING
    """
    try:
        fname = zxbpp.search_filename(p[2], p.lineno(2), local_first=True)
        if not fname:
            p[0] = None
            return
        with src.api.utils.open_file(fname, 'rb') as f:
            filecontent = f.read()
    except IOError:
        error(p.lineno(2), "cannot read file '%s'" % p[2])
        p[0] = None
        return

    p[0] = Asm(p.lineno(1), 'DEFB', filecontent)
Beispiel #21
0
def check_call_arguments(lineno: int, id_: str, args):
    """ Check arguments against function signature.

        Checks every argument in a function call against a function.
        Returns True on success.
    """
    if not global_.SYMBOL_TABLE.check_is_declared(id_, lineno, 'function'):
        return False

    if not global_.SYMBOL_TABLE.check_class(id_, CLASS.function, lineno):
        return False

    entry = global_.SYMBOL_TABLE.get_entry(id_)

    if len(args) != len(entry.params):
        c = 's' if len(entry.params) != 1 else ''
        errmsg.error(lineno, "Function '%s' takes %i parameter%s, not %i" %
                     (id_, len(entry.params), c, len(args)))
        return False

    for arg, param in zip(args, entry.params):
        if arg.class_ in (CLASS.var, CLASS.array) and param.class_ != arg.class_:
            errmsg.error(lineno, "Invalid argument '{}'".format(arg.value))
            return None

        if not arg.typecast(param.type_):
            return False

        if param.byref:
            if not isinstance(arg.value, symbols.VAR):
                errmsg.error(lineno, "Expected a variable name, not an expression (parameter By Reference)")
                return False

            if arg.class_ not in (CLASS.var, CLASS.array):
                errmsg.error(lineno, "Expected a variable or array name (parameter By Reference)")
                return False

            arg.byref = True

        if arg.value is not None:
            arg.value.add_required_symbol(param)

    if entry.forwarded:  # The function / sub was DECLARED but not implemented
        errmsg.error(lineno, "%s '%s' declared but not implemented" % (CLASS.to_string(entry.class_), entry.name))
        return False

    return True
Beispiel #22
0
def check_and_make_label(lbl: Union[str, int, float], lineno):
    """ Checks if the given label (or line number) is valid and, if so,
    returns a label object.
    :param lbl: Line number of label (string)
    :param lineno: Line number in the basic source code for error reporting
    :return: Label object or None if error.
    """
    if isinstance(lbl, float):
        if lbl == int(lbl):
            id_ = str(int(lbl))
        else:
            errmsg.error(lineno, 'Line numbers must be integers.')
            return None
    else:
        id_ = lbl

    return global_.SYMBOL_TABLE.access_label(id_, lineno)
Beispiel #23
0
def check_type(lineno, type_list, arg):
    """ Check arg's type is one in type_list, otherwise,
    raises an error.
    """
    if not isinstance(type_list, list):
        type_list = [type_list]

    if arg.type_ in type_list:
        return True

    if len(type_list) == 1:
        errmsg.error(lineno, "Wrong expression type '%s'. Expected '%s'" %
                     (arg.type_, type_list[0]))
    else:
        errmsg.error(lineno, "Wrong expression type '%s'. Expected one of '%s'"
                     % (arg.type_, tuple(type_list)))

    return False
Beispiel #24
0
    def dump(self):
        """ Returns a tuple containing code ORG (origin address), and a list of bytes (OUTPUT)
        """
        org = min(self.memory_bytes.keys())  # Org is the lowest one
        OUTPUT = []
        align = []

        for label in self.global_labels.values():
            if not label.defined:
                error(label.lineno, "Undefined GLOBAL label '%s'" % label.name)

        for i in range(org, max(self.memory_bytes.keys()) + 1):
            if gl.has_errors:
                return org, OUTPUT

            try:
                try:
                    a = [x for x in self.orgs[i]
                         if isinstance(x, Asm)]  # search for asm instructions

                    if not a:
                        align.append(
                            0)  # Fill with ZEROes not used memory regions
                        continue

                    OUTPUT += align
                    align = []
                    a = a[0]
                    if a.pending:
                        a.arg = a.argval()
                        a.pending = False
                        tmp = a.bytes()

                        for r in range(len(tmp)):
                            self.memory_bytes[i + r] = tmp[r]
                except KeyError:
                    pass

                OUTPUT.append(self.memory_bytes[i])

            except KeyError:
                OUTPUT.append(0)  # Fill with ZEROes not used memory regions

        return org, OUTPUT
Beispiel #25
0
def assemble(input_):
    """ Assembles input string, and leave the result in the
    MEMORY global object
    """
    global MEMORY

    if MEMORY is None:
        MEMORY = Memory()

    if OPTIONS.zxnext:
        parser_ = zxnext_parser
    else:
        parser_ = parser

    parser_.parse(input_, lexer=LEXER, debug=OPTIONS.debug_level > 1)
    if len(MEMORY.scopes):
        error(MEMORY.scopes[-1], 'Missing ENDP to close this scope')

    return gl.has_errors
Beispiel #26
0
def p_ind8_I(p):
    """    reg8_I : LP IX expr RP
               | LP IY expr RP
               | LP IX PLUS pexpr RP
               | LP IX MINUS pexpr RP
               | LP IY PLUS pexpr RP
               | LP IY MINUS pexpr RP
               | LB IX expr RB
               | LB IY expr RB
               | LB IX PLUS pexpr RB
               | LB IX MINUS pexpr RB
               | LB IY PLUS pexpr RB
               | LB IY MINUS pexpr RB
    """
    if len(p) == 6:
        expr = p[4]
        sign = p[3]
    else:
        expr = p[3]
        gen_ = expr.inorder()
        first_expr = next(gen_, '')
        if first_expr and first_expr.parent:
            if len(first_expr.parent.children) == 2:
                first_token = first_expr.symbol.item
            else:
                first_token = first_expr.parent.symbol.item
        else:
            first_token = '<nothing>'
        if first_token not in ('-', '+'):
            error(
                p.lineno(2),
                "Unexpected token '{}'. Expected '+' or '-'".format(
                    first_token))
        sign = '+'

    if sign == '-':
        expr = Expr.makenode(Container(sign, p.lineno(2)), expr)

    p[0] = ('(%s+N)' % p[2], expr)
Beispiel #27
0
    def make_node(cls, id_: str, arglist: SymbolARGLIST, lineno: int, filename: str) -> Optional['SymbolARRAYACCESS']:
        """ Creates an array access. A(x1, x2, ..., xn)
        """
        assert isinstance(arglist, SymbolARGLIST)
        variable = gl.SYMBOL_TABLE.access_array(id_, lineno)
        if variable is None:
            return None

        if variable.scope != SCOPE.parameter:
            if len(variable.bounds) != len(arglist):
                errmsg.error(lineno, "Array '%s' has %i dimensions, not %i" %
                             (variable.name, len(variable.bounds), len(arglist)))
                return None

            # Checks for array subscript range if the subscript is constant
            # e.g. A(1) is a constant subscript access
            btype = gl.SYMBOL_TABLE.basic_types[gl.BOUND_TYPE]
            for i, b in zip(arglist, variable.bounds):
                lower_bound = NUMBER(b.lower, type_=btype, lineno=lineno)

                if check.is_number(i.value) or check.is_const(i.value):
                    val = i.value.value
                    if val < b.lower or val > b.upper:
                        errmsg.warning(lineno, "Array '%s' subscript out of range" % id_)

                i.value = BINARY.make_node('MINUS',
                                           TYPECAST.make_node(btype, i.value, lineno),
                                           lower_bound, lineno, func=lambda x, y: x - y,
                                           type_=btype)
        else:
            btype = gl.SYMBOL_TABLE.basic_types[gl.BOUND_TYPE]
            for arg in arglist:
                arg.value = TYPECAST.make_node(btype, arg.value, arg.value.lineno)

        # Returns the variable entry and the node
        return cls(variable, arglist, lineno, filename)
Beispiel #28
0
    def make_node(cls, new_type, node, lineno):
        """ Creates a node containing the type cast of
        the given one. If new_type == node.type, then
        nothing is done, and the same node is
        returned.

        Returns None on failure (and calls syntax_error)
        """
        assert isinstance(new_type, SymbolTYPE)

        # None (null) means the given AST node is empty (usually an error)
        if node is None:
            return None  # Do nothing. Return None

        assert isinstance(node, Symbol), '<%s> is not a Symbol' % node
        # The source and dest types are the same
        if new_type == node.type_:
            return node  # Do nothing. Return as is

        # TODO: Create a base scalar type
        if isinstance(node, SymbolVARARRAY):
            if new_type.size == node.type_.size and TYPE.string not in (new_type, node.type_):
                return node

            error(lineno, "Array {} type does not match parameter type".format(node.name))
            return None

        STRTYPE = TYPE.string
        # Typecasting, at the moment, only for number
        if node.type_ == STRTYPE:
            error(lineno, 'Cannot convert string to a value. Use VAL() function')
            return None

        # Converting from string to number is done by STR
        if new_type == STRTYPE:
            error(lineno, 'Cannot convert value to string. Use STR() function')
            return None

        # If the given operand is a constant, perform a static typecast
        if check.is_CONST(node):
            node.expr = cls(new_type, node.expr, lineno)
            return node

        if not check.is_number(node) and not check.is_const(node):
            return cls(new_type, node, lineno)

        # It's a number. So let's convert it directly
        if check.is_const(node):
            node = SymbolNUMBER(node.value, node.lineno, node.type_)

        if new_type.is_basic and not TYPE.is_integral(new_type):  # not an integer
            node.value = float(node.value)
        else:  # It's an integer
            new_val = (int(node.value) & ((1 << (8 * new_type.size)) - 1))  # Mask it

            if node.value >= 0 and node.value != new_val:
                errmsg.warning_conversion_lose_digits(node.lineno)
                node.value = new_val
            elif node.value < 0 and (1 << (new_type.size * 8)) + \
                    node.value != new_val:  # Test for positive to negative coercion
                errmsg.warning_conversion_lose_digits(node.lineno)
                node.value = new_val - (1 << (new_type.size * 8))

        node.type_ = new_type
        return node
Beispiel #29
0
 def t_INITIAL_preproc_error(self, t):
     # error handling rule
     error(t.lexer.lineno, "illegal character '%s'" % t.value[0])
Beispiel #30
0
def t_INITIAL_bin_string_asm_preproc_comment_error(t):
    error(t.lineno, "illegal character '%s'" % t.value[0])