コード例 #1
0
    def __translate_define_expr(self, expr, end=True):
        ''' Get a single parsed DEFINE expression and return the equivalent
            C++ and CUDA code.

            If 'end' is false, then the final characters of the expression,
            like semi-colons and newlines, are not added.
        '''

        cpp = ''
        cuda = ''

        return_type = Type.enum_to_c_type(expr.loc, expr.type)
        cpp = f'{return_type} {expr.name}('

        # Add the parameters.
        for (arg_type, arg_name) in expr.args[:-1]:
            cpp += f'{Type.enum_to_c_type(expr.loc, arg_type)} {arg_name}, '

        # The last parameter is a little different.
        if len(expr.args) > 0:
            (arg_type, arg_name) = expr.args[-1]
            cpp += f'{Type.enum_to_c_type(expr.loc, arg_type)} {arg_name}'
        cpp += ')'

        # Add this prototype for use in a header file.
        self.cpp_prototypes.append(cpp + ';\n')

        cpp += ' {\n'

        # Add the body of the function.
        body_cpp = ''
        for e in expr.body[:-1]:
            (c, cu) = self.__translate_expr(e, True)
            body_cpp += c
            cuda += cu

        # The last expression should be returned.
        if len(expr.body) == 0:
            error_str = 'expected one or more body expressions'
            raise error.Syntax(expr.loc, error_str)

        e = expr.body[-1]
        if e.exprClass != ExprEnum.LITERAL and \
           e.exprClass != ExprEnum.GET_VAR and \
           e.exprClass != ExprEnum.CALL:
            error_str = 'expected literal, get, or call as last body expression'
            raise error.Syntax(expr.loc, error_str)

        (c, cu) = self.__translate_expr(e, False)
        body_cpp += f'return {c};\n'
        cuda += cu

        self._increase_indent()
        body_cpp = self._make_indented(body_cpp)
        self._decrease_indent()

        cpp = self._make_indented(cpp)
        cpp = cpp + body_cpp + self._make_indented('}\n\n')

        return (cpp, cuda)
コード例 #2
0
    def __parse_expr(self, parsing_exprs_list=False, got_paren=False):
        '''
        Private function to parse and return a single expression.

        If parsing_exprs_list is true, then reading the next non-whitespace
        character as ')' causes this function to return None, indicating that
        there are no more expressions in the list. If parsing_exprs_list is
        false and a ')' is read, an error occurs indicating invalid syntax.

        If got_paren is false, then the expression must start with '('. If
        got_paren is true, then the opening parenthesis was already read so
        the expression should begin with a keyword like lit, val, call, ....
        '''

        if not got_paren:
            # Make sure the expression starts with an open paranthesis.
            c = self.__eat_whitespace()
            if c == '':
                # The end of file was reached
                return

            if parsing_exprs_list and c == ')':
                # The end of the expression list has been reached.
                return None

            if c != '(':
                loc = self.__get_point_loc(True).span(self.__get_point_loc())
                raise error.Syntax(loc, f'expected "(" but found "{c}"')

        start_point_loc = self.__get_point_loc(prev_col=True)
        (loc, word) = self.__parse_word_with_loc()

        if word == 'lit':
            return self.__parse_literal(start_point_loc)
        elif word == 'get':
            return self.__parse_get_var(start_point_loc)
        elif word == 'val':
            return self.__parse_create_var(start_point_loc)
        elif word == 'set':
            return self.__parse_set_var(start_point_loc)
        elif word == 'define':
            return self.__parse_define(start_point_loc)
        elif word == 'call':
            return self.__parse_call(start_point_loc)
        elif word == 'if':
            return self.__parse_if(start_point_loc)
        elif word == 'loop' or word == 'seq_loop':
            return self.__parse_loop(start_point_loc, word == 'seq_loop')
        elif word == 'list':
            return self.__parse_list(start_point_loc)
        elif word == 'list_at':
            return self.__parse_list_at(start_point_loc)
        elif word == 'list_set':
            return self.__parse_list_set(start_point_loc)
        else:
            raise error.Syntax(loc, f'unknown expression type: {word}')
