Ejemplo n.º 1
0
 def _check_builtins(self):
     '''
     Query whether the current token is either a builtin or a registered vartype.
     '''
     if self.cur_tok.value in Builtins:
         raise ParseError(
             f'"{self.cur_tok.value}" cannot be used as an identifier (builtin)',
             self.cur_tok.position)
     if self.cur_tok.value in self.vartypes:
         raise ParseError(
             f'"{self.cur_tok.value}" cannot be used as an identifier (variable type)',
             self.cur_tok.position)
Ejemplo n.º 2
0
    def _parse_match_expr(self):
        start = self.cur_tok.position
        self._get_next_token()
        cond_item = self._parse_identifier_expr()
        self._match(TokenKind.PUNCTUATOR, '{')
        match_list = []
        default = None
        while not self._cur_tok_is_punctuator('}'):
            set_default = False
            value_list = []
            while True:
                if self.cur_tok.kind == TokenKind.DEFAULT:
                    if default is not None:
                        raise ParseError(
                            '"default" keyword specified multiple times in "match" statement',
                            self.cur_tok.position)
                    set_default = True
                    self._get_next_token()
                    break
                value = self._parse_expression()
                value_list.append(value)
                if not self._cur_tok_is_punctuator(','):
                    break
                self._get_next_token()
            self._match(TokenKind.PUNCTUATOR, ':')
            expression = self._parse_expression()
            if set_default:
                default = expression
                continue
            for n in value_list:
                match_list.append((n, expression))

        self._get_next_token()
        return Match(start, cond_item, match_list, default)
Ejemplo n.º 3
0
def binop_info(tok):
    kind, value, _, position = tok
    try:
        return _binop_map[value]
    except KeyError:
        from core.lexer import TokenKind, PUNCTUATORS
        if kind == TokenKind.PUNCTUATOR and value not in PUNCTUATORS:
            raise ParseError(f'Undefined operator: "{value}"', position)
        # Return a false binop info that has no precedence
        return FALSE_BINOP_INFO
Ejemplo n.º 4
0
    def _parse_with_expr(self):
        cur = self.cur_tok
        self._get_next_token()  # consume `with`

        if self.cur_tok.kind != TokenKind.VAR:
            raise ParseError(f'Invalid "with" expression',
                             self.cur_tok.position)

        vars = self._parse_var_expr()
        body = self._parse_expression()
        return With(cur, vars, body)
Ejemplo n.º 5
0
    def _parse_primary(self):
        if self.top_return and self.level == 1:
            raise ParseError(
                f'Unreachable code found after top-level "return" in function body',
                self.cur_tok.position)

        if self._cur_tok_is_punctuator('('):
            return self._parse_paren_expr()
        elif self._cur_tok_is_punctuator('{'):
            return self._parse_do_expr()
        elif self.cur_tok.kind in self.parse_actions:
            return getattr(
                self, f'_parse_{self.parse_actions[self.cur_tok.kind]}_expr')()
        elif self.cur_tok.kind == TokenKind.EOF:
            raise ParseError('Expression expected but reached end of code',
                             self.cur_tok.position)
        else:
            raise ParseError(
                f'Expression expected but met unknown token: "{self.cur_tok.value}"',
                self.cur_tok.position)
Ejemplo n.º 6
0
    def _match(self, expected_kind, expected_value=None, consume=True):
        '''
        Consume the current token; verify that it's of the expected kind.
        If expected_kind == TokenKind.OPERATOR, verify the operator's value.
        '''
        if self.cur_tok.kind != expected_kind or (
                expected_value and self.cur_tok.value != expected_value):
            val = expected_value if expected_value is not None else expected_kind
            raise ParseError(
                f'Expected "{val}" but got "{self.cur_tok.value}" instead',
                self.cur_tok.position)

        if consume:
            self._get_next_token()
Ejemplo n.º 7
0
 def _parse_argument_list(self, args_required=False):
     args = []
     self._get_next_token()
     while True:
         if self._cur_tok_is_punctuator(')'):
             break
         arg = self._parse_expression()
         args.append(arg)
         if not self._cur_tok_is_punctuator(','):
             break
         self._get_next_token()
     if args_required and len(args) == 0:
         raise ParseError(f'At least one argument is required',
                          self.cur_tok.position)
     return args
