def test_resize_array(self): interpreter = interpret('_x = [1,2]; _x resize 4')[0] self.assertEqual(Array([N(1), N(2), Nothing(), Nothing()]), interpreter['_x']) interpreter = interpret('_x = [1,2,3,4]; _x resize 2')[0] self.assertEqual(Array([N(1), N(2)]), interpreter['_x'])
def test_select(self): _, outcome = interpret('[1, 2] select 0') self.assertEqual(N(1), outcome) # alternative using floats _, outcome = interpret('[1, 2] select 0.5') self.assertEqual(N(1), outcome) _, outcome = interpret('[1, 2] select 0.6') self.assertEqual(N(2), outcome) # alternative using booleans _, outcome = interpret('[1, 2] select true') self.assertEqual(N(2), outcome) _, outcome = interpret('[1, 2] select false') self.assertEqual(N(1), outcome) # alternative using [start, count] _, outcome = interpret('[1, 2, 3] select [1, 2]') self.assertEqual(Array([N(2), N(3)]), outcome) _, outcome = interpret('[1, 2, 3] select [1, 10]') self.assertEqual(Array([N(2), N(3)]), outcome) with self.assertRaises(SQFParserError): _, outcome = interpret('[1, 2, 3] select [4, 10]') with self.assertRaises(SQFParserError): _, outcome = interpret('[1, 2, 3] select 10')
def test_pushBackUnique(self): interpreter, outcome = interpret('_x = [1]; _x pushBackUnique 2') self.assertEqual(Array([N(1), N(2)]), interpreter['_x']) self.assertEqual(N(1), outcome) interpreter, outcome = interpret('_x = [1, 2]; _x pushBackUnique 2') self.assertEqual(Array([N(1), N(2)]), interpreter['_x']) self.assertEqual(N(-1), outcome)
def test_reference(self): # tests that changing _x affects _y when _y = _x. interpreter, _ = interpret('_x = [1, 2]; _y = _x; _x set [0, 2];') self.assertEqual(Array([N(2), N(2)]), interpreter['_x']) self.assertEqual(Array([N(2), N(2)]), interpreter['_y']) interpreter, _ = interpret('_x = [1, 2]; _y = _x; reverse _x;') self.assertEqual(Array([N(2), N(1)]), interpreter['_y'])
def test_set(self): test = '_x = [1, 2]; _x set [0, 2];' interpreter, _ = interpret(test) self.assertEqual(Array([N(2), N(2)]), interpreter['_x']) test = '_x = [1, 2]; _x set [2, 3];' interpreter, _ = interpret(test) self.assertEqual(Array([N(1), N(2), N(3)]), interpreter['_x'])
def set_variable(self, var_name, value, broadcast=True): self._interpreter.set_global_variable(var_name, value) if broadcast: if var_name in self._listening_variables: self._interpreter.execute_code(self._listening_variables[var_name], params=Array([String('"'+var_name+'"'), value]))
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 test_array(self): code = "y = x select 2" analyzer = Analyzer() scope = analyzer.get_scope('x') scope['x'] = Array() analyze(parse(code), analyzer) self.assertEqual(len(analyzer.exceptions), 0) self.assertEqual(analyzer['y'], Anything())
def execute_token(self, token): """ Given a single token, recursively evaluate it without returning its value (only type) """ # interpret the statement recursively if isinstance(token, Statement): result = self.execute_single(statement=token) # we do not want the position of the statement, but of the token, so we do not # store it here elif isinstance(token, Array) and token.value is not None: result = Array([self.execute_token(s) for s in token.value]) result.position = token.position else: result = token result.position = token.position return result
def execute_token(self, token): """ Given a single token, recursively evaluate it and return its value. """ # interpret the statement recursively if isinstance(token, Statement): result = self.execute_single(statement=token) elif isinstance(token, Array): # empty statements are ignored result = Array( [self.execute_token(s)[1] for s in token.value if s]) elif token == Keyword('isServer'): result = Boolean(self.client.is_server) elif token == Keyword('isDedicated'): result = Boolean(self.client.is_dedicated) else: result = token result.position = token.position return result, self.value(result)
def test_forvar_edges(self): # see comments on https://community.bistudio.com/wiki/for_var # start = end => runs once test = 'y = -10; for "_i" from 0 to 0 do {y = _i;};' interpreter, _ = interpret(test) self.assertEqual(N(0), interpreter['y']) # start < end => never runs interpreter, _ = interpret( 'y = -10; for "_i" from 0 to -1 do {y = _i;};') self.assertEqual(N(-10), interpreter['y']) # do not overwrite globals interpreter, _ = interpret('for "x" from 0 to 0 do {};') self.assertEqual(Nothing(), interpreter['x']) # nested test = '_array = []; for "_i" from 0 to 1 do {for "_i" from 0 to 1 do {_array pushBack _i;}; _array pushBack _i;};' interpreter, _ = interpret(test) self.assertEqual(Array([N(0), N(1), N(0), N(0), N(1), N(1)]), interpreter['_array'])
def test_defines_in_array2(self): code = '#define x (0.1)\n#define y (0.02)\nz = [2 * x, 2 * y, 2 * x];' analyzer = analyze(parse(code)) self.assertEqual(len(analyzer.exceptions), 0) self.assertEqual(Array(), analyzer['z'])
def test_defines_in_array(self): code = '#define x 1\ny=[x,x]' analyzer = analyze(parse(code)) errors = analyzer.exceptions self.assertEqual(len(errors), 0) self.assertEqual(Array(), analyzer['y'])
def test_defines_in_array(self): code = '#define x 1\ny=[x,x]' analyzer = analyze(parse(code)) self.assertEqual(analyzer.exceptions, []) self.assertEqual(Array(), analyzer['y'])
def test_assign_array(self): interpreter = interpret('_y = [];')[0] self.assertEqual(Array([]), interpreter['_y'])
def test_array(self): self.assertEqual('[1,1]', str(Array([N(1), N(1)])))
def test_to_array_string(self): outcome = interpret('toArray("AaŒ")')[1] self.assertEqual(Array([N(65), N(97), N(338)]), outcome) outcome = interpret('toString([65,97,338])')[1] self.assertEqual(String('"AaŒ"'), outcome)
def test_for_var_step(self): test = 'y = []; for "_i" from 1 to 10 step 2 do {y pushBack _i;};' interpreter, outcome = interpret(test) self.assertEqual(Array([N(1), N(3), N(5), N(7), N(9)]), interpreter['y'])
def test_assign(self): interpreter, outcome = interpret('_x = [1, 2];') self.assertEqual(Array([N(1), N(2)]), interpreter['_x'])
def test_for_var(self): test = 'y = []; for "_i" from 1 to 10 do {y pushBack _i;};' interpreter, outcome = interpret(test) self.assertEqual(Array([N(i) for i in range(1, 11)]), interpreter['y'])
def test_reverse(self): interpreter, outcome = interpret('_x = [1, 2]; reverse _x') self.assertEqual(Nothing(), outcome) self.assertEqual(Array([N(2), N(1)]), interpreter['_x'])
def test_add(self): test = '_x = [1, 2]; _y = [3, 4]; _z = _x + _y' _, outcome = interpret(test) self.assertEqual(Array([N(1), N(2), N(3), N(4)]), outcome)
def parse_block(all_tokens, analyze_tokens, start=0, initial_lvls=None, stop_statement='both', defines=None): if not initial_lvls: initial_lvls = _LEVELS if defines is None: defines = defaultdict(dict) lvls = initial_lvls.copy() statements = [] tokens = [] i = start if not all_tokens: return Statement([]), 0 while i < len(all_tokens): token = all_tokens[i] # begin #ifdef controls if lvls['ifdef'] and token in OPEN_PARENTHESIS: lvls['ifdef_open_close'] += 1 stop = False if token in (Preprocessor('#ifdef'), Preprocessor('#ifndef')): stop = True lvls['ifdef'] += 1 expression, size = parse_block(all_tokens, _analyze_simple, i + 1, lvls, stop_statement, defines=defines) lvls['ifdef'] -= 1 if lvls['ifdef'] == 0: assert (isinstance(expression, IfDefStatement)) replacing_expression = parse_ifdef_block( expression, defines, get_coord(all_tokens[:i - 1])) new_all_tokens = sqf.base_type.get_all_tokens( tokens + replacing_expression) result, _ = parse_block(new_all_tokens, analyze_tokens, 0, None, stop_statement, defines=defines) expression.prepend(tokens) expression = IfDefResult(expression, result.tokens) statements.append(expression) len_expression = len(expression.get_all_tokens()) i += len_expression - len(tokens) - 1 tokens = [] else: tokens.append(expression) i += size + 1 # finish ifdef elif is_finish_ifdef_condition(tokens, lvls) and ( is_end_statement(token, stop_statement) or is_finish_ifdef_parenthesis(token, lvls) ) or lvls['ifdef'] > 1 and token == Preprocessor('#endif'): if token != EndOfFile() and token not in CLOSE_PARENTHESIS: tokens.append(token) if_def = finish_ifdef(tokens, all_tokens, start, statements) return if_def, i - start # parse during ifdef elif lvls['ifdef'] != 0: stop = True tokens.append(token) # end ifdef controls if lvls['ifdef'] and token in (STOP_KEYWORDS['single'] + CLOSE_PARENTHESIS): lvls['ifdef_open_close'] -= 1 if lvls['ifdef_open_close'] < 0: lvls['ifdef_open_close'] = 0 if stop: pass # try to match a #defined and get the arguments elif str(token) in defines: # is a define stop, define_statement, arg_indexes = find_match_if_def( all_tokens, i, defines, token) if stop: arg_number = len(define_statement.args) extra_tokens_to_move = 1 + 2 * ( arg_number != 0) + 2 * arg_number - 1 * (arg_number != 0) replaced_expression = all_tokens[i:i + extra_tokens_to_move] # the `all_tokens` after replacement replacing_expression = replace_in_expression( define_statement.expression, define_statement.args, arg_indexes, all_tokens) new_all_tokens = all_tokens[:i - len( tokens)] + tokens + replacing_expression + all_tokens[ i + extra_tokens_to_move:] new_start = i - len(tokens) expression, size = parse_block(new_all_tokens, analyze_tokens, new_start, lvls, stop_statement, defines=defines) # the all_tokens of the statement before replacement original_tokens_taken = len(replaced_expression) - len( replacing_expression) + size original_tokens = all_tokens[i - len(tokens):i - len(tokens) + original_tokens_taken] if isinstance(expression, Statement): expression = expression.content[0] if type(original_tokens[-1]) in (EndOfLine, Comment, EndOfFile): del original_tokens[-1] original_tokens_taken -= 1 expression = DefineResult(original_tokens, define_statement, expression) statements.append(expression) i += original_tokens_taken - len(tokens) - 1 tokens = [] if stop: pass elif token == ParserKeyword('['): lvls['[]'] += 1 expression, size = parse_block(all_tokens, analyze_tokens, i + 1, lvls, stop_statement='single', defines=defines) lvls['[]'] -= 1 tokens.append(expression) i += size + 1 elif token == ParserKeyword('('): lvls['()'] += 1 expression, size = parse_block(all_tokens, analyze_tokens, i + 1, lvls, stop_statement, defines=defines) lvls['()'] -= 1 tokens.append(expression) i += size + 1 elif token == ParserKeyword('{'): lvls['{}'] += 1 expression, size = parse_block(all_tokens, analyze_tokens, i + 1, lvls, stop_statement, defines=defines) lvls['{}'] -= 1 tokens.append(expression) i += size + 1 elif token == ParserKeyword(']'): if lvls['[]'] == 0: raise SQFParenthesisError( get_coord(all_tokens[:i]), 'Trying to close right parenthesis without them opened.') if statements: if isinstance(statements[0], DefineResult): statements[0]._tokens = [ Array( _analyze_array(statements[0]._tokens, analyze_tokens, all_tokens[:i])) ] return statements[0], i - start else: raise SQFParserError( get_coord(all_tokens[:i]), 'A statement %s cannot be in an array' % Statement(statements)) return Array(_analyze_array(tokens, analyze_tokens, all_tokens[:i])), i - start elif token == ParserKeyword(')'): if lvls['()'] == 0: raise SQFParenthesisError( get_coord(all_tokens[:i]), 'Trying to close parenthesis without opened parenthesis.') if tokens: statements.append(analyze_tokens(tokens)) return Statement(statements, parenthesis=True), i - start elif token == ParserKeyword('}'): if lvls['{}'] == 0: raise SQFParenthesisError( get_coord(all_tokens[:i]), 'Trying to close brackets without opened brackets.') if tokens: statements.append(analyze_tokens(tokens)) return Code(statements), i - start # end of statement when not in preprocessor states elif all(lvls[lvl_type] == 0 for lvl_type in ('#define', '#include')) and is_end_statement( token, stop_statement): if type(token) != EndOfFile: tokens.append(token) if tokens: statements.append(analyze_tokens(tokens)) tokens = [] elif token in (Preprocessor('#define'), Preprocessor('#include')): # notice that `token` is ignored here. It will be picked up in the end if tokens: # a pre-processor starts a new statement statements.append(analyze_tokens(tokens)) tokens = [] lvls[token.value] += 1 expression, size = parse_block(all_tokens, analyze_tokens, i + 1, lvls, stop_statement, defines=defines) lvls[token.value] -= 1 statements.append(expression) i += size elif token == Keyword('#') and lvls['#define'] != 0: # The # sqf command is superseded by the preprocessor directive's stringification command tokens.append(Preprocessor('#')) elif type(token) in (EndOfLine, Comment, EndOfFile) and any( lvls[x] != 0 for x in {'#define', '#include'}): tokens.insert( 0, all_tokens[start - 1]) # pick the token that triggered the statement if tokens[0] == Preprocessor('#define'): define_statement = _analyze_define(tokens) defines[define_statement.variable_name][len( define_statement.args)] = define_statement statements.append(define_statement) else: statements.append(analyze_tokens(tokens)) return Statement(statements), i - start elif type(token) != EndOfFile: tokens.append(token) i += 1 if is_finish_ifdef_condition(tokens, lvls): return finish_ifdef(tokens, all_tokens, start, statements), i - start for lvl_type in ('[]', '()', '{}', 'ifdef'): if lvls[lvl_type] != 0: message = 'Parenthesis "%s" not closed' % lvl_type[0] if lvl_type == 'ifdef': message = '#ifdef statement not closed' raise SQFParenthesisError(get_coord(all_tokens[:start - 1]), message) if tokens: statements.append(analyze_tokens(tokens)) return Statement(statements), i - start
def test_append(self): interpreter, outcome = interpret('_x = [1,2]; _x append [3,4]') self.assertEqual(Nothing(), outcome) self.assertEqual(Array([N(1), N(2), N(3), N(4)]), interpreter['_x'])
def test_subtract(self): test = '_x = [1, 2, 3, 2, 4]; _y = [2, 3]; _z = _x - _y' _, outcome = interpret(test) self.assertEqual(Array([N(1), N(4)]), outcome)