コード例 #3
0
    def __parse_if(self, start_point_loc):
        '''
        Private function to parse a single IF expression. The _file
        variable should be in a state starting with (without quotes):
        '<cond> then <e1> <e2> ... else <e1> <e2> ...)'
        That is, the '(if ' section has alread been read.

        Returns an instance of If()
        '''

        # Parse the condition expression.
        if_cond = self.__parse_expr()

        # After the condition there should be the 'then' keyword.
        (l, then_word) = self.__parse_word_with_loc()
        if then_word != 'then':
            raise error.Syntax(l, f'expected "then" but got {then_word}')

        # Parse the list of 'then' expressions.
        if_then = []
        while True:
            e = self.__parse_expr_or_keyword()

            # Check if a keyword was read.
            if e in keywords:
                if e == 'else':
                    break
                else:
                    point_l = self.__get_point_loc()
                    point_l.col -= len(e)
                    loc = point_l.span(self.__get_point_loc())

                    raise error.Syntax(loc, f'expected "else" but got {e}')

            # An expression was parsed.
            if_then.append(e)

        # Parse the list of 'else' expressions.
        if_else = []
        while True:
            e = self.__parse_expr(parsing_exprs_list=True)

            if e is None:
                break

            if_else.append(e)

        # Now the entire if expression has been parsed.
        loc = start_point_loc.span(self.__get_point_loc())
        return If(loc, if_cond, if_then, if_else)
コード例 #4
0
    def __translate_call_prim_binary_expr(self, expr, end=True):
        ''' Get a single parsed CALL expression for a primitive binary function
            and return the equivalent C++ and CUDA code.

            If 'end' is false, then the final characters of the expression,
            like semi-colons and newlines, are not added.
        '''

        cpp = ''
        cuda = ''

        if len(expr.params) != 2:
            error_str = f'expected 2 arguments but got {len(expr.params)}'
            raise error.Syntax(expr.loc, error_str)

        cpp += '('
        cpp += f'{self.__translate_expr(expr.params[0], False)[0]}'
        cpp += f' {prim_binary_funcs[expr.name]} '
        cpp += f'{self.__translate_expr(expr.params[1], False)[0]}'
        cpp += ')'

        cpp += ';\n' if end else ''
        cpp = self._make_indented(cpp)

        return (cpp, cuda)
コード例 #5
0
    def convert_type(loc, type_enum, val):
        '''
        type_enum: Type.INT, Type.FLOAT, ...
        val: string that will be converted to type 'type_enum'

        Return 'val' as a value of the type corresponding to 'type_enum'. For
        example:

        convert_type(loc, Type.INT, "67")       -> 67
        convert_type(loc, Type.FLOAT, "0.43")   -> 0.43
        convert_type(loc, Type.STRING, "hello") -> "hello"
        '''

        try:
            if type_enum == Type.INT:
                return int(val)
            elif type_enum == Type.FLOAT:
                return float(val)
            elif type_enum == Type.STRING:
                if val[0] != '\'' or val[-1] != '\'':
                    raise error.Syntax(loc, f'invalid string: {val}')
                return str(val[1:-1])
            else:
                error_str = f'convert_type: unknown type {type_enum}'
                raise error.InternalError(loc, error_str)
        except ValueError:
            type_str = Type.type_to_str(loc, type_enum)
            raise error.IncompatibleType(loc, type_str, val)
コード例 #6
0
    def __eat_close_paren(self):
        ''' Advance the file one character and varify that it reads ')'. '''

        c = self._file.read(1)
        self._file_col += 1

        loc = self.__get_point_loc(True).span(self.__get_point_loc())

        if c != ')':
            raise error.Syntax(loc, f'expected ")" but got "{c}"')