Ejemplo n.º 8
0
 def _parse_decorator(self):
     start = self.cur_tok.position
     self._get_next_token()
     dec_name = self._parse_identifier_expr()
     if dec_name.name not in Decorators:
         raise ParseError(f'Unknown decorator "{dec_name.name}"', start)
     if self._cur_tok_is_punctuator('{'):
         dec_body = []
         self._get_next_token()
         while True:
             dec_body.append(self._generate_toplevel())
             if self._cur_tok_is_punctuator('}'):
                 self._get_next_token()
                 break
     else:
         dec_body = [self._generate_toplevel()]
     return Decorator(start, dec_name, dec_body)
Ejemplo n.º 9
0
    def _parse_array_accessor(self):
        self._match(TokenKind.PUNCTUATOR, '[')

        start = self.cur_tok.position
        elements = []

        while True:
            dimension = self._parse_expression()
            if hasattr(dimension, 'val'):
                dimension.val = int(dimension.val)
            elements.append(dimension)
            if self._cur_tok_is_punctuator(','):
                self._get_next_token()
                continue
            elif self._cur_tok_is_punctuator(']'):
                break
            else:
                raise ParseError('Unclosed array accessor',
                                 self.cur_tok.position)

        return ArrayAccessor(start, elements)
Ejemplo n.º 10
0
    def _parse_vartype_expr(self):
        # This is an exception - it doesn't return an AST node,
        # but a variable type that is part of an AST node.

        is_ptr = 0

        while self.cur_tok.kind == TokenKind.PTR:
            is_ptr += 1
            self._get_next_token()

        if self.cur_tok.value in self.vartypes:
            vartype = self.vartypes[self.cur_tok.value]

        elif self.cur_tok.value in self.local_types:
            vartype = self.local_types[self.cur_tok.value].vartype

        else:
            raise ParseError(
                f'Expected a variable type but got {self.cur_tok.value} instead',
                self.cur_tok.position)

        if isinstance(vartype, self.vartypes.func.__class__):
            self._get_next_token()
            self._match(TokenKind.PUNCTUATOR, '(')

            arguments = []
            while True:
                n = self._parse_vartype_expr()
                arguments.append(n)
                if self._cur_tok_is_punctuator(','):
                    self._get_next_token()
                    continue
                if self._cur_tok_is_punctuator(')'):
                    break

            self._get_next_token()
            self._match(TokenKind.PUNCTUATOR, ':')
            func_type = self._parse_vartype_expr()
            vartype = vartype(func_type, arguments)

        else:
            self._get_next_token()

        if self._cur_tok_is_punctuator('['):
            accessor = self._parse_array_accessor()

            fixed_size = True

            for n in accessor.elements:
                if isinstance(n, Variable):
                    fixed_size = False
                    break

            if not fixed_size:
                raise ParseError(
                    f'Array size cannot be set dynamically with a variable; use a constant',
                    n.position)

            # the array has to be built from the inside out
            # for n in reversed(accessor.elements):
            #vartype = VarTypes.array(vartype, int(n.val))

            elements = []
            for n in accessor.elements:
                elements.append(int(n.val))

            vartype = ArrayClass(vartype, elements)

            self._get_next_token()

        if vartype.is_obj:
            is_ptr += 1

        while is_ptr > 0:
            vartype = vartype.as_pointer(getattr(vartype, 'addrspace', 0))
            is_ptr -= 1

        return vartype
