def test_string(self): code = "y = 'x' + x" analyzer = Analyzer() scope = analyzer.get_scope('x') scope['x'] = String() analyze(parse(code), analyzer) self.assertEqual(analyzer.exceptions, []) self.assertEqual(analyzer['y'], String())
def test_default_and_or(self): code = ''' switch %s do { case "0"; default {"default"}; case "3": {"3"}; case "1"; case "4"; case "2": {"2"}; }''' self.assertEqual(String('"3"'), interpret(code % '"0"')[1]) self.assertEqual(String('"default"'), interpret(code % '"5"')[1]) self.assertEqual(String('"2"'), interpret(code % '"1"')[1]) self.assertEqual(String('"3"'), interpret(code % '"3"')[1])
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 preprocessor_stringify(token, is_variable): # Verify that token starts with appropriate char and is alphanumeric if not (is_variable or re.match(r'[a-zA-Z_]\w*', str(token))): # todo: need to fix the coordinates for errors here raise SQFParserError(get_coord(""), 'Stringification failed on invalid characters') # todo: check for invalid string created (missing ") return String("\"" + str(token) + "\"")
def __init__(self, variable=None, from_=None, to=None, step=None): if step is None: step = Number(1) if variable is None: variable = String() assert (variable is None or isinstance(variable, String)) assert (from_ is None or isinstance(from_, Type)) assert (to is None or isinstance(to, Type)) assert (isinstance(step, Type)) super().__init__(variable) self.from_ = from_ self.to = to self.step = step
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_add(self): test = '_x = "ABA"; _y = "BAB"; _x + _y' _, outcome = interpret(test) self.assertEqual(String('"ABABAB"'), outcome)
def test_assign(self): test = '_x = "ABA";' interpreter, outcome = interpret(test) self.assertEqual(String('"ABA"'), interpreter['_x'])
def test_with_default_unused(self): analyzer = analyze(parse('[""] params [["_x", 0]]; _x')) self.assertEqual(analyzer.exceptions, []) self.assertEqual(String('""'), analyzer['_x'])
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
def parse_strings_and_comments(all_tokens): """ Function that parses the strings of a script, transforming them into `String`. """ string = '' # the buffer for the activated mode tokens = [] # the final result in_double = False mode = None # [None, "string_single", "string_double", "comment_line", "comment_bulk"] for i, token in enumerate(all_tokens): if mode == "string_double": string += token if token == '"': if in_double: in_double = False elif not in_double and i != len(all_tokens) - 1 and all_tokens[ i + 1] == '"': in_double = True else: tokens.append(String(string)) mode = None in_double = False elif mode == "string_single": string += token if token == "'": if in_double: in_double = False elif not in_double and i != len(all_tokens) - 1 and all_tokens[ i + 1] == "'": in_double = True else: tokens.append(String(string)) mode = None in_double = False elif mode == "comment_bulk": string += token if token == '*/': mode = None tokens.append(Comment(string)) string = '' elif mode == "comment_line": string += token if token in ('\n', '\r\n'): mode = None tokens.append(Comment(string)) string = '' else: # mode is None if token == '"': string = token mode = "string_double" elif token == "'": string = token mode = "string_single" elif token == '/*': string = token mode = "comment_bulk" elif token == '//': string = token mode = "comment_line" else: tokens.append(token) if mode in ("comment_line", "comment_bulk"): tokens.append(Comment(string)) elif mode is not None: raise SQFParserError(get_coord(tokens), 'String is not closed') return tokens
class Interpreter(BaseInterpreter): private_default_class = Nothing def __init__(self, all_vars=None): super().__init__(all_vars) self._simulation = None self._client = None @property def simulation(self): return self._simulation @property def client(self): return self._client @client.setter def client(self, client): self._client = client self._simulation = client.simulation def _add_params(self, token): super()._add_params(token) lhs = token[0].value scope = self.get_scope(lhs) scope[lhs] = token[1] 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 execute_single(self, statement): assert (not isinstance(statement, Code)) outcome = Nothing() _outcome = outcome # evaluate the types of all tokens base_tokens = statement.base_tokens values = [] tokens = [] types = [] for token in base_tokens: t, v = self.execute_token(token) values.append(v) tokens.append(t) types.append(type(v)) case_found = None for case in EXPRESSIONS: if case.is_match(values): case_found = case break if case_found is not None: outcome = case_found.execute(values, self) # todo: replace all elif below by expressions elif len(tokens) == 2 and tokens[0] == Keyword('publicVariable'): if not isinstance(tokens[1], String) or tokens[1].value.startswith('_'): raise SQFParserError( statement.position, 'Interpretation of "%s" failed' % statement) var_name = tokens[1].value scope = self.get_scope(var_name, 'missionNamespace') self.simulation.broadcast(var_name, scope[var_name]) elif len(tokens) == 2 and tokens[0] == Keyword('publicVariableServer'): if not isinstance(tokens[1], String) or tokens[1].value.startswith('_'): raise SQFParserError( statement.position, 'Interpretation of "%s" failed' % statement) var_name = tokens[1].value scope = self.get_scope(var_name, 'missionNamespace') self.simulation.broadcast(var_name, scope[var_name], -1) # -1 => to server elif len(tokens) == 2 and tokens[0] == Keyword('private'): if isinstance(values[1], String): self.add_privates([values[1]]) elif isinstance(values[1], Array): self.add_privates(values[1].value) elif isinstance(base_tokens[1], Statement) and isinstance( base_tokens[1].base_tokens[0], Variable): var = base_tokens[1].base_tokens[0] self.add_privates([String('"' + var.name + '"')]) outcome = PrivateType(var) # binary operators elif len(tokens) == 3 and tokens[1] in ( Keyword('='), Keyword('publicVariableClient')): # it is a binary statement: token, operation, token lhs = tokens[0] lhs_v = values[0] lhs_t = types[0] op = tokens[1] rhs = tokens[2] rhs_v = values[2] rhs_t = types[2] if op == Keyword('='): if isinstance(lhs, PrivateType): lhs = lhs.variable else: lhs = self.get_variable(base_tokens[0]) if not isinstance(lhs, Variable) or not isinstance( rhs_v, Type): raise SQFParserError( statement.position, 'Interpretation of "%s" failed' % statement) scope = self.get_scope(lhs.name) scope[lhs.name] = rhs_v outcome = rhs elif op == Keyword('publicVariableClient'): if not lhs_t == Number or rhs.value.startswith('_'): raise SQFParserError( statement.position, 'Interpretation of "%s" failed' % statement) client_id = lhs.value var_name = rhs.value scope = self.get_scope(var_name, 'missionNamespace') self.simulation.broadcast(var_name, scope[var_name], client_id) # code, variables and values elif len(tokens) == 1 and isinstance(tokens[0], (Type, Keyword)): outcome = values[0] else: raise SQFParserError(statement.position, 'Interpretation of "%s" failed' % statement) if statement.ending: outcome = _outcome assert (type(outcome) in (Nothing, Keyword) or not outcome.is_undefined) return outcome