コード例 #7
0
    def __parse_call(self, start_point_loc):
        '''
        Private function to parse a single CALL expression. The _file
        variable should be in a state starting with (without quotes):
        '<name> <arg1_expr> <arg2_expr> ...)'
        That is, the '(call ' section has alread been read.

        Returns an instance of Call()
        '''

        call_name = self.__parse_word()
        call_params = []

        # Parse the ':' between function name and start of arguments.
        c = self.__eat_whitespace()
        if c == '':
            # The end of file was reached.
            loc = self.__get_point_loc(True).span(self.__get_point_loc())
            raise error.Syntax(loc, f'unexpected end of file')

        if c != ':':
            loc = self.__get_point_loc(True).span(self.__get_point_loc())
            raise error.Syntax(loc, f'expected ":" but found "{c}"')

        # Parse the argumnts, if any.
        while True:
            e = self.__parse_expr(parsing_exprs_list=True)

            if e is None:
                break

            call_params.append(e)

        # Now the entire call has been parsed.
        loc = start_point_loc.span(self.__get_point_loc())
        return Call(loc, call_name, call_params)
コード例 #8
0
    def get_type_from_string_val(loc, v):
        '''
        Return the intended type of 'v', which is of type string. Some examples
        of expected behavior are:
        v = "1245" -> Type.INT
        v = "12.4" -> Type.FLOAT
        v = "'hi'" -> Type.STRING
        '''

        if v[0] == '\'':
            if v[-1] != '\'':
                raise error.Syntax(loc, f'invalid string: {v}')
            return Type.STRING
        elif '.' in v:
            return Type.FLOAT
        else:
            return Type.INT
コード例 #9
0
    def __validate_define_expr_type(self, expr):
        ''' Typecheck and add environment data for a single Define Expr. '''

        # Make sure the name is not in the current scope.
        if self._env.name_in_scope(expr.name):
            error_str = f'function {expr.name} already defined in this scope'
            raise error.Name(expr.loc, error_str)

        # Update the environment before validating the body expressions so that
        # recursive functions are allowed (i.e. the body expression can find
        # the return type of this function).
        func_type = [expr.type]  # This is the return type.

        for arg in expr.args:
            # arg[0] is the type and arg[1] is the name.
            func_type.append(arg[0])

        self._env.add(expr.name, func_type, False)

        # The function body is in a new scope.
        self._env.push_scope()

        # Add the function arguments to the environment so that the body can
        # use them.
        for arg in expr.args:
            # arg[0] is the type and arg[1] is the name.
            self._env.add(arg[1], [arg[0]], True)

        # Validate all of body expressions.
        for e in expr.body:
            self.__validate_single_expr(e)

        # The last body expression is returned from the function, so it must
        # have the correct type.
        if len(expr.body) == 0:
            error_str = f'expected body to have an expression to return'
            raise error.Syntax(expr.loc, error_str)

        ret_expr = expr.body[-1]
        self.__validate_type_of_expr(ret_expr.loc, func_type[0], ret_expr)

        # The function body scope ended.
        expr.env = self._env.copy()
        self._env.pop_scope(expr.loc)
コード例 #10
0
ファイル: env.py プロジェクト: AndrewHess/cs179-project
    def lookup_variable(self, loc, expr_name):
        ''' Find a variable in the environment and return its type. '''

        # Make sure the variable already exists.
        (name, type_lst, is_var) = self.get_entry_for_name(expr_name)

        if name is None:
            error_str = f'variable {expr_name} does not exist'
            raise error.Name(loc, error_str)

        if len(type_lst) != 1 or not is_var:
            error_str = f'{expr_name} is a function; expected a variable'
            raise error.Syntax(loc, error_str)

        # Make sure the variable has a type.
        if type_lst == [Type.UNDETERMINED] or type_lst == [Type.NONE]:
            error_str = f'variable {expr_name} has bad type: {type_lst[0]}'
            raise error.Type(loc, error_str)

        return type_lst[0]
