Пример #1
0
    def execute_code(self,
                     code,
                     params=None,
                     extra_scope=None,
                     namespace_name='missionnamespace',
                     delete_mode=False):
        key = self.code_key(code)

        if key in self._unexecuted_codes:
            del self._unexecuted_codes[key]
        self._executed_codes.add(key)

        self.delete_scope_level += delete_mode
        outcome = super().execute_code(code, params, extra_scope,
                                       namespace_name)
        self.delete_scope_level -= delete_mode

        # collect `private` statements that have a variable but were not collected by the assignment operator
        if isinstance(code, File):
            for key in self._unexecuted_codes:
                self.execute_unexecuted_code(key)

            for private in self.privates:
                self.exception(
                    SQFWarning(private.position,
                               'private argument must be a string.'))

            for token in self.unevaluated_interpreter_tokens:
                self.exception(
                    SQFWarning(
                        token.position, 'helper type "%s" not evaluated' %
                        token.__class__.__name__))

        return outcome
Пример #2
0
    def add_params(self, base_token, arguments=None):
        assert (isinstance(base_token, Array))

        if arguments is None or isinstance(arguments, Nothing):
            arguments = self['_this']
        if isinstance(arguments, Array) and not arguments.is_undefined:
            arguments = arguments.value
        else:
            arguments = self._parse_params_args(arguments, base_token)

        if len(arguments) > len(base_token):
            self.exception(
                SQFWarning(
                    base_token.position,
                    '`params` lhs (%d elements) is larger than rhs (%d elements).'
                    ' Some arguments are ignored.' %
                    (len(arguments), len(base_token))))

        for i, token in enumerate(base_token):
            if isinstance(token, String):
                if token.value == '':
                    continue
                self.add_privates([token])
                if i >= len(arguments):
                    self.exception(
                        SQFWarning(
                            token.position,
                            '`params` mandatory argument %s is missing in rhs'
                            % token))
                else:
                    self.current_scope[token.value] = arguments[i]
            elif isinstance(token, Array):
                if len(token) in (2, 3, 4):
                    if i < len(arguments) and not isinstance(
                            arguments[i], Nothing):
                        argument = arguments[i]
                    else:
                        argument = token[1]

                    self._add_params(token)
                    self.current_scope[token[0].value] = argument
                else:
                    self.exception(
                        SQFParserError(
                            base_token.position,
                            '`params` array element must have 2-4 elements'))
            else:
                self.exception(
                    SQFParserError(
                        base_token.position,
                        '`params` array element must be a string or array'))
        return True
Пример #3
0
    def execute_code(self,
                     code,
                     extra_scope=None,
                     namespace_name='missionnamespace',
                     delete_mode=False):
        key = self.code_key(code)
        exe_code_key = self.exe_code_key(code, extra_scope)

        if key in self._unexecuted_codes:
            del self._unexecuted_codes[key]
        if exe_code_key in self._executed_codes:
            outcome = self._executed_codes[exe_code_key]
        else:
            self.delete_scope_level += delete_mode
            outcome = super().execute_code(code, extra_scope, namespace_name)
            self.delete_scope_level -= delete_mode
            self._executed_codes[exe_code_key] = outcome

        if isinstance(code, File):
            for key in self._unexecuted_codes:
                self.execute_unexecuted_code(key)

            # collect `private` statements that have a variable but were not collected by the assignment operator
            # this check is made at the scope level
            for private in self.privates:
                self.exception(
                    SQFWarning(private.position,
                               'private argument must be a string.'))

            # this check is made at the scope level
            for token in self.unevaluated_interpreter_tokens:
                self.exception(
                    SQFWarning(
                        token.position, 'helper type "%s" not evaluated' %
                        token.__class__.__name__))

            # this check is made at script level
            if not delete_mode:
                # collect variables that were not used
                for key in self.variable_uses:
                    if self.variable_uses[key]['count'] == 0:
                        variable = self.variable_uses[key]['variable']
                        self.exception(
                            SQFWarning(
                                variable.position,
                                'Variable "%s" not used' % variable.value))

        return outcome