Ejemplo n.º 11
0
    def _codegen_Call(self, node, obj_method=False):
        if not obj_method:
            if node.name in Dunders:
                return self._codegen_dunder_methods(node)
            if node.name in Builtins:
                return getattr(self, '_codegen_Builtins_' + node.name)(node)

        call_args = []
        possible_opt_args_funcs = set()

        # The reason for the peculiar construction below
        # is to first process a blank argument list, so
        # we can match calls to functions that have
        # all optional arguments

        for arg in node.args + [None]:
            _ = mangle_types(node.name, call_args)
            if _ in self.opt_args_funcs:
                possible_opt_args_funcs.add(self.opt_args_funcs[_])
            if arg:
                call_args.append(self._codegen(arg))

        if obj_method:
            c = call_args[0]
            try:
                c1 = c.type.pointee.name
            except:
                c1 = c.type
            node.name = f'{c1}.__{node.name}__'

        if not possible_opt_args_funcs:
            mangled_name = mangle_types(node.name, call_args)
            callee_func = self.module.globals.get(mangled_name, None)

        else:
            try:
                match = False
                for f1 in possible_opt_args_funcs:
                    if len(call_args) > len(f1.args):
                        continue
                    match = True
                    for function_arg, call_arg in zip(f1.args, call_args):
                        if function_arg.type != call_arg.type:
                            match = False
                            break
                if not match:
                    raise TypeError
            except TypeError:
                raise ParseError(
                    f'argument types do not match possible argument signature for optional-argument function "{f1.public_name}"',
                    node.position)
            else:
                callee_func = f1
                for n in range(len(call_args), len(f1.args)):
                    call_args.append(f1.args[n].default_value)

        # Determine if this is a function pointer

        try:

            # if we don't yet have a reference,
            # since this might be a function pointer,
            # attempt to obtain one from the variable list

            if not callee_func:
                callee_func = self._varaddr(node.name, False)

            if callee_func.type.is_func():
                # retrieve actual function pointer from the variable ref
                func_to_check = callee_func.type.pointee.pointee
                final_call = self.builder.load(callee_func)
                ftype = func_to_check

                final_call.decorators = []
                #final_call.decorators = callee_func.decorators

                # It's not possible to trace decorators across
                # function pointers

            else:
                # this is a regular old function, not a function pointer
                func_to_check = callee_func
                final_call = callee_func
                ftype = getattr(func_to_check, 'ftype', None)

        except Exception:
            raise CodegenError(
                f'Call to unknown function "{node.name}" with signature "{[n.type.describe() for n in call_args]}" (maybe this call signature is not implemented for this function?)',
                node.position)

        if not ftype.var_arg:
            if len(func_to_check.args) != len(call_args):
                raise CodegenError(
                    f'Call argument length mismatch for "{node.name}" (expected {len(callee_func.args)}, got {len(node.args)})',
                    node.position)
        else:
            if len(call_args) < len(func_to_check.args):
                raise CodegenError(
                    f'Call argument length mismatch for "{node.name}" (expected at least {len(callee_func.args)}, got {len(node.args)})',
                    node.position)

        nomod = 'nomod' in final_call.decorators

        for x, n in enumerate(zip(call_args, func_to_check.args)):
            type0 = n[0].type

            # in some cases, such as with a function pointer,
            # the argument is not an Argument but a core.vartypes instance
            # so this check is necessary

            if type(n[1]) == ir.values.Argument:
                type1 = n[1].type
            else:
                type1 = n[1]

            if type0 != type1:
                raise CodegenError(
                    f'Call argument type mismatch for "{node.name}" (position {x}: expected {type1.describe()}, got {type0.describe()})',
                    node.args[x].position)

            # if this is a traced object, and we give it away,
            # then we can't delete it in this scope anymore
            # because we no longer have ownership of it

            if not nomod:
                to_check = self._extract_operand(n[0])
                if to_check.heap_alloc:
                    to_check.tracked = False

        call_to_return = self.builder.call(final_call, call_args, 'calltmp')

        # Check for the presence of an object returned from the call
        # that requires memory tracing

        if callee_func in self.gives_alloc:
            call_to_return.heap_alloc = True
            call_to_return.tracked = True

        # FIXME: There ought to be a better way to assign this

        if callee_func.tracked == True:
            call_to_return.heap_alloc = True
            call_to_return.tracked = True

        if 'unsafe_req' in final_call.decorators and not self.allow_unsafe:
            raise CodegenError(
                f'Function "{node.name}" is decorated with "@unsafe_req" and requires an "unsafe" block"',
                node.position)

        # if callee_func.do_not_allocate == True:
        #     call_to_return.do_not_allocate = True

        # if 'nomod' in callee_func.decorators:
        #     call_to_return.tracked=False

        return call_to_return