コード例 #11
0
    def __translate_create_var_expr(self, expr, end=True):
        ''' Get a single parsed CREATE_VAR expression and return the equivalent
            C++ and CUDA code.

            If 'end' is false, then the final characters of the expression,
            like semi-colons and newlines, are not added.
        '''

        cpp = ''
        cuda = ''
        if expr.type == Type.INT:
            (c, cu) = self.__translate_expr(expr.val, False)
            cpp = f'int {expr.name} = {c}'
            cpp += ';\n' if end else ''
            cuda += cu
        elif expr.type == Type.FLOAT:
            (c, cu) = self.__translate_expr(expr.val, False)
            cpp = f'float {expr.name} = {c}'
            cpp += ';\n' if end else ''
            cuda += cu
        elif expr.type == Type.STRING:
            if expr.val.exprClass == ExprEnum.LITERAL:
                size = len(expr.val.val) + 1  # Add 1 for the null terminator.
                cpp = f'\nchar *{expr.name} = malloc({size});\n' + \
                      f'if ({expr.name} == NULL) {"{"}\n' + \
                      f'    printf("failed to allocate {size} bytes\\n");\n' + \
                      f'    exit(1);\n' + \
                      f'{"}"}\n' + \
                      f'*{expr.name} = "{expr.val.val}"'
                cpp += ';\n' if end else ''
            elif expr.val.exprClass == ExprEnum.GET_VAR or \
                 expr.val.exprClass == ExprEnum.CALL:
                (c, cu) = self.__translate_expr(expr.val, False)
                cpp = f'char *{expr.name} = {c}'
                cpp += ';\n' if end else ''
                cuda += cu
            else:
                raise error.Syntax(expr.loc, 'invalid string initialization')

        cpp = self._make_indented(cpp)
        return (cpp, cuda)
コード例 #12
0
    def __parse_loop(self, start_point_loc, no_parallelization):
        '''
        Private function to parse a single LOOP expression. The _file
        variable should be in a state starting with (without quotes):
        '<init> <test> <update> <e1> <e2> ...)'
        That is, the '(loop ' section has alread been read.

        If 'no_parallelization' is true, then the loop will not be
        parallelized, even if it is possible to do so.

        Returns an instance of Loop()
        '''

        # Parse the beginning expressions.
        loop_init = self.__parse_expr()
        loop_test = self.__parse_expr()
        loop_update = self.__parse_expr()

        # Just before the body expressions there should be the 'do' keyword.
        (l, do_word) = self.__parse_word_with_loc()
        if do_word != 'do':
            raise error.Syntax(l, f'expected "do" but got {do_word}')

        # Parse the list of body expressions.
        loop_body = []
        while True:
            e = self.__parse_expr(parsing_exprs_list=True)

            if e is None:
                break

            loop_body.append(e)

        # Now the entire loop expression has been parsed.
        loc = start_point_loc.span(self.__get_point_loc())
        return Loop(loc, loop_init, loop_test, loop_update, loop_body,
                    no_parallelization)
コード例 #13
0
ファイル: env.py プロジェクト: AndrewHess/cs179-project
    def lookup_function(self, loc, expr_name):
        '''
        Find a function in the environment.

        Return a tuple with two elements. The first is the return type of the
        function and the second is a list of types for the arguments.
        '''

        # Make sure the function already exists.
        (name, type_lst, is_var) = self.get_entry_for_name(expr_name)

        if name is None:
            error_str = f'function {expr_name} does not exist'
            raise error.Name(loc, error_str)

        if is_var:
            error_str = f'{expr_name} is a variable; expected a function'
            raise error.Syntax(loc, error_str)

        if len(type_lst) == 0:
            error_str = f'{expr_name} has no return type'
            raise error.InternalError(loc, error_str)

        # Make sure the function has a return type.
        ret_type = type_lst[0]
        if ret_type == Type.UNDETERMINED or ret_type == Type.NONE:
            error_str = f'function {expr_name} has bad return type: {ret_type}'
            raise error.Type(loc, error_str)

        # Make sure the parameter types are valid.
        for param_type in type_lst[1:]:
            if param_type == Type.UNDETERMINED or param_type == Type.NONE:
                error_str =  f'function {expr_name} has bad '
                error_str += f'paramter type: {param_type}'
                raise error.Type(expr.loc, error_str)

        return (ret_type, type_lst[1:])