Пример #4
0
    def value(self, token, namespace_name=None):
        """
        Given a single token, recursively evaluates and returns its value
        """
        if namespace_name is None:
            namespace_name = self.current_namespace.name

        assert (isinstance(token, BaseType))
        if isinstance(token, IfDefResult):
            for x in token.result:
                x.set_position(token.position)
                result = self.value(self.execute_token(x))
        elif isinstance(token, DefineResult):
            token.result.set_position(token.position)
            result = self.value(self.execute_token(token.result))
        elif isinstance(token, Statement):
            result = self.value(self.execute_token(token))
        elif isinstance(token, Variable):
            scope = self.get_scope(token.name, namespace_name)
            if scope.level == 0 and not token.is_global:
                self.exception(
                    SQFWarning(
                        token.position,
                        'Local variable "%s" is not from this scope (not private)'
                        % token))

            try:
                result = scope[token.name]
            except KeyError:
                result = self.private_default_class()
            result.position = token.position

            key = '%s_%s_%s' % (namespace_name, scope.level,
                                scope.normalize(token.name))
            if key in self.variable_uses:
                self.variable_uses[key]['count'] += 1

        elif isinstance(token, Array) and not token.is_undefined:
            result = Array(
                [self.value(self.execute_token(s)) for s in token.value])
            result.position = token.position
        else:
            null_expressions = values_to_expressions([token], EXPRESSIONS_MAP,
                                                     EXPRESSIONS)
            if null_expressions:
                result = null_expressions[0].execute([token], self)
            else:
                result = token
            result.position = token.position

        if isinstance(
                result,
                Code) and self.code_key(result) not in self._unexecuted_codes:
            self._unexecuted_codes[self.code_key(result)] = UnexecutedCode(
                result, self)

        return result
Пример #5
0
 def execute_other(self, statement):
     if isinstance(statement, Comment):
         string = str(statement)[2:]
         matches = [x for x in self.COMMENTS_FOR_PRIVATE if string.startswith(x)]
         if matches:
             length = len(matches[0]) + 1  # +1 for the space
             try:
                 parsed_statement = parse(string[length:])
                 array = parsed_statement[0][0]
                 assert(isinstance(array, Array))
                 self.add_privates(self.value(array))
                 # these are unknown values.
                 for token in array.value:
                     if isinstance(token, Statement):
                         token = token.base_tokens[0]
                     self.current_scope[token.value] = Anything()
             except Exception:
                 self.exception(SQFWarning(statement.position, '{0} comment must be `//{0} ["var1",...]`'.format(matches[0])))
Пример #6
0
 def execute_other(self, statement):
     if isinstance(statement, Comment):
         string = str(statement)[2:]
         matches = [
             x for x in self.COMMENTS_FOR_PRIVATE if string.startswith(x)
         ]
         if matches:
             length = len(matches[0]) + 1  # +1 for the space
             try:
                 parsed_statement = parse(string[length:])
                 array = parsed_statement[0][0]
                 assert (isinstance(array, Array))
                 self.add_privates(self.value(array))
             except Exception:
                 self.exception(
                     SQFWarning(
                         statement.position,
                         '{0} comment must be `//{0} ["var1",...]`'.format(
                             matches[0])))
Пример #7
0
    def assign(self, lhs, rhs_v):
        """
        Assigns the rhs_v to the lhs variable.
        """
        lhs_name = lhs.name
        lhs_position = lhs.position

        scope = self.get_scope(lhs_name)

        try:
            lhs_t = type(scope[lhs.name])
        except KeyError:
            lhs_t = self.private_default_class
        rhs_t = type(rhs_v)

        if scope.level == 0:
            # global variable becomes undefined when:
            # 1. it changes type AND
            # 2. it is modified on a higher delete scope (e.g. if {}) or it already has a defined type
            if (lhs_t != Anything or self.delete_scope_level > scope.level) and \
                    lhs_t != rhs_t and lhs_name not in self.undefined_variables:
                self.undefined_variables.add(lhs_name)

        if scope.level == 0:
            if lhs_name in self.undefined_variables:
                rhs_t = Anything
        elif lhs_t != rhs_t and self.delete_scope_level >= scope.level:
            rhs_t = Anything

        if (rhs_t == Namespace):
            scope[lhs_name] = rhs_t(lhs_name)
        else:
            scope[lhs_name] = rhs_t()

        if scope.level == 0 and lhs_name.startswith('_'):
            self.exception(
                SQFWarning(
                    lhs_position,
                    'Local variable "%s" assigned to an outer scope (not private)'
                    % lhs_name))
