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
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
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
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
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])))
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])))
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))
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