コード例 #14
0
    def __parse_expr_or_keyword(self):
        '''
        Private function to parse and return a single expression or a keyword.
        Returns either a parsed expression or a string keyword.
        '''

        c = self.__eat_whitespace()
        if c == '':
            # The end of file was reached
            return

        if c == '(':
            # Get the rest of the expression.
            return self.__parse_expr(got_paren=True)
        else:
            # All expressions start with '(', so this must be a keyword.
            (loc, word) = self.__parse_word_with_loc()
            word = c + word

            if word in keywords:
                return word
            else:
                error_str = f'expected expression or keyword but found "{word}"'
                raise error.Syntax(loc, error_str)
コード例 #15
0
    def __parse_define(self, start_point_loc):
        '''
        Private function to parse a single DEFINE expression. The _file
        variable should be in a state starting with (without quotes):
        '<name> (<arg1> <arg2> ...) <body1> <body2> ...)'
        That is, the '(define ' section has alread been read.

        Returns an instance of Define()
        '''

        # Get the return type and function name.
        (l, def_return_type) = self.__parse_word_with_loc()
        if def_return_type == 'list':
            list_type = self.__parse_type()
            if list_type == Type.INT:
                def_return_type = Type.LIST_INT
            elif list_type == Type.FLOAT:
                def_return_type = Type.LIST_FLOAT
            elif list_type == Type.STRING:
                def_return_type = Type.LIST_STRING
            else:
                error_str = '__parse_define cannot get here'
                raise error.InternalError(l, error_str)
        else:
            def_return_type = Type.str_to_type(l, def_return_type)

        def_name = self.__parse_word()

        # Parse the ':' between function name and start of arguments.
        c = self.__eat_whitespace()
        if c == '':
            # The end of file was reached.
            loc = self.__get_point_loc(True).span(self.__get_point_loc())
            raise error.Syntax(loc, f'unexpected end of file')

        if c != ':':
            loc = self.__get_point_loc(True).span(self.__get_point_loc())
            raise error.Syntax(loc, f'expected ":" but found "{c}"')

        # Parse the arguments.
        def_args = []

        while True:
            (l, maybe_type) = self.__parse_word_with_loc()

            # Check if the end of the arguments has been reached.
            if maybe_type == ':':
                break

            # There is another argument to parse.
            arg_type = None
            if maybe_type == 'list':
                list_type = self.__parse_type()
                if list_type == Type.INT:
                    arg_type = Type.LIST_INT
                elif list_type == Type.FLOAT:
                    arg_type = Type.LIST_FLOAT
                elif list_type == Type.STRING:
                    arg_type = Type.LIST_STRING
                else:
                    error_str = '__parse_define cannot get here'
                    raise error.InternalError(l, error_str)
            else:
                arg_type = Type.str_to_type(l, maybe_type)

            arg_name = self.__parse_word()

            def_args.append((arg_type, arg_name))

        # Parse the body expressions.
        def_body = []

        while True:
            e = self.__parse_expr(parsing_exprs_list=True)

            if e is None:
                break

            def_body.append(e)

        # Now the entire function has been parsed.
        loc = start_point_loc.span(self.__get_point_loc())
        return Define(loc, def_return_type, def_name, def_args, def_body)