Пример #8
0
    def execute_single(self, statement):
        assert (isinstance(statement, Statement))

        outcome = Nothing()
        outcome.position = statement.position

        base_tokens = []
        for token in statement.tokens:
            if not statement.is_base_token(token):
                self.execute_other(token)
            else:
                base_tokens.append(token)

        if not base_tokens:
            return outcome

        # operations that cannot evaluate the value of all base_tokens
        if type(base_tokens[0]) == DefineStatement:
            return base_tokens[0]
        elif base_tokens[0] == Preprocessor("#include"):
            if len(base_tokens) != 2:
                exception = SQFParserError(base_tokens[0].position,
                                           "#include requires one argument")
                self.exception(exception)
            elif type(self.execute_token(base_tokens[1])) != String:
                exception = SQFParserError(
                    base_tokens[0].position,
                    "#include first argument must be a string")
                self.exception(exception)
            return outcome
        elif isinstance(base_tokens[0],
                        Keyword) and base_tokens[0].value in PREPROCESSORS:
            # remaining preprocessors are ignored
            return outcome
        elif len(base_tokens) == 2 and base_tokens[0] == Keyword('private'):
            # the rhs may be a variable, so we cannot get the value
            rhs = self.execute_token(base_tokens[1])
            if isinstance(rhs, String):
                self.add_privates([rhs])
            elif isinstance(rhs, Array):
                value = self.value(rhs)
                if value.is_undefined:
                    self.exception(
                        SQFWarning(
                            base_tokens[0].position,
                            'Obfuscated statement. Consider explicitly set what is private.'
                        ))
                else:
                    self.add_privates(value)
            elif isinstance(rhs, Variable):
                var = String('"' + rhs.name + '"')
                var.position = rhs.position
                self.add_privates([var])
                outcome = PrivateType(rhs)
                outcome.position = rhs.position
                self.privates.add(outcome)
            else:
                self.exception(
                    SQFParserError(base_tokens[0].position,
                                   '`private` used incorrectly'))
            return outcome
        # assignment operator
        elif len(base_tokens) == 3 and base_tokens[1] == Keyword('='):
            lhs = self.execute_token(base_tokens[0])
            if isinstance(lhs, PrivateType):
                self.privates.remove(lhs)
                lhs = lhs.variable
            else:
                lhs = self.get_variable(base_tokens[0])

            if not isinstance(lhs, Variable):
                self.exception(
                    SQFParserError(
                        base_tokens[0].position,
                        'lhs of assignment operator must be a variable'))
            else:
                # if the rhs_v is code and calls `lhs` (recursion) it will assume lhs is anything (and not Nothing)
                scope = self.get_scope(lhs.name)
                if lhs.name not in scope or isinstance(scope[lhs.name],
                                                       Nothing):
                    scope[lhs.name] = Anything()

                rhs_v = self.value(base_tokens[2])
                self.assign(lhs, rhs_v)
                if not statement.ending:
                    outcome = rhs_v
            return outcome
        # A variable can only be evaluated if we need its value, so we will not call its value until the very end.
        elif len(base_tokens) == 1 and type(
                base_tokens[0]) in (Variable, Array):
            return self.execute_token(base_tokens[0])
        # heuristic for defines (that are thus syntactically correct):
        #   - is keyword but upper cased
        #   - first token string starts uppercased
        elif len(base_tokens) == 1 and type(base_tokens[0]) == Keyword and str(
                base_tokens[0])[0].isupper():
            outcome = Variable(str(base_tokens[0]))
            outcome.position = base_tokens[0].position
            return outcome
        elif is_undefined_define(base_tokens):
            # get all arguments and compute their value to analyze them
            if isinstance(base_tokens[1].base_tokens[0], Statement):
                sub_tokens = base_tokens[1].base_tokens[0].base_tokens
            else:
                sub_tokens = base_tokens[0]
            for sub_token in sub_tokens:
                self.value(sub_token)

            # finally, build the outcome
            outcome = Anything()
            outcome.position = base_tokens[0].position
            return outcome

        # evaluate all the base_tokens, trying to obtain their values
        values = []
        tokens = []
        for token in base_tokens:
            t = self.execute_token(token)
            v = self.value(t)
            tokens.append(t)
            values.append(v)

        # try to find a match for any expression, both typed and un-typed
        case_found = None
        possible_expressions = values_to_expressions(values, EXPRESSIONS_MAP,
                                                     EXPRESSIONS)
        for case in possible_expressions:
            if case.is_signature_match(values):  # match first occurrence
                case_found = case
                break