def process(self, scope, preserve_types=False): lhs = to_value(self.lhs, scope) op = self.operator if op is not None: rhs = to_value(self.rhs, scope) if op.power: return lhs**rhs, None elif op.bitwise_not: return ~rhs, None elif op.multiply: return lhs * rhs, None elif op.divide: return lhs / rhs, None elif op.modulus: return lhs & rhs, None elif op.add: return lhs + rhs, None elif op.subtract: return lhs - rhs, None elif op.bitwise_and: return lhs & rhs, None elif op.bitwise_xor: return lhs ^ rhs, None elif op.bitwise_or: return lhs | rhs, None return None, None if preserve_types: return self.lhs, None else: return lhs, None
def resolve(self, scope, fallback=None): if self.skip: return fallback value = scope.get(self.as_key(scope), fallback) return to_value(value, scope)
def execute_script(browser, script, scope=None, environment=None, preserve_state=False, scriptpath=None): # setup the environment if not environment: environment = Environment(scope, browser=browser) if not scope: scope = environment.scope if not scriptpath: scriptpath = os.path.abspath(os.getcwd()) callbacks = set() # load and parse the script friendscript = parser.Friendscript(data=script, environment=environment) # tell the environment about the calling script environment.script = friendscript if browser: # setup event handlers for this execution for handler in friendscript.handlers: callback_id = browser.default.on( parser.to_value(handler.pattern, scope), _handle_event(friendscript, handler, scope) ) logging.debug('Add event handler {}'.format(callback_id)) callbacks.add(callback_id) # Script Execution starts NOW # --------------------------------------------------------------------------------------------- try: # recursively evaluate all blocks and nested blocks starting from the top level for block in friendscript.blocks: try: evaluate_block(friendscript, block, scope, scriptpath=scriptpath) except parser.exceptions.ScriptError: raise except Exception as e: logging.debug('Exception') raise raise parser.exceptions.ScriptError('{}'.format(e), model=block) finally: if not preserve_state: # unregister the event handlers we created for this run for callback_id in callbacks: logging.debug('Remove event handler {}'.format(callback_id)) browser.default.remove_handler(callback_id) # ...and done. # --------------------------------------------------------------------------------------------- # return the final state after script execution return scope
def evaluate(self, environment, scope=None): if scope is None: scope = environment.scope # Inline Assignment and test condition # ------------------------------------------------------------------------------------------ if self.assignment: self.assignment.assign(environment, scope, force=True) # recursively evaluate the condition portion of the statement return self.condition.evaluate(environment, scope) # Command Evaluation (with optional test condition) # ------------------------------------------------------------------------------------------ elif self.command: # execute the command resultkey, result = environment.execute(self.command, scope) # whatever the result of the expression command was, put it in the calling scope scope.set(resultkey, result, force=True) # if the statement has a condition, then evaluate using it if self.condition: # recursively evaluate the condition portion of the statement return self.condition.evaluate(environment, scope) else: # no condition, so just do the same thing as a unary comparison return self.compare(result) # Comparison # ------------------------------------------------------------------------------------------ else: result = self.compare(to_value(self.lhs, scope), self.operator, to_value(self.rhs, scope)) if self.negate is True: return not result else: return result
def as_key(self, scope): parts = tuple() for part in self.parts: if len(part.key): parts = parts + (part.key.decode('UTF-8'), ) if part.index is not None: index = to_value(part.index, scope) if index is not None: parts = parts + (index, ) return parts
def interpolate(self, value, scope=None, deindent_heredocs=True, **kwargs): if scope is None: scope = self._scope scope = Scope(kwargs, parent=scope) scopevars = scope.as_dict() if isinstance(value, parser.lang.Expression): value, _ = value.process(scope, preserve_types=True) # if this is an exact-match string, then interpolate is a no-op if isinstance(value, parser.types.StringLiteral): if isinstance(value, str): value = value.decode('UTF-8') return value # for heredocs, we're going to return the literal as-is, but we trim off the indentation # whitespace of the calling command, that way heredocs can be written to match the indentation # level of the surrounding script, but when evaluated they are indented to the "intended" level if isinstance(value, parser.types.Heredoc): if deindent_heredocs: return value.deindent(self.col - 1) else: return value actual = parser.to_value(value, scope) if isinstance(actual, str): actual = actual.decode('UTF-8') # lists get recursed into if isinstance(actual, list): # recurse into lists return [self.interpolate(v, **scopevars) for v in actual] # dicts also get recursed into elif isinstance(actual, dict): # recurse into dicts return dict([(k, self.interpolate(v, **scopevars)) for k, v in actual.items()]) # strings (as returned from to_value) get interpolated elif isinstance(actual, basestring): # do the interpolation return actual.format(**self.prepare_output_values(scopevars)) # everything else passes through else: return actual
def execute_loop(self, environment, scope=None): if scope is None: scope = environment.scope i = 0 if self.iterable: loop = self.iterable if isinstance(loop.iterator, variables.Variable): iter_value = to_value(loop.iterator, scope) else: _, iter_value = environment.execute(loop.iterator, scope=scope) try: if isinstance(iter_value, dict): iter_value = iter_value.items() if not iter_value: return for item in iter(iter_value): if len(loop.variables) > 1 and isinstance(item, tuple): # unpack iter items into the variables we were given for var_i, var in enumerate( loop.variables[0:len(item)]): if not var.skip: if var_i < len(item): scope.set(var.as_key(scope), item[var_i], force=True) # null out any remaining variables if len(loop.variables) > len(item): for var in loop.variables[len(item):]: scope.set(var.as_key(scope), None, force=True) else: scope.set(loop.variables[0].as_key(scope), item, force=True) scope.set('index', i, force=True) for result in self.iterate_blocks(i, environment, scope): yield result i += 1 except TypeError: raise TypeError("Cannot loop on result of {}".format( loop.iterator)) elif self.bounded: loop = self.bounded environment.execute(loop.initial, scope=scope) while loop.termination.evaluate(environment, scope=scope): scope.set('index', i, force=True) for result in self.iterate_blocks(i, environment, scope): yield result environment.execute(loop.next, scope=scope) i += 1 elif self.truthy: loop = self.truthy while loop.termination.evaluate(environment, scope=scope): scope.set('index', i, force=True) for result in self.iterate_blocks(i, environment, scope): yield result i += 1 elif self.fixedlen: loop = self.fixedlen count = int(to_value(loop.count, scope)) for _ in range(count): scope.set('index', i, force=True) for result in self.iterate_blocks(i, environment, scope): yield result i += 1 else: while True: scope.set('index', i, force=True) for result in self.iterate_blocks(i, environment, scope): yield result i += 1
def evaluate_block(scriptmgr, block, scope, scriptpath=None): environment = scriptmgr.environment # line, column = scriptmgr.get_item_position(block) # print('{} = line {}, column: {}'.format(block.__class__, line, column)) # Assignment # --------------------------------------------------------------------------------------------- if isinstance(block, parser.variables.Assignment): block.assign(environment, scope) # Directives # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.lang.Directive): if block.is_unset: for var in block.variables: scope.unset(var.as_key(scope)) elif block.is_include: if scriptpath is None: raise parser.exceptions.ScriptError('Cannot include; no scriptpath set') fileset = environment.interpolate(block.fileglob) if not fileset.startswith('/'): fileset = os.path.join(scriptpath, fileset) # expand the globs, becomes a list here fileset = glob.glob(fileset) for include in fileset: if include.endswith('.fs'): logging.debug('Including script {}'.format(include)) with open(include, 'r+b') as subscript: execute_script( environment.browser, subscript.read(), environment.scope, environment, scriptpath=os.path.dirname(include) ) # Expressions # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.lang.Expression): result, put_result_in = block.process(environment, scope) if put_result_in: scope.set(put_result_in.as_key(scope), result) # Commands # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.commands.CommandSequence): # for each command in the pipeline... for command in block.commands: key, value = environment.execute(command, scope) if not isinstance(value, IgnoreResults): value = parser.to_value(value, scope) if key is not None: scope.set(key, value) # perform delay if we have one if environment.has_execution_option('demo.post_command_delay'): delay = environment.get_execution_option('demo.post_command_delay') time.sleep(delay / 1e3) # If / Else If / Else # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.conditions.IfElseBlock): subscope = Scope(parent=scope) for subblock in block.get_blocks(environment, scope=subscope): evaluate_block(scriptmgr, subblock, subscope) # Loops # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.loops.LoopBlock): loopscope = Scope(parent=scope) for i, _, subblock, scope in block.execute_loop(environment, scope=loopscope): try: evaluate_block(scriptmgr, subblock, scope=scope) except parser.loops.FlowControlMultiLevel as e: if e.levels > 1: e.levels -= 1 raise elif isinstance(e, parser.loops.FlowControlBreak): break elif isinstance(e, parser.loops.FlowControlContinue): continue # Flow Control # --------------------------------------------------------------------------------------------- elif isinstance(block, parser.loops.FlowControlWord): if block.is_break: raise parser.loops.FlowControlBreak('break', levels=block.levels) elif block.is_continue: raise parser.loops.FlowControlContinue('continue', levels=block.levels) else: raise parser.exceptions.ScriptError('Unrecognized statement')
def resolve(self, scope): if self.variable: return self.variable.resolve(scope) return to_value(self.value, scope)